-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Oracle pallet and precompile (#853)
* chore : switch to asset-id evm * cleanup * bump subxt * clean slate * use common exports * use common exports * wip * wip * wip * fix compile * cleanup * compiling * precompiles fixes * runtimes building * update mocks * cleanup tests * update js types * update subxt * cleanup clippy * use nightly fmt * use stable fmt * cleanup clippy * cleanup tests * cleanup tests * cleanup fmt * more tests passing * cleanup clippy * clipyy fixes * cleanup clippy * import oracle pallet * update traits * update precompile * cleanup mock * fix * updates to tests * update precompile * update docs * update interface * fix tests
- Loading branch information
Showing
50 changed files
with
1,946 additions
and
56 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
[package] | ||
name = "pallet-oracle" | ||
description = "Oracle module that makes off-chain data available on-chain." | ||
repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/oracle" | ||
license = "Apache-2.0" | ||
version = "1.1.0" | ||
authors = ["Laminar Developers <[email protected]>"] | ||
edition = "2021" | ||
|
||
[dependencies] | ||
parity-scale-codec = { workspace = true } | ||
scale-info = { workspace = true } | ||
serde = { workspace = true, optional = true } | ||
|
||
frame-support = { workspace = true } | ||
frame-system = { workspace = true } | ||
sp-application-crypto = { workspace = true } | ||
sp-io = { workspace = true } | ||
sp-runtime = { workspace = true } | ||
sp-std = { workspace = true } | ||
frame-benchmarking = { workspace = true, optional = true } | ||
tangle-primitives = { workspace = true } | ||
|
||
[dev-dependencies] | ||
sp-core = { workspace = true } | ||
|
||
[features] | ||
default = [ "std" ] | ||
std = [ | ||
"frame-benchmarking?/std", | ||
"frame-support/std", | ||
"frame-system/std", | ||
"tangle-primitives/std", | ||
"parity-scale-codec/std", | ||
"scale-info/std", | ||
"serde", | ||
"sp-application-crypto/std", | ||
"sp-io/std", | ||
"sp-runtime/std", | ||
"sp-std/std", | ||
] | ||
try-runtime = [ | ||
"frame-support/try-runtime", | ||
"frame-system/try-runtime", | ||
"sp-runtime/try-runtime", | ||
] | ||
runtime-benchmarks = [ | ||
"frame-benchmarking/runtime-benchmarks", | ||
"frame-support/runtime-benchmarks", | ||
"frame-system/runtime-benchmarks", | ||
"sp-runtime/runtime-benchmarks", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Oracle Pallet | ||
|
||
## Overview | ||
|
||
The Oracle pallet provides a decentralized mechanism for feeding external off-chain data into the Tangle blockchain. It allows authorized oracle operators to submit data which can then be aggregated to provide reliable on-chain values. | ||
|
||
## Features | ||
|
||
- **Authorized Data Feeding**: Only authorized operators can submit data to the oracle | ||
- **Timestamped Values**: All submitted values include timestamps for freshness verification | ||
- **Data Aggregation**: Raw values from multiple operators can be combined using customizable aggregation logic | ||
- **Flexible Key-Value Storage**: Supports arbitrary key types for versatile data storage | ||
- **EVM Integration**: Includes precompile support for Ethereum compatibility | ||
|
||
## Interface | ||
|
||
### Extrinsics (Callable Functions) | ||
|
||
- `feed_values(values: Vec<(OracleKey, OracleValue)>)`: Submit multiple key-value pairs to the oracle | ||
- Requires sender to be an authorized operator | ||
- Values are stored with current timestamp | ||
- Limited by `MaxFeedValues` configuration | ||
|
||
### Storage Queries | ||
|
||
- `get(key)`: Retrieve the current combined value for a given key | ||
- `get_all_values()`: Retrieve all stored key-value pairs | ||
- `read_raw_values(key)`: Get all raw values submitted for a specific key | ||
|
||
### EVM Precompile Interface | ||
|
||
The pallet includes an EVM precompile that exposes the following functions: | ||
|
||
```solidity | ||
interface IOraclePrecompile { | ||
// Feed multiple values into the oracle | ||
function feedValues(uint256[] keys, uint256[] values) external; | ||
// Read a value from the oracle | ||
function getValue(uint256 key) external view returns (uint256 value, uint256 timestamp); | ||
} | ||
``` | ||
|
||
## Configuration | ||
|
||
The pallet is configurable through the following types: | ||
|
||
- `OracleKey`: The type used for oracle keys (e.g., `u32`) | ||
- `OracleValue`: The type used for oracle values (e.g., `u64`) | ||
- `MaxFeedValues`: Maximum number of values that can be fed in a single call | ||
- `Members`: Source of oracle operator membership (typically using `pallet-membership`) | ||
|
||
## Usage Examples | ||
|
||
### Feeding Values | ||
|
||
```rust | ||
// Submit values through runtime call | ||
Oracle::feed_values( | ||
RuntimeOrigin::signed(operator_account), | ||
vec![(key1, value1), (key2, value2)] | ||
)?; | ||
|
||
// Read values | ||
if let Some(timestamped_value) = Oracle::get(&key) { | ||
let value = timestamped_value.value; | ||
let timestamp = timestamped_value.timestamp; | ||
// Use the value... | ||
} | ||
``` | ||
|
||
### Through EVM | ||
|
||
```solidity | ||
// Assuming the precompile is deployed at ORACLE_ADDRESS | ||
IOraclePrecompile oracle = IOraclePrecompile(ORACLE_ADDRESS); | ||
// Feed values | ||
uint256[] memory keys = new uint256[](2); | ||
uint256[] memory values = new uint256[](2); | ||
keys[0] = 1; | ||
keys[1] = 2; | ||
values[0] = 100; | ||
values[1] = 200; | ||
oracle.feedValues(keys, values); | ||
// Read value | ||
(uint256 value, uint256 timestamp) = oracle.getValue(1); | ||
``` | ||
|
||
## Security | ||
|
||
- Only authorized operators can feed values | ||
- All values are timestamped to ensure data freshness | ||
- Membership changes automatically clean up old operator data | ||
- Gas costs are properly accounted for in EVM operations | ||
|
||
## Dependencies | ||
|
||
- `frame-support`: Core FRAME support library | ||
- `frame-system`: Core Substrate system library | ||
- `pallet-membership`: (optional) For managing oracle operators | ||
- `pallet-evm`: For EVM precompile support |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "pallet-oracle-runtime-api" | ||
version = "1.1.0" | ||
authors = ["Laminar Developers <[email protected]>"] | ||
edition = "2021" | ||
license = "Apache-2.0" | ||
description = "Runtime API module for pallet-oracle." | ||
repository = "https://github.com/open-web3-stack/open-runtime-module-library" | ||
|
||
[dependencies] | ||
parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"] } | ||
sp-api = { workspace = true } | ||
sp-std = { workspace = true } | ||
|
||
[features] | ||
default = [ "std" ] | ||
std = [ "parity-scale-codec/std", "sp-api/std", "sp-std/std" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//! Runtime API definition for oracle module. | ||
#![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 parity_scale_codec::Codec; | ||
use sp_std::prelude::Vec; | ||
|
||
sp_api::decl_runtime_apis! { | ||
pub trait OracleApi<ProviderId, Key, Value> where | ||
ProviderId: Codec, | ||
Key: Codec, | ||
Value: Codec, | ||
{ | ||
fn get_value(provider_id: ProviderId, key: Key) -> Option<Value>; | ||
fn get_all_values(provider_id: ProviderId) -> Vec<(Key, Option<Value>)>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use super::*; | ||
use crate::Pallet as Oracle; | ||
|
||
use frame_benchmarking::v2::*; | ||
|
||
use frame_support::assert_ok; | ||
use frame_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, | ||
} | ||
} |
Oops, something went wrong.