Skip to content

Commit

Permalink
Oracle data integration (#922)
Browse files Browse the repository at this point in the history
* Integrate oracle pallet

* integrate oracle data to core

* test oracle data fetch

* parameterized bounded vecs

* fmt

* clean

* remove unwrap

* add build checks

* add changelog

* merge

* clean

* add benchmarks

* fix
  • Loading branch information
JesseAbram authored Aug 15, 2024
1 parent aa4c3d9 commit 5d64794
Show file tree
Hide file tree
Showing 19 changed files with 545 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ At the moment this project **does not** adhere to
- Add `blake2` as built in hash function and make `HashingAlgorithm` non-exhaustive ([#881](https://github.com/entropyxyz/entropy-core/pull/881))
- Add sort to subgroup signer selection ([#900](https://github.com/entropyxyz/entropy-core/pull/900))
- Create four node Docker Compose chainspec ([#902](https://github.com/entropyxyz/entropy-core/pull/902))
- Oracle data integration ([#922](https://github.com/entropyxyz/entropy-core/pull/922))

### Changed
- Move TSS mnemonic out of keystore ([#853](https://github.com/entropyxyz/entropy-core/pull/853))
Expand Down
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified crates/client/entropy_metadata.scale
Binary file not shown.
23 changes: 17 additions & 6 deletions crates/threshold-signature-server/src/helpers/substrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
entropy::{
self,
runtime_types::{
bounded_collections::bounded_vec::BoundedVec,
bounded_collections::bounded_vec::BoundedVec, pallet_programs::pallet::ProgramInfo,
pallet_registry::pallet::RegisteredInfo,
},
},
Expand Down Expand Up @@ -52,13 +52,24 @@ pub async fn get_program(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
program_pointer: &<EntropyConfig as Config>::Hash,
) -> Result<Vec<u8>, UserErr> {
) -> Result<ProgramInfo<AccountId32>, UserErr> {
let bytecode_address = entropy::storage().programs().programs(program_pointer);

Ok(query_chain(api, rpc, bytecode_address, None)
let program_info = query_chain(api, rpc, bytecode_address, None)
.await?
.ok_or(UserErr::NoProgramDefined(program_pointer.to_string()))?
.bytecode)
.ok_or(UserErr::NoProgramDefined(program_pointer.to_string()))?;
Ok(program_info)
}

/// Queries the oracle data needed for the program
pub async fn get_oracle_data(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
program_oracle_data: Vec<u8>,
) -> Result<Vec<u8>, UserErr> {
let oracle_data_call = entropy::storage().oracle().oracle_data(BoundedVec(program_oracle_data));
let oracle_info =
query_chain(api, rpc, oracle_data_call, None).await?.unwrap_or(BoundedVec(vec![]));
Ok(oracle_info.0)
}

/// Returns a registered user's key visibility
Expand Down
1 change: 1 addition & 0 deletions crates/threshold-signature-server/src/helpers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use entropy_kvdb::{encrypted_sled::PasswordMethod, get_db_path, kv_manager::KvMa
use entropy_protocol::PartyId;
use entropy_shared::{DAVE_VERIFYING_KEY, EVE_VERIFYING_KEY, NETWORK_PARENT_KEY};
use std::time::Duration;

use subxt::{
backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner,
utils::AccountId32 as SubxtAccountId32, Config, OnlineClient,
Expand Down
4 changes: 2 additions & 2 deletions crates/threshold-signature-server/src/helpers/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ pub async fn compute_hash(
},
HashingAlgorithm::Blake2_256 => Ok(blake2_256(message)),
HashingAlgorithm::Custom(i) => {
let program = get_program(api, rpc, &programs_data[*i].program_pointer).await?;
runtime.custom_hash(program.as_slice(), message).map_err(|e| e.into())
let program_info = get_program(api, rpc, &programs_data[*i].program_pointer).await?;
runtime.custom_hash(program_info.bytecode.as_slice(), message).map_err(|e| e.into())
},
_ => Err(UserErr::UnknownHashingAlgorithm),
}
Expand Down
15 changes: 11 additions & 4 deletions crates/threshold-signature-server/src/user/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ use crate::{
launch::LATEST_BLOCK_NUMBER_NEW_USER,
signing::{do_signing, Hasher},
substrate::{
get_program, get_registered_details, get_stash_address, query_chain, submit_transaction,
get_oracle_data, get_program, get_registered_details, get_stash_address, query_chain,
submit_transaction,
},
user::{check_in_registration_group, compute_hash, do_dkg},
validator::{get_signer, get_signer_and_x25519_secret},
Expand Down Expand Up @@ -183,11 +184,17 @@ pub async fn sign_tx(

let mut runtime = Runtime::new(ProgramConfig { fuel });

for (i, program_info) in user_details.programs_data.0.iter().enumerate() {
let program = get_program(&api, &rpc, &program_info.program_pointer).await?;
for (i, program_data) in user_details.programs_data.0.iter().enumerate() {
let program_info = get_program(&api, &rpc, &program_data.program_pointer).await?;
let oracle_data = get_oracle_data(&api, &rpc, program_info.oracle_data_pointer).await?;
let auxilary_data = auxilary_data_vec[i].as_ref().map(hex::decode).transpose()?;
let signature_request = SignatureRequest { message: message.clone(), auxilary_data };
runtime.evaluate(&program, &signature_request, Some(&program_info.program_config), None)?;
runtime.evaluate(
&program_info.bytecode,
&signature_request,
Some(&program_data.program_config),
Some(&oracle_data),
)?;
}

let with_parent_key = user_details.derivation_path.is_some();
Expand Down
2 changes: 2 additions & 0 deletions crates/threshold-signature-server/src/user/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ pub enum UserErr {
ValidationErr(#[from] crate::validation::errors::ValidationErr),
#[error("No program set at: {0}")]
NoProgramDefined(String),
#[error("No oracle data for pointer: {0}")]
NoOracleDataForPointer(String),
#[error("No program pointer defined for account")]
NoProgramPointerDefined(),
#[error("Runtime error: {0:?}")]
Expand Down
21 changes: 20 additions & 1 deletion crates/threshold-signature-server/src/user/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ use crate::{
DEFAULT_ENDPOINT, DEFAULT_MNEMONIC,
},
signing::Hasher,
substrate::{query_chain, submit_transaction},
substrate::{get_oracle_data, query_chain, submit_transaction},
tests::{
check_has_confirmation, check_if_confirmation, create_clients, initialize_test_logger,
remove_program, run_to_block, setup_client, spawn_testing_validators, unsafe_get,
Expand Down Expand Up @@ -1682,6 +1682,25 @@ async fn test_increment_or_wipe_request_limit() {
clean_tests();
}

#[tokio::test]
#[serial_test::serial]
async fn test_get_oracle_data() {
initialize_test_logger().await;
let cxt = testing_context().await;
setup_client().await;
let api = get_api(&cxt.node_proc.ws_url).await.unwrap();
let rpc = get_rpc(&cxt.node_proc.ws_url).await.unwrap();
run_to_block(&rpc, 1).await;

let oracle_data = get_oracle_data(&api, &rpc, "block_number_entropy".encode()).await.unwrap();
let current_block = rpc.chain_get_header(None).await.unwrap().unwrap().number;
assert_eq!(current_block.encode(), oracle_data);

// fails gracefully
let oracle_data_fail = get_oracle_data(&api, &rpc, "random_heading".encode()).await.unwrap();
assert_eq!(oracle_data_fail.len(), 0);
}

pub async fn submit_transaction_requests(
validator_urls_and_keys: Vec<(String, [u8; 32])>,
signature_request: UserSignatureRequest,
Expand Down
40 changes: 40 additions & 0 deletions pallets/oracle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name ="pallet-oracle"
version ='0.2.0-rc.1'
authors =['Entropy Cryptography <engineering@entropy.xyz>']
homepage ='https://entropy.xyz/'
license ='AGPL-3.0-or-later'
repository='https://github.com/entropyxyz/entropy-core'
edition ='2021'
publish =false

[dependencies]
codec ={ package="parity-scale-codec", version="3.6.3", default-features=false, features=["derive"] }
scale-info={ version="2.11", default-features=false, features=["derive"] }

frame-benchmarking={ version="29.0.0", default-features=false, optional=true }
frame-support ={ version="29.0.0", default-features=false }
frame-system ={ version="29.0.0", default-features=false }
sp-runtime ={ version="32.0.0", default-features=false }
sp-std ={ version="14.0.0", default-features=false }

[dev-dependencies]
sp-core={ version="29.0.0" }
sp-io ={ version="31.0.0" }

[features]
default=["std"]
runtime-benchmarks=[
'frame-benchmarking',
'frame-support/runtime-benchmarks',
'frame-system/runtime-benchmarks',
]
std=[
"frame-support/std",
"frame-system/std",
"scale-info/std",
"sp-runtime/std",
"sp-std/std",
'frame-benchmarking/std',
]
try-runtime=["frame-support/try-runtime"]
33 changes: 33 additions & 0 deletions pallets/oracle/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (C) 2023 Entropy Cryptography Inc.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

// //! Benchmarking setup for pallet-oracle

use super::*;

#[allow(unused)]
use crate::Pallet as Oracle;
use frame_benchmarking::benchmarks;

benchmarks! {
on_initialize {
}: {
Oracle::<T>::on_initialize(50u32.into());
} verify {
assert_eq!(OracleData::<T>::get(BoundedVec::try_from("block_number_entropy".encode()).unwrap()).unwrap()[0], 50);
}

impl_benchmark_test_suite!(Oracle, crate::mock::new_test_ext(), crate::mock::Test);
}
110 changes: 110 additions & 0 deletions pallets/oracle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (C) 2023 Entropy Cryptography Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! # Programs Oracle
//!
//! ## Overview
//!
//! A pallet to manage oracle data for programs.
//!
//! Oracle data is stored in OracleData storage and can be pointed to and pulled in for programs
//!
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::unused_unit)]

use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;
pub mod weights;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;

pub use module::*;

#[frame_support::pallet]
pub mod module {
use super::*;
pub use crate::weights::WeightInfo;

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// The maximum amount of owned programs.
type MaxOracleKeyLength: Get<u32>;
/// The maximum amount of owned programs.
type MaxOracleValueLength: Get<u32>;
/// Weight information for the extrinsics in this module.
type WeightInfo: WeightInfo;
}

#[pallet::storage]
#[pallet::getter(fn oracle_data)]
pub type OracleData<T: Config> = StorageMap<
_,
Blake2_128Concat,
BoundedVec<u8, T::MaxOracleKeyLength>,
BoundedVec<u8, T::MaxOracleValueLength>,
OptionQuery,
>;

#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
#[serde(skip)]
_config: sp_std::marker::PhantomData<T>,
}

#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
// Makes sure key chosen can fit in bounded vec
assert!("block_number_entropy".encode().len() as u32 <= T::MaxOracleKeyLength::get());
// Makes sure block number can fit in bounded vec
assert!(u64::MAX.encode().len() as u32 <= T::MaxOracleKeyLength::get());
}
}

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(block_number: BlockNumberFor<T>) -> Weight {
OracleData::<T>::insert(
BoundedVec::try_from("block_number_entropy".encode())
.expect("Key fits in bounded vec; qed"),
BoundedVec::try_from(block_number.encode())
.expect("Block number fits in bounded vec; qed"),
);
T::WeightInfo::on_initialize()
}
}

#[pallet::error]
pub enum Error<T> {}

#[pallet::event]
pub enum Event<T: Config> {}

#[pallet::call]
impl<T: Config> Pallet<T> {}
}
Loading

0 comments on commit 5d64794

Please sign in to comment.