feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
[package]
|
||||
name = "pezpallet-oracle"
|
||||
version = "1.0.0"
|
||||
authors = [
|
||||
"Acala Developers",
|
||||
"Kurdistan Tech Institute <info@pezkuwichain.io>",
|
||||
"Parity Technologies <admin@parity.io>",
|
||||
]
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "FRAME oracle pallet for off-chain data"
|
||||
readme = "README.md"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
pezframe-benchmarking = { workspace = true, optional = true }
|
||||
pezframe-support = { workspace = true }
|
||||
pezframe-system = { workspace = true }
|
||||
impl-trait-for-tuples = { workspace = true }
|
||||
pezsp-application-crypto = { workspace = true }
|
||||
pezsp-io = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
pezsp-std = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"pezframe-benchmarking?/std",
|
||||
"pezframe-support/std",
|
||||
"pezframe-system/std",
|
||||
"scale-info/std",
|
||||
"serde/std",
|
||||
"pezsp-application-crypto/std",
|
||||
"pezsp-io/std",
|
||||
"pezsp-runtime/std",
|
||||
"pezsp-std/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"pezframe-benchmarking/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezframe-system/runtime-benchmarks",
|
||||
"pezsp-io/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"pezframe-support/try-runtime",
|
||||
"pezframe-system/try-runtime",
|
||||
"pezsp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,59 @@
|
||||
# pezpallet-oracle
|
||||
|
||||
## Overview
|
||||
|
||||
The Oracle pallet provides a decentralized and trustworthy way to bring external, off-chain data onto the
|
||||
blockchain. It allows a configurable set of oracle operators to feed data, such as prices, into the system.
|
||||
This data can then be used by other pallets.
|
||||
|
||||
The pallet is designed to be flexible and can be configured to use different data sources and aggregation
|
||||
strategies.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- **Oracle Operators**: A set of trusted accounts that are authorized to submit data to the oracle. The pallet
|
||||
uses the `frame_support::traits::SortedMembers` trait to manage the set of operators. This allows using pallets
|
||||
like `pezpallet-membership` to manage the oracle members.
|
||||
- **Data Feeds**: Operators feed data as key-value pairs. The `OracleKey` is used to identify the data being fed
|
||||
(e.g., a specific currency pair), and the `OracleValue` is the data itself (e.g., the price).
|
||||
- **Data Aggregation**: The pallet can be configured with a `CombineData` implementation to aggregate the raw
|
||||
values submitted by individual operators into a single, trusted value. A default implementation
|
||||
`DefaultCombineData` is provided, which takes the median of the values.
|
||||
- **Timestamped Data**: All data submitted to the oracle is timestamped, allowing consumers of the data to know
|
||||
how fresh it is.
|
||||
|
||||
## Interface
|
||||
|
||||
### Dispatchable Functions
|
||||
|
||||
- `feed_values` - Allows an authorized oracle operator to submit a set of key-value data points.
|
||||
|
||||
### Public Functions
|
||||
|
||||
- `get` - Returns the aggregated and timestamped value for a given key.
|
||||
- `get_all_values` - Returns all aggregated and timestamped values.
|
||||
- `read_raw_values` - Returns the raw, un-aggregated values for a given key from all oracle operators.
|
||||
|
||||
### Data Providers
|
||||
|
||||
The pallet implements the `DataProvider` and `DataProviderExtended` traits, allowing other pallets to easily
|
||||
consume the oracle data.
|
||||
|
||||
## Usage
|
||||
|
||||
To use the oracle pallet, you need to:
|
||||
|
||||
1. **Add it to your runtime's `Cargo.toml`**.
|
||||
2. **Implement the `Config` trait** for the pallet in your runtime. This includes specifying:
|
||||
- `OnNewData`: A hook to perform actions when new data is received.
|
||||
- `CombineData`: The data aggregation strategy.
|
||||
- `Time`: The time provider.
|
||||
- `OracleKey`, `OracleValue`: The types for the data key and value.
|
||||
- `RootOperatorAccountId`: An account with sudo-like permissions for the oracle.
|
||||
- `Members`: The source of oracle operators.
|
||||
3. **Add the pallet to your runtime's `construct_runtime!` macro**.
|
||||
|
||||
Once configured, authorized operators can call `feed_values` to submit data, and other pallets can use the
|
||||
`DataProvider` trait to read the aggregated data.
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "pezpallet-oracle-runtime-api"
|
||||
version = "1.0.0"
|
||||
authors = [
|
||||
"Acala Developers",
|
||||
"Kurdistan Tech Institute <info@pezkuwichain.io>",
|
||||
"Parity Technologies <admin@parity.io>",
|
||||
]
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Runtime API for the oracle pallet."
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true, features = ["derive"] }
|
||||
scale-info = { workspace = true }
|
||||
pezsp-api = { workspace = true }
|
||||
pezsp-std = { workspace = true }
|
||||
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["codec/std", "scale-info/std", "pezsp-api/std", "pezsp-std/std"]
|
||||
runtime-benchmarks = ["pezsp-api/runtime-benchmarks"]
|
||||
@@ -0,0 +1,92 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Runtime API definition for the oracle pallet.
|
||||
//!
|
||||
//! This crate provides runtime APIs that allow external clients to query oracle data
|
||||
//! from the blockchain. The APIs are designed to be efficient and provide access to
|
||||
//! both individual oracle values and complete datasets.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! The oracle runtime API enables off-chain applications, wallets, and other blockchain
|
||||
//! clients to retrieve oracle data without needing to parse storage directly. This
|
||||
//! abstraction provides a clean interface for accessing oracle information and ensures
|
||||
//! compatibility across different runtime versions.
|
||||
//!
|
||||
//! The API supports querying data from specific oracle providers and retrieving all
|
||||
//! available oracle data, making it suitable for various use cases such as price
|
||||
//! feeds, data monitoring, and external integrations.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
// The `too_many_arguments` warning originates from `decl_runtime_apis` macro.
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
// The `unnecessary_mut_passed` warning originates from `decl_runtime_apis` macro.
|
||||
#![allow(clippy::unnecessary_mut_passed)]
|
||||
|
||||
use codec::Codec;
|
||||
use pezsp_std::prelude::Vec;
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
/// Runtime API for querying oracle data from the blockchain.
|
||||
///
|
||||
/// This trait provides methods to retrieve oracle data without requiring direct
|
||||
/// storage access. It's designed to be called from external clients, RPC nodes,
|
||||
/// and other blockchain infrastructure components that need access to oracle
|
||||
/// information.
|
||||
///
|
||||
/// The API is generic over three type parameters:
|
||||
/// - `ProviderId`: Identifies the oracle provider or data source
|
||||
/// - `Key`: The oracle key identifying the specific data feed
|
||||
/// - `Value`: The oracle data value type
|
||||
pub trait OracleApi<ProviderId, Key, Value> where
|
||||
ProviderId: Codec,
|
||||
Key: Codec,
|
||||
Value: Codec,
|
||||
{
|
||||
/// Retrieves a specific oracle value for a given provider and key.
|
||||
///
|
||||
/// Returns the current oracle value if available, or `None` if no data exists
|
||||
/// for the specified provider and key combination.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `provider_id`: The oracle provider identifier
|
||||
/// * `key`: The oracle key identifying the data feed
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `Some(value)` if oracle data exists, `None` otherwise.
|
||||
fn get_value(provider_id: ProviderId, key: Key) -> Option<Value>;
|
||||
|
||||
/// Retrieves all oracle values for a specific provider.
|
||||
///
|
||||
/// Returns a vector of key-value pairs containing all available oracle data
|
||||
/// from the specified provider. Each pair contains the oracle key and its
|
||||
/// corresponding value (if available).
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `provider_id`: The oracle provider identifier
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a vector of `(Key, Option<Value>)` pairs representing all oracle
|
||||
/// data available from the specified provider.
|
||||
fn get_all_values(provider_id: ProviderId) -> Vec<(Key, Option<Value>)>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::*;
|
||||
use crate::Pallet as Oracle;
|
||||
|
||||
use pezframe_benchmarking::v2::*;
|
||||
|
||||
use pezframe_support::assert_ok;
|
||||
use pezframe_system::{Pallet as System, RawOrigin};
|
||||
|
||||
#[instance_benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn feed_values(
|
||||
x: Linear<0, { T::BenchmarkHelper::get_currency_id_value_pairs().len() as u32 }>,
|
||||
) {
|
||||
// Register the caller
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Members::add(&caller);
|
||||
|
||||
let values = T::BenchmarkHelper::get_currency_id_value_pairs()[..x as usize]
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.expect("Must succeed since at worst the length remained the same.");
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Signed(caller.clone()), values);
|
||||
|
||||
assert!(HasDispatched::<T, I>::get().contains(&caller));
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn on_finalize() {
|
||||
// Register the caller
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Members::add(&caller);
|
||||
|
||||
// Feed some values before running `on_finalize` hook
|
||||
System::<T>::set_block_number(1u32.into());
|
||||
let values = T::BenchmarkHelper::get_currency_id_value_pairs();
|
||||
assert_ok!(Oracle::<T, I>::feed_values(RawOrigin::Signed(caller).into(), values));
|
||||
|
||||
#[block]
|
||||
{
|
||||
Oracle::<T, I>::on_finalize(System::<T>::block_number());
|
||||
}
|
||||
|
||||
assert!(!HasDispatched::<T, I>::exists());
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite! {
|
||||
Oracle,
|
||||
crate::mock::new_test_ext(),
|
||||
crate::mock::Test,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{CombineData, Config, MomentOf, TimestampedValueOf};
|
||||
use pezframe_support::traits::{Get, Time};
|
||||
use pezsp_runtime::traits::Saturating;
|
||||
use pezsp_std::{marker, prelude::*};
|
||||
|
||||
/// Sort by value and returns median timestamped value.
|
||||
/// Returns prev_value if not enough valid values.
|
||||
pub struct DefaultCombineData<T, MinimumCount, ExpiresIn, I = ()>(
|
||||
marker::PhantomData<(T, I, MinimumCount, ExpiresIn)>,
|
||||
);
|
||||
|
||||
impl<T, I, MinimumCount, ExpiresIn>
|
||||
CombineData<<T as Config<I>>::OracleKey, TimestampedValueOf<T, I>>
|
||||
for DefaultCombineData<T, MinimumCount, ExpiresIn, I>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
MinimumCount: Get<u32>,
|
||||
ExpiresIn: Get<MomentOf<T, I>>,
|
||||
{
|
||||
fn combine_data(
|
||||
_key: &<T as Config<I>>::OracleKey,
|
||||
mut values: Vec<TimestampedValueOf<T, I>>,
|
||||
prev_value: Option<TimestampedValueOf<T, I>>,
|
||||
) -> Option<TimestampedValueOf<T, I>> {
|
||||
let expires_in = ExpiresIn::get();
|
||||
let now = T::Time::now();
|
||||
|
||||
values.retain(|x| x.timestamp.saturating_add(expires_in) > now);
|
||||
|
||||
let count = values.len() as u32;
|
||||
let minimum_count = MinimumCount::get();
|
||||
if count < minimum_count || count == 0 {
|
||||
return prev_value;
|
||||
}
|
||||
|
||||
let mid_index = count / 2;
|
||||
// Won't panic as `values` ensured not empty.
|
||||
let (_, value, _) =
|
||||
values.select_nth_unstable_by(mid_index as usize, |a, b| a.value.cmp(&b.value));
|
||||
Some(value.clone())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Oracle
|
||||
//!
|
||||
//! A pallet that provides a decentralized and trustworthy way to bring external, off-chain data
|
||||
//! onto the blockchain.
|
||||
//!
|
||||
//! ## Pallet API
|
||||
//!
|
||||
//! See the [`pallet`] module for more information about the interfaces this pallet exposes,
|
||||
//! including its configuration trait, dispatchables, storage items, events and errors.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! The Oracle pallet enables blockchain applications to access real-world data through a
|
||||
//! decentralized network of trusted data providers. It's designed to be flexible and can handle
|
||||
//! various types of external data such as cryptocurrency prices, weather data, sports scores, or
|
||||
//! any other off-chain information that needs to be brought on-chain.
|
||||
//!
|
||||
//! The pallet operates on a permissioned model where only authorized oracle operators can submit
|
||||
//! data. This ensures data quality and prevents spam while maintaining decentralization through
|
||||
//! multiple independent operators. The system aggregates data from multiple sources using
|
||||
//! configurable algorithms, typically taking the median to resist outliers and manipulation
|
||||
//! attempts.
|
||||
//!
|
||||
//! ### Key Concepts
|
||||
//!
|
||||
//! * **Oracle Operators**: A set of trusted accounts authorized to submit data. Managed through the
|
||||
//! [`SortedMembers`] trait, allowing integration with membership pallets.
|
||||
//! * **Data Feeds**: Key-value pairs where keys identify the data type (e.g., currency pair) and
|
||||
//! values contain the actual data (e.g., price).
|
||||
//! * **Data Aggregation**: Configurable algorithms to combine multiple operator inputs into a
|
||||
//! single trusted value, with median aggregation provided by default.
|
||||
//! * **Timestamped Data**: All submitted data includes timestamps for freshness tracking.
|
||||
//!
|
||||
//! ## Low Level / Implementation Details
|
||||
//!
|
||||
//! ### Design Goals
|
||||
//!
|
||||
//! The oracle system aims to provide:
|
||||
//! - **Decentralization**: Multiple independent data providers prevent single points of failure
|
||||
//! - **Data Quality**: Aggregation mechanisms filter out outliers and malicious data
|
||||
//! - **Flexibility**: Configurable data types and aggregation strategies
|
||||
//! - **Performance**: Efficient storage and retrieval of timestamped data
|
||||
//! - **Security**: Permissioned access with cryptographic verification of data integrity
|
||||
//!
|
||||
//! ### Design
|
||||
//!
|
||||
//! The pallet uses a dual-storage approach:
|
||||
//! - [`RawValues`]: Stores individual operator submissions with timestamps
|
||||
//! - [`Values`]: Stores the final aggregated values after processing
|
||||
//!
|
||||
//! This design allows for:
|
||||
//! - Historical tracking of individual operator submissions
|
||||
//! - Efficient access to final aggregated values
|
||||
//! - Clean separation between raw data and processed results
|
||||
//! - Easy integration with data aggregation algorithms
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use pezframe_support::{
|
||||
dispatch::Pays,
|
||||
ensure,
|
||||
pezpallet_prelude::*,
|
||||
traits::{ChangeMembers, Get, SortedMembers, Time},
|
||||
weights::Weight,
|
||||
PalletId, Parameter,
|
||||
};
|
||||
use pezframe_system::pezpallet_prelude::*;
|
||||
use scale_info::TypeInfo;
|
||||
use pezsp_runtime::{
|
||||
traits::{AccountIdConversion, Member},
|
||||
DispatchResult, RuntimeDebug,
|
||||
};
|
||||
use pezsp_std::{prelude::*, vec};
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
mod default_combine_data;
|
||||
pub use default_combine_data::DefaultCombineData;
|
||||
pub mod traits;
|
||||
pub use traits::{CombineData, DataFeeder, DataProvider, DataProviderExtended, OnNewData};
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod weights;
|
||||
|
||||
pub use pallet::*;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
/// Helper trait for benchmarking oracle operations.
|
||||
pub trait BenchmarkHelper<OracleKey, OracleValue, L: Get<u32>> {
|
||||
/// Returns a list of `(oracle_key, oracle_value)` pairs to be used for
|
||||
/// benchmarking.
|
||||
///
|
||||
/// NOTE: User should ensure to at least submit two values, otherwise the
|
||||
/// benchmark linear analysis might fail.
|
||||
fn get_currency_id_value_pairs() -> BoundedVec<(OracleKey, OracleValue), L>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl<OracleKey, OracleValue, L: Get<u32>> BenchmarkHelper<OracleKey, OracleValue, L> for () {
|
||||
fn get_currency_id_value_pairs() -> BoundedVec<(OracleKey, OracleValue), L> {
|
||||
BoundedVec::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[pezframe_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
pub(crate) type MomentOf<T, I = ()> = <<T as Config<I>>::Time as Time>::Moment;
|
||||
pub(crate) type TimestampedValueOf<T, I = ()> =
|
||||
TimestampedValue<<T as Config<I>>::OracleValue, MomentOf<T, I>>;
|
||||
|
||||
/// A wrapper for a value with a timestamp.
|
||||
#[derive(
|
||||
Encode,
|
||||
Decode,
|
||||
RuntimeDebug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Clone,
|
||||
Copy,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
TypeInfo,
|
||||
MaxEncodedLen,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
pub struct TimestampedValue<Value, Moment> {
|
||||
/// The value.
|
||||
pub value: Value,
|
||||
/// The timestamp.
|
||||
pub timestamp: Moment,
|
||||
}
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config<I: 'static = ()>: pezframe_system::Config {
|
||||
/// A hook to be called when new data is received.
|
||||
///
|
||||
/// This hook is triggered whenever an oracle operator successfully submits new data.
|
||||
/// It allows other pallets to react to oracle updates, enabling real-time responses to
|
||||
/// external data changes.
|
||||
type OnNewData: OnNewData<Self::AccountId, Self::OracleKey, Self::OracleValue>;
|
||||
|
||||
/// The implementation to combine raw values into a single aggregated value.
|
||||
///
|
||||
/// This type defines how multiple oracle operator submissions are combined into a single
|
||||
/// trusted value. Common implementations include taking the median (to resist outliers)
|
||||
/// or weighted averages based on operator reputation.
|
||||
type CombineData: CombineData<Self::OracleKey, TimestampedValueOf<Self, I>>;
|
||||
|
||||
/// The time provider for timestamping oracle data.
|
||||
///
|
||||
/// This type provides the current timestamp used to mark when oracle data was submitted.
|
||||
/// Timestamps are crucial for determining data freshness and preventing stale data usage.
|
||||
type Time: Time;
|
||||
|
||||
/// The key type for identifying oracle data feeds.
|
||||
///
|
||||
/// This type is used to uniquely identify different types of oracle data (e.g., currency
|
||||
/// pairs, asset prices, weather data).
|
||||
type OracleKey: Parameter + Member + MaxEncodedLen;
|
||||
|
||||
/// The value type for oracle data.
|
||||
///
|
||||
/// This type represents the actual data submitted by oracle operators (e.g., prices,
|
||||
/// temperatures, scores).
|
||||
type OracleValue: Parameter + Member + Ord + MaxEncodedLen;
|
||||
|
||||
/// The pallet ID.
|
||||
///
|
||||
/// Will be used to derive the pallet's account, which is used as the oracle account
|
||||
/// when values are fed by root.
|
||||
#[pallet::constant]
|
||||
type PalletId: Get<PalletId>;
|
||||
|
||||
/// The source of oracle members.
|
||||
///
|
||||
/// This type provides the set of accounts authorized to submit oracle data.
|
||||
/// Typically implemented by membership pallets to allow governance-controlled
|
||||
/// management of oracle operators.
|
||||
type Members: SortedMembers<Self::AccountId>;
|
||||
|
||||
/// Weight information for extrinsics in this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
|
||||
/// The maximum number of oracle operators that can feed data in a single block.
|
||||
#[pallet::constant]
|
||||
type MaxHasDispatchedSize: Get<u32>;
|
||||
|
||||
/// The maximum number of key-value pairs that can be submitted in a single extrinsic.
|
||||
#[pallet::constant]
|
||||
type MaxFeedValues: Get<u32>;
|
||||
|
||||
/// A helper trait for benchmarking oracle operations.
|
||||
///
|
||||
/// Provides sample data for benchmarking the oracle pallet, allowing accurate
|
||||
/// weight calculations and performance testing.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper: BenchmarkHelper<
|
||||
Self::OracleKey,
|
||||
Self::OracleValue,
|
||||
Self::MaxFeedValues,
|
||||
>;
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T, I = ()> {
|
||||
/// The sender is not a member of the oracle and does not have
|
||||
/// permission to feed data.
|
||||
NoPermission,
|
||||
/// The oracle member has already fed data in the current block.
|
||||
AlreadyFeeded,
|
||||
/// Exceeds the maximum number of `HasDispatched` size.
|
||||
ExceedsMaxHasDispatchedSize,
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
|
||||
pub enum Event<T: Config<I>, I: 'static = ()> {
|
||||
/// New data has been fed into the oracle.
|
||||
NewFeedData {
|
||||
/// The account that fed the data.
|
||||
sender: T::AccountId,
|
||||
/// The key-value pairs of the data that was fed.
|
||||
values: Vec<(T::OracleKey, T::OracleValue)>,
|
||||
},
|
||||
}
|
||||
|
||||
/// The raw values for each oracle operator.
|
||||
///
|
||||
/// Maps `(AccountId, OracleKey)` to `TimestampedValue` containing the operator's submitted
|
||||
/// value along with the timestamp when it was submitted. This storage maintains the complete
|
||||
/// history of individual operator submissions, allowing for data aggregation and audit trails.
|
||||
///
|
||||
/// ## Storage Economics
|
||||
///
|
||||
/// No storage deposits are required as this data is considered essential for the oracle's
|
||||
/// operation and data integrity. The storage cost is borne by the blockchain as part of the
|
||||
/// oracle infrastructure.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn raw_values)]
|
||||
pub type RawValues<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
|
||||
_,
|
||||
Twox64Concat,
|
||||
T::AccountId,
|
||||
Twox64Concat,
|
||||
T::OracleKey,
|
||||
TimestampedValueOf<T, I>,
|
||||
>;
|
||||
|
||||
/// The aggregated values for each oracle key.
|
||||
///
|
||||
/// Maps `OracleKey` to `TimestampedValue`.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn values)]
|
||||
pub type Values<T: Config<I>, I: 'static = ()> =
|
||||
StorageMap<_, Twox64Concat, <T as Config<I>>::OracleKey, TimestampedValueOf<T, I>>;
|
||||
|
||||
/// A set of accounts that have already fed data in the current block.
|
||||
///
|
||||
/// This storage item tracks which oracle operators have already submitted data in the
|
||||
/// current block to enforce the "one submission per block" rule. This prevents spam and
|
||||
/// ensures fair participation among oracle operators.
|
||||
///
|
||||
/// The storage is cleared at the end of each block in the `on_finalize` hook, resetting
|
||||
/// the state for the next block.
|
||||
#[pallet::storage]
|
||||
pub(crate) type HasDispatched<T: Config<I>, I: 'static = ()> =
|
||||
StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxHasDispatchedSize>, ValueQuery>;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
|
||||
/// `on_initialize` to return the weight used in `on_finalize`.
|
||||
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
|
||||
T::WeightInfo::on_finalize()
|
||||
}
|
||||
|
||||
fn on_finalize(_n: BlockNumberFor<T>) {
|
||||
// cleanup for next block
|
||||
<HasDispatched<T, I>>::kill();
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::view_functions]
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
/// Retrieve the aggregated oracle value for a specific key, including its timestamp.
|
||||
pub fn get_value(key: T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
|
||||
Self::get(&key)
|
||||
}
|
||||
|
||||
/// Retrieve every aggregated oracle value tracked by the pallet.
|
||||
pub fn all_values() -> Vec<(T::OracleKey, TimestampedValueOf<T, I>)> {
|
||||
<Values<T, I>>::iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
/// Feeds external data values into the oracle system.
|
||||
///
|
||||
/// ## Dispatch Origin
|
||||
///
|
||||
/// The dispatch origin of this call must be a signed account that is either:
|
||||
/// - A member of the oracle operators set (managed by [`SortedMembers`])
|
||||
/// - The root origin
|
||||
///
|
||||
/// ## Details
|
||||
///
|
||||
/// This function allows authorized oracle operators to submit timestamped key-value pairs
|
||||
/// into the oracle system. Each submitted value is immediately timestamped with the current
|
||||
/// block time and stored in the [`RawValues`] storage. The system then attempts to
|
||||
/// aggregate all raw values for each key using the configured [`CombineData`] trait
|
||||
/// implementation, updating the final [`Values`] storage with the aggregated result.
|
||||
///
|
||||
/// Only one submission per oracle operator per block is allowed to prevent spam and ensure
|
||||
/// fair participation. The function also triggers the [`OnNewData`] hook for each submitted
|
||||
/// value, allowing other pallets to react to new oracle data.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// - [`Error::NoPermission`]: The sender is not authorized to feed data
|
||||
/// - [`Error::AlreadyFeeded`]: The sender has already fed data in the current block
|
||||
/// - [`Error::ExceedsMaxHasDispatchedSize`]: Too many operators have fed data in this block
|
||||
///
|
||||
/// ## Events
|
||||
///
|
||||
/// - [`Event::NewFeedData`]: Emitted when data is successfully fed into the oracle
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(T::WeightInfo::feed_values(values.len() as u32))]
|
||||
pub fn feed_values(
|
||||
origin: OriginFor<T>,
|
||||
values: BoundedVec<(T::OracleKey, T::OracleValue), T::MaxFeedValues>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let feeder = ensure_signed_or_root(origin.clone())?;
|
||||
|
||||
let who = Self::ensure_account(feeder)?;
|
||||
|
||||
// ensure account hasn't dispatched an updated yet
|
||||
<HasDispatched<T, I>>::try_mutate(|set| {
|
||||
set.try_insert(who.clone())
|
||||
.map_err(|_| Error::<T, I>::ExceedsMaxHasDispatchedSize)?
|
||||
.then_some(())
|
||||
.ok_or(Error::<T, I>::AlreadyFeeded)
|
||||
})?;
|
||||
|
||||
Self::do_feed_values(who, values.into());
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
fn get_pallet_account() -> T::AccountId {
|
||||
T::PalletId::get().into_account_truncating()
|
||||
}
|
||||
|
||||
/// Reads the raw values for a given key from all oracle members.
|
||||
pub fn read_raw_values(key: &T::OracleKey) -> Vec<TimestampedValueOf<T, I>> {
|
||||
T::Members::sorted_members()
|
||||
.iter()
|
||||
.chain([Self::get_pallet_account()].iter())
|
||||
.filter_map(|x| Self::raw_values(x, key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the aggregated and timestamped value for a given key.
|
||||
pub fn get(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
|
||||
Self::values(key)
|
||||
}
|
||||
|
||||
fn combined(key: &T::OracleKey) -> Option<TimestampedValueOf<T, I>> {
|
||||
let values = Self::read_raw_values(key);
|
||||
T::CombineData::combine_data(key, values, Self::values(key))
|
||||
}
|
||||
|
||||
fn ensure_account(who: Option<T::AccountId>) -> Result<T::AccountId, DispatchError> {
|
||||
// ensure feeder is authorized
|
||||
if let Some(who) = who {
|
||||
ensure!(T::Members::contains(&who), Error::<T, I>::NoPermission);
|
||||
Ok(who)
|
||||
} else {
|
||||
Ok(Self::get_pallet_account())
|
||||
}
|
||||
}
|
||||
|
||||
fn do_feed_values(who: T::AccountId, values: Vec<(T::OracleKey, T::OracleValue)>) {
|
||||
let now = T::Time::now();
|
||||
for (key, value) in &values {
|
||||
let timestamped = TimestampedValue { value: value.clone(), timestamp: now };
|
||||
RawValues::<T, I>::insert(&who, key, timestamped);
|
||||
|
||||
// Update `Values` storage if `combined` yielded result.
|
||||
if let Some(combined) = Self::combined(key) {
|
||||
<Values<T, I>>::insert(key, combined);
|
||||
}
|
||||
|
||||
T::OnNewData::on_new_data(&who, key, value);
|
||||
}
|
||||
Self::deposit_event(Event::NewFeedData { sender: who, values });
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> ChangeMembers<T::AccountId> for Pallet<T, I> {
|
||||
fn change_members_sorted(
|
||||
_incoming: &[T::AccountId],
|
||||
outgoing: &[T::AccountId],
|
||||
_new: &[T::AccountId],
|
||||
) {
|
||||
// remove values
|
||||
for removed in outgoing {
|
||||
let _ = RawValues::<T, I>::clear_prefix(removed, u32::MAX, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_prime(_prime: Option<T::AccountId>) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> DataProvider<T::OracleKey, T::OracleValue> for Pallet<T, I> {
|
||||
fn get(key: &T::OracleKey) -> Option<T::OracleValue> {
|
||||
Self::get(key).map(|timestamped_value| timestamped_value.value)
|
||||
}
|
||||
}
|
||||
impl<T: Config<I>, I: 'static> DataProviderExtended<T::OracleKey, TimestampedValueOf<T, I>>
|
||||
for Pallet<T, I>
|
||||
{
|
||||
fn get_all_values() -> impl Iterator<Item = (T::OracleKey, Option<TimestampedValueOf<T, I>>)> {
|
||||
<Values<T, I>>::iter().map(|(k, v)| (k, Some(v)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> DataFeeder<T::OracleKey, T::OracleValue, T::AccountId>
|
||||
for Pallet<T, I>
|
||||
{
|
||||
fn feed_value(
|
||||
who: Option<T::AccountId>,
|
||||
key: T::OracleKey,
|
||||
value: T::OracleValue,
|
||||
) -> DispatchResult {
|
||||
Self::do_feed_values(Self::ensure_account(who)?, vec![(key, value)]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate as pezpallet_oracle;
|
||||
|
||||
use crate::{Config, DefaultCombineData};
|
||||
use pezframe_support::{
|
||||
construct_runtime, derive_impl, parameter_types,
|
||||
traits::{ConstU32, SortedMembers, Time},
|
||||
PalletId,
|
||||
};
|
||||
use pezsp_runtime::{traits::IdentityLookup, BuildStorage};
|
||||
|
||||
pub type AccountId = u128;
|
||||
type Key = u32;
|
||||
type Value = u32;
|
||||
|
||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig as pezframe_system::DefaultConfig)]
|
||||
impl pezframe_system::Config for Test {
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static TIME: u32 = 0;
|
||||
pub static MEMBERS: Vec<AccountId> = vec![1, 2, 3];
|
||||
}
|
||||
|
||||
pub struct Timestamp;
|
||||
impl Time for Timestamp {
|
||||
type Moment = u32;
|
||||
|
||||
fn now() -> Self::Moment {
|
||||
TIME::get()
|
||||
}
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
pub fn set_timestamp(val: u32) {
|
||||
TIME::set(val);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Members;
|
||||
|
||||
impl SortedMembers<AccountId> for Members {
|
||||
fn sorted_members() -> Vec<AccountId> {
|
||||
MEMBERS::get().clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn add(who: &AccountId) {
|
||||
MEMBERS::mutate(|members| {
|
||||
members.push(*who);
|
||||
members.sort();
|
||||
})
|
||||
}
|
||||
}
|
||||
parameter_types! {
|
||||
pub const MaxFeedValues: u32 = 5;
|
||||
pub const OraclePalletId: PalletId = PalletId(*b"py/oracl");
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type OnNewData = ();
|
||||
type CombineData = DefaultCombineData<Self, ConstU32<3>, ConstU32<600>>;
|
||||
type Time = Timestamp;
|
||||
type OracleKey = Key;
|
||||
type OracleValue = Value;
|
||||
type PalletId = OraclePalletId;
|
||||
type Members = Members;
|
||||
type WeightInfo = ();
|
||||
type MaxHasDispatchedSize = ConstU32<100>;
|
||||
type MaxFeedValues = MaxFeedValues;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = ();
|
||||
}
|
||||
|
||||
type Block = pezframe_system::mocking::MockBlock<Test>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Test {
|
||||
System: pezframe_system,
|
||||
ModuleOracle: pezpallet_oracle,
|
||||
}
|
||||
);
|
||||
|
||||
pub fn set_members(members: Vec<AccountId>) {
|
||||
MEMBERS::set(members);
|
||||
}
|
||||
|
||||
// This function basically just builds a genesis storage key/value store
|
||||
// according to our desired mockup.
|
||||
pub fn new_test_ext() -> pezsp_io::TestExternalities {
|
||||
let storage = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
|
||||
let mut t: pezsp_io::TestExternalities = storage.into();
|
||||
|
||||
t.execute_with(|| {
|
||||
Timestamp::set_timestamp(12345);
|
||||
});
|
||||
|
||||
t
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::*;
|
||||
use pezframe_support::{assert_noop, assert_ok};
|
||||
use mock::*;
|
||||
|
||||
#[test]
|
||||
fn should_feed_values_from_member() {
|
||||
new_test_ext().execute_with(|| {
|
||||
System::set_block_number(1);
|
||||
let account_id: AccountId = 1;
|
||||
|
||||
assert_noop!(
|
||||
ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(5),
|
||||
vec![(50, 1000), (51, 900), (52, 800)].try_into().unwrap()
|
||||
),
|
||||
Error::<Test, _>::NoPermission,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(account_id),
|
||||
vec![(50, 1000), (51, 900), (52, 800)].try_into().unwrap()
|
||||
)
|
||||
.unwrap()
|
||||
.pays_fee,
|
||||
Pays::No
|
||||
);
|
||||
System::assert_last_event(RuntimeEvent::ModuleOracle(crate::Event::NewFeedData {
|
||||
sender: 1,
|
||||
values: vec![(50, 1000), (51, 900), (52, 800)],
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&account_id, &50),
|
||||
Some(TimestampedValue { value: 1000, timestamp: 12345 })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&account_id, &51),
|
||||
Some(TimestampedValue { value: 900, timestamp: 12345 })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&account_id, &52),
|
||||
Some(TimestampedValue { value: 800, timestamp: 12345 })
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_feed_values_from_root() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let root_feeder: AccountId = OraclePalletId::get().into_account_truncating();
|
||||
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::root(),
|
||||
vec![(50, 1000), (51, 900), (52, 800)].try_into().unwrap()
|
||||
));
|
||||
|
||||
// Or feed from root using the DataFeeder trait with None
|
||||
assert_ok!(ModuleOracle::feed_value(None, 53, 700));
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&root_feeder, &50),
|
||||
Some(TimestampedValue { value: 1000, timestamp: 12345 })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&root_feeder, &51),
|
||||
Some(TimestampedValue { value: 900, timestamp: 12345 })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&root_feeder, &52),
|
||||
Some(TimestampedValue { value: 800, timestamp: 12345 })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::raw_values(&root_feeder, &53),
|
||||
Some(TimestampedValue { value: 700, timestamp: 12345 })
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_feed_values_from_root_directly() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let root_feeder: AccountId = OraclePalletId::get().into_account_truncating();
|
||||
|
||||
assert_noop!(
|
||||
ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(root_feeder),
|
||||
vec![(50, 1000), (51, 900), (52, 800)].try_into().unwrap()
|
||||
),
|
||||
Error::<Test, _>::NoPermission,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_read_raw_values() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let key: u32 = 50;
|
||||
|
||||
let raw_values = ModuleOracle::read_raw_values(&key);
|
||||
assert_eq!(raw_values, vec![]);
|
||||
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(key, 1000)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(key, 1200)].try_into().unwrap()
|
||||
));
|
||||
|
||||
let raw_values = ModuleOracle::read_raw_values(&key);
|
||||
assert_eq!(
|
||||
raw_values,
|
||||
vec![
|
||||
TimestampedValue { value: 1000, timestamp: 12345 },
|
||||
TimestampedValue { value: 1200, timestamp: 12345 },
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_combined_data() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let key: u32 = 50;
|
||||
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(key, 1300)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(key, 1000)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(3),
|
||||
vec![(key, 1200)].try_into().unwrap()
|
||||
));
|
||||
|
||||
let expected = Some(TimestampedValue { value: 1200, timestamp: 12345 });
|
||||
|
||||
assert_eq!(ModuleOracle::get(&key), expected);
|
||||
|
||||
Timestamp::set_timestamp(23456);
|
||||
|
||||
assert_eq!(ModuleOracle::get(&key), expected);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_none_for_non_exist_key() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(ModuleOracle::get(&50), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_calls_should_fail() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(50, 1300)].try_into().unwrap()
|
||||
));
|
||||
|
||||
// Fails feeding by the extrinsic
|
||||
assert_noop!(
|
||||
ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(50, 1300)].try_into().unwrap()
|
||||
),
|
||||
Error::<Test, _>::AlreadyFeeded,
|
||||
);
|
||||
|
||||
// But not if fed thought the trait internally
|
||||
assert_ok!(ModuleOracle::feed_value(Some(1), 50, 1300));
|
||||
|
||||
ModuleOracle::on_finalize(1);
|
||||
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(50, 1300)].try_into().unwrap()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_all_values_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let eur: u32 = 1;
|
||||
let jpy: u32 = 2;
|
||||
|
||||
assert_eq!(ModuleOracle::get_all_values().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
// feed eur & jpy
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(eur, 1300)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(eur, 1000)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(3),
|
||||
vec![(jpy, 9000)].try_into().unwrap()
|
||||
));
|
||||
|
||||
// not enough eur & jpy prices
|
||||
assert_eq!(ModuleOracle::get(&eur), None);
|
||||
assert_eq!(ModuleOracle::get(&jpy), None);
|
||||
assert_eq!(ModuleOracle::get_all_values().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
// finalize block
|
||||
ModuleOracle::on_finalize(1);
|
||||
|
||||
// feed eur & jpy
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(3),
|
||||
vec![(eur, 1200)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(jpy, 8000)].try_into().unwrap()
|
||||
));
|
||||
|
||||
// enough eur prices
|
||||
let eur_price = Some(TimestampedValue { value: 1200, timestamp: 12345 });
|
||||
assert_eq!(ModuleOracle::get(&eur), eur_price);
|
||||
|
||||
// not enough jpy prices
|
||||
assert_eq!(ModuleOracle::get(&jpy), None);
|
||||
|
||||
assert_eq!(ModuleOracle::get_all_values().collect::<Vec<_>>(), vec![(eur, eur_price)]);
|
||||
|
||||
// feed jpy
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(jpy, 7000)].try_into().unwrap()
|
||||
));
|
||||
|
||||
// enough jpy prices
|
||||
let jpy_price = Some(TimestampedValue { value: 8000, timestamp: 12345 });
|
||||
assert_eq!(ModuleOracle::get(&jpy), jpy_price);
|
||||
|
||||
assert_eq!(
|
||||
ModuleOracle::get_all_values().collect::<Vec<_>>(),
|
||||
vec![(eur, eur_price), (jpy, jpy_price)]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_member_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
set_members(vec![2, 3, 4]);
|
||||
<ModuleOracle as ChangeMembers<AccountId>>::change_members_sorted(&[4], &[1], &[2, 3, 4]);
|
||||
assert_noop!(
|
||||
ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(50, 1000)].try_into().unwrap()
|
||||
),
|
||||
Error::<Test, _>::NoPermission,
|
||||
);
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(50, 1000)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(4),
|
||||
vec![(50, 1000)].try_into().unwrap()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_clear_data_for_removed_members() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(50, 1000)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(50, 1000)].try_into().unwrap()
|
||||
));
|
||||
|
||||
ModuleOracle::change_members_sorted(&[4], &[1], &[2, 3, 4]);
|
||||
|
||||
assert_eq!(ModuleOracle::raw_values(&1, 50), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn values_are_updated_on_feed() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(1),
|
||||
vec![(50, 900)].try_into().unwrap()
|
||||
));
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(2),
|
||||
vec![(50, 1000)].try_into().unwrap()
|
||||
));
|
||||
|
||||
assert_eq!(ModuleOracle::values(50), None);
|
||||
|
||||
// Upon the third price feed, the value is updated immediately after `combine`
|
||||
// can produce valid result.
|
||||
assert_ok!(ModuleOracle::feed_values(
|
||||
RuntimeOrigin::signed(3),
|
||||
vec![(50, 1100)].try_into().unwrap()
|
||||
));
|
||||
assert_eq!(
|
||||
ModuleOracle::values(50),
|
||||
Some(TimestampedValue { value: 1000, timestamp: 12345 })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! This module provides traits for data feeding and provisioning.
|
||||
|
||||
use pezsp_runtime::DispatchResult;
|
||||
use pezsp_std::vec::Vec;
|
||||
|
||||
/// A trait for feeding data to a data provider.
|
||||
pub trait DataFeeder<Key, Value, AccountId> {
|
||||
/// Feeds a new value for a given key.
|
||||
fn feed_value(who: Option<AccountId>, key: Key, value: Value) -> DispatchResult;
|
||||
}
|
||||
|
||||
/// A simple trait for providing data.
|
||||
pub trait DataProvider<Key, Value> {
|
||||
/// Returns the data for a given key.
|
||||
fn get(key: &Key) -> Option<Value>;
|
||||
}
|
||||
|
||||
/// An extended `DataProvider` that provides timestamped data.
|
||||
pub trait DataProviderExtended<Key, TimestampedValue> {
|
||||
/// Returns a list of all keys and their optional timestamped values.
|
||||
fn get_all_values() -> impl Iterator<Item = (Key, Option<TimestampedValue>)>;
|
||||
}
|
||||
|
||||
/// Returns the median of a list of values.
|
||||
pub fn median<T: Ord + Clone>(mut items: Vec<T>) -> Option<T> {
|
||||
if items.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mid_index = items.len() / 2;
|
||||
|
||||
// Won't panic as `items` ensured not empty.
|
||||
let (_, item, _) = items.select_nth_unstable(mid_index);
|
||||
Some(item.clone())
|
||||
}
|
||||
|
||||
/// Creates a median data provider from a list of other data providers.
|
||||
#[macro_export]
|
||||
macro_rules! create_median_value_data_provider {
|
||||
($name:ident, $key:ty, $value:ty, $timestamped_value:ty, [$( $provider:ty ),*]) => {
|
||||
pub struct $name;
|
||||
impl $crate::DataProvider<$key, $value> for $name {
|
||||
fn get(key: &$key) -> Option<$value> {
|
||||
let mut values = vec![];
|
||||
$(
|
||||
if let Some(v) = <$provider as $crate::DataProvider<$key, $value>>::get(&key) {
|
||||
values.push(v);
|
||||
}
|
||||
)*
|
||||
$crate::traits::median(values)
|
||||
}
|
||||
}
|
||||
impl $crate::DataProviderExtended<$key, $timestamped_value> for $name {
|
||||
fn get_all_values() -> impl Iterator<Item = ($key, Option<$timestamped_value>)> {
|
||||
let mut keys = pezsp_std::collections::btree_set::BTreeSet::new();
|
||||
$(
|
||||
<$provider as $crate::DataProviderExtended<$key, $timestamped_value>>::get_all_values()
|
||||
.into_iter()
|
||||
.for_each(|(k, _)| { keys.insert(k); });
|
||||
)*
|
||||
keys.into_iter().map(|k| (k, Self::get(&k)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to combine data from multiple providers.
|
||||
pub trait CombineData<Key, TimestampedValue> {
|
||||
/// Combine data provided by operators
|
||||
fn combine_data(
|
||||
key: &Key,
|
||||
values: Vec<TimestampedValue>,
|
||||
prev_value: Option<TimestampedValue>,
|
||||
) -> Option<TimestampedValue>;
|
||||
}
|
||||
|
||||
/// A handler for new data events.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait OnNewData<AccountId, Key, Value> {
|
||||
/// New data is available
|
||||
fn on_new_data(who: &AccountId, key: &Key, value: &Value);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pezsp_std::cell::RefCell;
|
||||
|
||||
thread_local! {
|
||||
static MOCK_PRICE_1: RefCell<Option<u8>> = RefCell::new(None);
|
||||
static MOCK_PRICE_2: RefCell<Option<u8>> = RefCell::new(None);
|
||||
static MOCK_PRICE_3: RefCell<Option<u8>> = RefCell::new(None);
|
||||
static MOCK_PRICE_4: RefCell<Option<u8>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
macro_rules! mock_data_provider {
|
||||
($provider:ident, $price:ident) => {
|
||||
pub struct $provider;
|
||||
impl $provider {
|
||||
fn set_price(price: Option<u8>) {
|
||||
$price.with(|v| *v.borrow_mut() = price)
|
||||
}
|
||||
}
|
||||
impl DataProvider<u8, u8> for $provider {
|
||||
fn get(_: &u8) -> Option<u8> {
|
||||
$price.with(|v| *v.borrow())
|
||||
}
|
||||
}
|
||||
impl DataProviderExtended<u8, u8> for $provider {
|
||||
fn get_all_values() -> impl Iterator<Item = (u8, Option<u8>)> {
|
||||
vec![(0, Self::get(&0))].into_iter()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mock_data_provider!(Provider1, MOCK_PRICE_1);
|
||||
mock_data_provider!(Provider2, MOCK_PRICE_2);
|
||||
mock_data_provider!(Provider3, MOCK_PRICE_3);
|
||||
mock_data_provider!(Provider4, MOCK_PRICE_4);
|
||||
|
||||
create_median_value_data_provider!(
|
||||
Providers,
|
||||
u8,
|
||||
u8,
|
||||
u8,
|
||||
[Provider1, Provider2, Provider3, Provider4]
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn median_value_data_provider_works() {
|
||||
assert_eq!(<Providers as DataProvider<_, _>>::get(&0), None);
|
||||
|
||||
let data = vec![
|
||||
(vec![None, None, None, Some(1)], Some(1)),
|
||||
(vec![None, None, Some(2), Some(1)], Some(2)),
|
||||
(vec![Some(5), Some(2), None, Some(7)], Some(5)),
|
||||
(vec![Some(5), Some(13), Some(2), Some(7)], Some(7)),
|
||||
];
|
||||
|
||||
for (values, target) in data {
|
||||
Provider1::set_price(values[0]);
|
||||
Provider2::set_price(values[1]);
|
||||
Provider3::set_price(values[2]);
|
||||
Provider4::set_price(values[3]);
|
||||
|
||||
assert_eq!(<Providers as DataProvider<_, _>>::get(&0), target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) 2020-2025 Acala Foundation.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Autogenerated weights for `pezpallet_oracle`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 50.0.0
|
||||
//! DATE: 2025-09-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `Mac.lan`, CPU: `<UNKNOWN>`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
|
||||
|
||||
// Executed Command:
|
||||
// frame-omni-bencher
|
||||
// v1
|
||||
// benchmark
|
||||
// pallet
|
||||
// --extrinsic=all
|
||||
// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm
|
||||
// --pallet=pezpallet_oracle
|
||||
// --header=bizinikiwi/HEADER-APACHE2
|
||||
// --output=bizinikiwi/pezframe/honzon/oracle/src/weights.rs
|
||||
// --wasm-execution=compiled
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --heap-pages=4096
|
||||
// --template=bizinikiwi/.maintain/frame-weight-template.hbs
|
||||
// --no-storage-info
|
||||
// --no-min-squares
|
||||
// --no-median-slopes
|
||||
// --genesis-builder-policy=none
|
||||
// --exclude-pallets=pezpallet_xcm,pezpallet_xcm_benchmarks::fungible,pezpallet_xcm_benchmarks::generic,pezpallet_nomination_pools,pezpallet_remark,pezpallet_transaction_storage,pezpallet_election_provider_multi_block,pezpallet_election_provider_multi_block::signed,pezpallet_election_provider_multi_block::unsigned,pezpallet_election_provider_multi_block::verifier
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use pezframe_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for `pezpallet_oracle`.
|
||||
pub trait WeightInfo {
|
||||
fn feed_values(x: u32, ) -> Weight;
|
||||
fn on_finalize() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for `pezpallet_oracle` using the Bizinikiwi node and recommended hardware.
|
||||
pub struct BizinikiwiWeight<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
|
||||
/// Storage: `TechnicalMembership::Members` (r:1 w:0)
|
||||
/// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Oracle::HasDispatched` (r:1 w:1)
|
||||
/// Proof: `Oracle::HasDispatched` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Timestamp::Now` (r:1 w:0)
|
||||
/// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Oracle::RawValues` (r:10 w:10)
|
||||
/// Proof: `Oracle::RawValues` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Oracle::Values` (r:10 w:0)
|
||||
/// Proof: `Oracle::Values` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
|
||||
/// The range of component `x` is `[0, 10]`.
|
||||
fn feed_values(x: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `3`
|
||||
// Estimated: `4687 + x * (2551 ±0)`
|
||||
// Minimum execution time: 5_000_000 picoseconds.
|
||||
Weight::from_parts(6_049_545, 4687)
|
||||
// Standard Error: 9_666
|
||||
.saturating_add(Weight::from_parts(5_167_799, 0).saturating_mul(x.into()))
|
||||
.saturating_add(T::DbWeight::get().reads(3_u64))
|
||||
.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(x.into())))
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into())))
|
||||
.saturating_add(Weight::from_parts(0, 2551).saturating_mul(x.into()))
|
||||
}
|
||||
/// Storage: `Oracle::HasDispatched` (r:0 w:1)
|
||||
/// Proof: `Oracle::HasDispatched` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
|
||||
fn on_finalize() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 0_000 picoseconds.
|
||||
Weight::from_parts(1_000_000, 0)
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
/// Storage: `TechnicalMembership::Members` (r:1 w:0)
|
||||
/// Proof: `TechnicalMembership::Members` (`max_values`: Some(1), `max_size`: Some(3202), added: 3697, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Oracle::HasDispatched` (r:1 w:1)
|
||||
/// Proof: `Oracle::HasDispatched` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Timestamp::Now` (r:1 w:0)
|
||||
/// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Oracle::RawValues` (r:10 w:10)
|
||||
/// Proof: `Oracle::RawValues` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Oracle::Values` (r:10 w:0)
|
||||
/// Proof: `Oracle::Values` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`)
|
||||
/// The range of component `x` is `[0, 10]`.
|
||||
fn feed_values(x: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `3`
|
||||
// Estimated: `4687 + x * (2551 ±0)`
|
||||
// Minimum execution time: 5_000_000 picoseconds.
|
||||
Weight::from_parts(6_049_545, 4687)
|
||||
// Standard Error: 9_666
|
||||
.saturating_add(Weight::from_parts(5_167_799, 0).saturating_mul(x.into()))
|
||||
.saturating_add(RocksDbWeight::get().reads(3_u64))
|
||||
.saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(x.into())))
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(x.into())))
|
||||
.saturating_add(Weight::from_parts(0, 2551).saturating_mul(x.into()))
|
||||
}
|
||||
/// Storage: `Oracle::HasDispatched` (r:0 w:1)
|
||||
/// Proof: `Oracle::HasDispatched` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
|
||||
fn on_finalize() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 0_000 picoseconds.
|
||||
Weight::from_parts(1_000_000, 0)
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user