Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oracle data integration #922

Merged
merged 14 commits into from
Aug 15, 2024
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
Loading