From cb61c9052a53f6dae730832d02723da14104ea61 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 14 Oct 2023 02:47:58 -0400 Subject: [PATCH] Reorganize serai-client Instead of functions taking a block hash, has a scope to a block hash before functions can be called. Separates functions by pallets. --- substrate/client/src/serai/coins.rs | 94 +++++ substrate/client/src/serai/in_instructions.rs | 32 +- substrate/client/src/serai/mod.rs | 332 +++++++++--------- substrate/client/src/serai/tokens.rs | 69 ---- substrate/client/src/serai/validator_sets.rs | 78 ++-- substrate/client/tests/batch.rs | 38 +- substrate/client/tests/burn.rs | 23 +- .../client/tests/common/in_instructions.rs | 12 +- substrate/client/tests/common/mod.rs | 2 +- substrate/client/tests/common/tx.rs | 4 +- .../client/tests/common/validator_sets.rs | 22 +- substrate/client/tests/time.rs | 4 +- substrate/client/tests/validator_sets.rs | 39 +- 13 files changed, 384 insertions(+), 365 deletions(-) create mode 100644 substrate/client/src/serai/coins.rs delete mode 100644 substrate/client/src/serai/tokens.rs diff --git a/substrate/client/src/serai/coins.rs b/substrate/client/src/serai/coins.rs new file mode 100644 index 00000000..1d6d255a --- /dev/null +++ b/substrate/client/src/serai/coins.rs @@ -0,0 +1,94 @@ +use sp_core::sr25519::Public; +use serai_runtime::{ + primitives::{SeraiAddress, SubstrateAmount, Amount, Coin, Balance}, + assets::{AssetDetails, AssetAccount}, + tokens, Tokens, Runtime, +}; +pub use tokens::primitives; +use primitives::OutInstruction; + +use subxt::tx::Payload; + +use crate::{TemporalSerai, SeraiError, Composite, scale_value, scale_composite}; + +const PALLET: &str = "Tokens"; + +pub type TokensEvent = tokens::Event; + +#[derive(Clone, Copy)] +pub struct SeraiCoins<'a>(pub(crate) TemporalSerai<'a>); +impl<'a> SeraiCoins<'a> { + pub fn into_inner(self) -> TemporalSerai<'a> { + self.0 + } + + pub async fn mint_events(&self) -> Result, SeraiError> { + self.0.events::(|event| matches!(event, TokensEvent::Mint { .. })).await + } + + pub async fn burn_events(&self) -> Result, SeraiError> { + self.0.events::(|event| matches!(event, TokensEvent::Burn { .. })).await + } + + pub async fn sri_balance(&self, address: SeraiAddress) -> Result { + let data: Option< + serai_runtime::system::AccountInfo>, + > = self.0.storage("System", "Account", Some(vec![scale_value(address)])).await?; + Ok(data.map(|data| data.data.free).unwrap_or(0)) + } + + pub async fn token_supply(&self, coin: Coin) -> Result { + Ok(Amount( + self + .0 + .storage::>( + "Assets", + "Asset", + Some(vec![scale_value(coin)]), + ) + .await? + .map(|token| token.supply) + .unwrap_or(0), + )) + } + + pub async fn token_balance( + &self, + coin: Coin, + address: SeraiAddress, + ) -> Result { + Ok(Amount( + self + .0 + .storage::>( + "Assets", + "Account", + Some(vec![scale_value(coin), scale_value(address)]), + ) + .await? + .map(|account| account.balance()) + .unwrap_or(0), + )) + } + + pub fn transfer_sri(to: SeraiAddress, amount: Amount) -> Payload> { + Payload::new( + "Balances", + // TODO: Use transfer_allow_death? + // TODO: Replace the Balances pallet with something much simpler + "transfer", + scale_composite(serai_runtime::balances::Call::::transfer { + dest: to, + value: amount.0, + }), + ) + } + + pub fn burn(balance: Balance, instruction: OutInstruction) -> Payload> { + Payload::new( + PALLET, + "burn", + scale_composite(tokens::Call::::burn { balance, instruction }), + ) + } +} diff --git a/substrate/client/src/serai/in_instructions.rs b/substrate/client/src/serai/in_instructions.rs index 62e3d74a..ab737fcb 100644 --- a/substrate/client/src/serai/in_instructions.rs +++ b/substrate/client/src/serai/in_instructions.rs @@ -6,42 +6,42 @@ use subxt::utils::Encoded; use crate::{ primitives::{BlockHash, NetworkId}, - SeraiError, Serai, scale_value, + SeraiError, Serai, TemporalSerai, scale_value, }; pub type InInstructionsEvent = in_instructions::Event; const PALLET: &str = "InInstructions"; -impl Serai { - pub async fn get_latest_block_for_network( +#[derive(Clone, Copy)] +pub struct SeraiInInstructions<'a>(pub(crate) TemporalSerai<'a>); +impl<'a> SeraiInInstructions<'a> { + pub fn into_inner(self) -> TemporalSerai<'a> { + self.0 + } + + pub async fn latest_block_for_network( &self, - hash: [u8; 32], network: NetworkId, ) -> Result, SeraiError> { - self.storage(PALLET, "LatestNetworkBlock", Some(vec![scale_value(network)]), hash).await + self.0.storage(PALLET, "LatestNetworkBlock", Some(vec![scale_value(network)])).await } - pub async fn get_last_batch_for_network( + pub async fn last_batch_for_network( &self, - hash: [u8; 32], network: NetworkId, ) -> Result, SeraiError> { - self.storage(PALLET, "LastBatch", Some(vec![scale_value(network)]), hash).await + self.0.storage(PALLET, "LastBatch", Some(vec![scale_value(network)])).await } - pub async fn get_batch_events( - &self, - block: [u8; 32], - ) -> Result, SeraiError> { + pub async fn batch_events(&self) -> Result, SeraiError> { self - .events::(block, |event| { - matches!(event, InInstructionsEvent::Batch { .. }) - }) + .0 + .events::(|event| matches!(event, InInstructionsEvent::Batch { .. })) .await } pub fn execute_batch(batch: SignedBatch) -> Encoded { - Self::unsigned::(&in_instructions::Call::::execute_batch { batch }) + Serai::unsigned::(&in_instructions::Call::::execute_batch { batch }) } } diff --git a/substrate/client/src/serai/mod.rs b/substrate/client/src/serai/mod.rs index 667b4a10..e07651b8 100644 --- a/substrate/client/src/serai/mod.rs +++ b/substrate/client/src/serai/mod.rs @@ -33,9 +33,12 @@ use serai_runtime::{ system::Config, support::traits::PalletInfo as PalletInfoTrait, PalletInfo, Runtime, }; -pub mod tokens; +pub mod coins; +pub use coins::SeraiCoins; pub mod in_instructions; +pub use in_instructions::SeraiInInstructions; pub mod validator_sets; +pub use validator_sets::SeraiValidatorSets; #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Encode, Decode)] pub struct Tip { @@ -136,153 +139,14 @@ pub enum SeraiError { #[derive(Clone)] pub struct Serai(OnlineClient); +#[derive(Clone, Copy)] +pub struct TemporalSerai<'a>(pub(crate) &'a Serai, pub(crate) [u8; 32]); + impl Serai { pub async fn new(url: &str) -> Result { Ok(Serai(OnlineClient::::from_url(url).await.map_err(SeraiError::RpcError)?)) } - async fn storage( - &self, - pallet: &'static str, - name: &'static str, - keys: Option>, - block: [u8; 32], - ) -> Result, SeraiError> { - let storage = self.0.storage(); - #[allow(clippy::unwrap_or_default)] - let address = subxt::dynamic::storage(pallet, name, keys.unwrap_or(vec![])); - debug_assert!(storage.validate(&address).is_ok(), "invalid storage address"); - - storage - .at(block.into()) - .fetch(&address) - .await - .map_err(SeraiError::RpcError)? - .map(|res| R::decode(&mut res.encoded()).map_err(|_| SeraiError::InvalidRuntime)) - .transpose() - } - - async fn events( - &self, - block: [u8; 32], - filter: impl Fn(&E) -> bool, - ) -> Result, SeraiError> { - let mut res = vec![]; - for event in self.0.events().at(block.into()).await.map_err(SeraiError::RpcError)?.iter() { - let event = event.map_err(|_| SeraiError::InvalidRuntime)?; - if PalletInfo::index::

().unwrap() == usize::from(event.pallet_index()) { - let mut with_variant: &[u8] = - &[[event.variant_index()].as_ref(), event.field_bytes()].concat(); - let event = E::decode(&mut with_variant).map_err(|_| SeraiError::InvalidRuntime)?; - if filter(&event) { - res.push(event); - } - } - } - Ok(res) - } - - pub async fn get_latest_block_hash(&self) -> Result<[u8; 32], SeraiError> { - Ok(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?.into()) - } - - pub async fn get_latest_block(&self) -> Result { - Block::new( - self - .0 - .rpc() - .block(Some(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?)) - .await - .map_err(SeraiError::RpcError)? - .ok_or(SeraiError::InvalidNode)? - .block, - ) - } - - // There is no provided method for this - // TODO: Add one to Serai - pub async fn is_finalized(&self, header: &Header) -> Result, SeraiError> { - // Get the latest finalized block - let finalized = self.get_latest_block_hash().await?.into(); - // If the latest finalized block is this block, return true - if finalized == header.hash() { - return Ok(Some(true)); - } - - let Some(finalized) = - self.0.rpc().header(Some(finalized)).await.map_err(SeraiError::RpcError)? - else { - return Ok(None); - }; - - // If the finalized block has a lower number, this block can't be finalized - if finalized.number() < header.number() { - return Ok(Some(false)); - } - - // This block, if finalized, comes before the finalized block - // If we request the hash of this block's number, Substrate will return the hash on the main - // chain - // If that hash is this hash, this block is finalized - let Some(hash) = - self.0.rpc().block_hash(Some(header.number().into())).await.map_err(SeraiError::RpcError)? - else { - // This is an error since there is a block at this index - Err(SeraiError::InvalidNode)? - }; - - Ok(Some(header.hash() == hash)) - } - - pub async fn get_block(&self, hash: [u8; 32]) -> Result, SeraiError> { - let Some(res) = self.0.rpc().block(Some(hash.into())).await.map_err(SeraiError::RpcError)? - else { - return Ok(None); - }; - - // Only return finalized blocks - if self.is_finalized(&res.block.header).await? != Some(true) { - return Ok(None); - } - - Ok(Some(Block::new(res.block)?)) - } - - // Ideally, this would be get_block_hash, not get_block_by_number - // Unfortunately, in order to only operate over only finalized data, we have to check the - // returned hash is for a finalized block. We can only do that by calling the extensive - // is_finalized method, which at least requires the header - // In practice, the block is likely more useful than the header - pub async fn get_block_by_number(&self, number: u64) -> Result, SeraiError> { - let Some(hash) = - self.0.rpc().block_hash(Some(number.into())).await.map_err(SeraiError::RpcError)? - else { - return Ok(None); - }; - self.get_block(hash.into()).await - } - - /// A stream which yields whenever new block(s) have been finalized. - pub async fn newly_finalized_block( - &self, - ) -> Result>, SeraiError> { - Ok(self.0.rpc().subscribe_finalized_block_headers().await.map_err(SeraiError::RpcError)?.map( - |next| { - next.map_err(SeraiError::RpcError)?; - Ok(()) - }, - )) - } - - pub async fn get_nonce(&self, address: &SeraiAddress) -> Result { - self - .0 - .rpc() - .system_account_next_index(&sp_core::sr25519::Public(address.0).to_string()) - .await - .map_err(SeraiError::RpcError) - } - fn unsigned(call: &C) -> Encoded { // TODO: Should Serai purge the old transaction code AND set this to 0/1? const TRANSACTION_VERSION: u8 = 4; @@ -322,29 +186,173 @@ impl Serai { self.0.rpc().submit_extrinsic(tx).await.map(|_| ()).map_err(SeraiError::RpcError) } - pub async fn get_sri_balance( - &self, - block: [u8; 32], - address: SeraiAddress, - ) -> Result { - let data: Option< - serai_runtime::system::AccountInfo>, - > = self.storage("System", "Account", Some(vec![scale_value(address)]), block).await?; - Ok(data.map(|data| data.data.free).unwrap_or(0)) + pub async fn latest_block_hash(&self) -> Result<[u8; 32], SeraiError> { + Ok(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?.into()) } - pub fn transfer_sri(to: SeraiAddress, amount: Amount) -> Payload> { - Payload::new( - "Balances", - // TODO: Use transfer_allow_death? - // TODO: Replace the Balances pallet with something much simpler - "transfer", - scale_composite(serai_runtime::balances::Call::::transfer { - dest: to, - value: amount.0, - }), + pub async fn latest_block(&self) -> Result { + Block::new( + self + .0 + .rpc() + .block(Some(self.0.rpc().finalized_head().await.map_err(SeraiError::RpcError)?)) + .await + .map_err(SeraiError::RpcError)? + .ok_or(SeraiError::InvalidNode)? + .block, ) } + + // There is no provided method for this + // TODO: Add one to Serai + pub async fn is_finalized(&self, header: &Header) -> Result, SeraiError> { + // Get the latest finalized block + let finalized = self.latest_block_hash().await?.into(); + // If the latest finalized block is this block, return true + if finalized == header.hash() { + return Ok(Some(true)); + } + + let Some(finalized) = + self.0.rpc().header(Some(finalized)).await.map_err(SeraiError::RpcError)? + else { + return Ok(None); + }; + + // If the finalized block has a lower number, this block can't be finalized + if finalized.number() < header.number() { + return Ok(Some(false)); + } + + // This block, if finalized, comes before the finalized block + // If we request the hash of this block's number, Substrate will return the hash on the main + // chain + // If that hash is this hash, this block is finalized + let Some(hash) = + self.0.rpc().block_hash(Some(header.number().into())).await.map_err(SeraiError::RpcError)? + else { + // This is an error since there is a block at this index + Err(SeraiError::InvalidNode)? + }; + + Ok(Some(header.hash() == hash)) + } + + pub async fn block(&self, hash: [u8; 32]) -> Result, SeraiError> { + let Some(res) = self.0.rpc().block(Some(hash.into())).await.map_err(SeraiError::RpcError)? + else { + return Ok(None); + }; + + // Only return finalized blocks + if self.is_finalized(&res.block.header).await? != Some(true) { + return Ok(None); + } + + Ok(Some(Block::new(res.block)?)) + } + + // Ideally, this would be block_hash, not block_by_number + // Unfortunately, in order to only operate over only finalized data, we have to check the + // returned hash is for a finalized block. We can only do that by calling the extensive + // is_finalized method, which at least requires the header + // In practice, the block is likely more useful than the header + pub async fn block_by_number(&self, number: u64) -> Result, SeraiError> { + let Some(hash) = + self.0.rpc().block_hash(Some(number.into())).await.map_err(SeraiError::RpcError)? + else { + return Ok(None); + }; + self.block(hash.into()).await + } + + /// A stream which yields whenever new block(s) have been finalized. + pub async fn newly_finalized_block( + &self, + ) -> Result>, SeraiError> { + Ok(self.0.rpc().subscribe_finalized_block_headers().await.map_err(SeraiError::RpcError)?.map( + |next| { + next.map_err(SeraiError::RpcError)?; + Ok(()) + }, + )) + } + + pub async fn nonce(&self, address: &SeraiAddress) -> Result { + self + .0 + .rpc() + .system_account_next_index(&sp_core::sr25519::Public(address.0).to_string()) + .await + .map_err(SeraiError::RpcError) + } + + pub async fn with_current_latest_block(&self) -> Result { + let latest = self.latest_block_hash().await?; + Ok(TemporalSerai(self, latest)) + } + + /// Returns a TemporalSerai able to retrieve state as of the specified block. + pub fn as_of(&self, block: [u8; 32]) -> TemporalSerai { + TemporalSerai(self, block) + } +} + +impl<'a> TemporalSerai<'a> { + pub fn into_inner(&self) -> &Serai { + self.0 + } + + async fn events( + &self, + filter: impl Fn(&E) -> bool, + ) -> Result, SeraiError> { + let mut res = vec![]; + for event in self.0 .0.events().at(self.1.into()).await.map_err(SeraiError::RpcError)?.iter() { + let event = event.map_err(|_| SeraiError::InvalidRuntime)?; + if PalletInfo::index::

().unwrap() == usize::from(event.pallet_index()) { + let mut with_variant: &[u8] = + &[[event.variant_index()].as_ref(), event.field_bytes()].concat(); + let event = E::decode(&mut with_variant).map_err(|_| SeraiError::InvalidRuntime)?; + if filter(&event) { + res.push(event); + } + } + } + Ok(res) + } + + async fn storage( + &self, + pallet: &'static str, + name: &'static str, + keys: Option>, + ) -> Result, SeraiError> { + let storage = self.0 .0.storage(); + #[allow(clippy::unwrap_or_default)] + let address = subxt::dynamic::storage(pallet, name, keys.unwrap_or(vec![])); + debug_assert!(storage.validate(&address).is_ok(), "invalid storage address"); + + storage + .at(self.1.into()) + .fetch(&address) + .await + .map_err(SeraiError::RpcError)? + .map(|res| R::decode(&mut res.encoded()).map_err(|_| SeraiError::InvalidRuntime)) + .transpose() + } + + pub fn coins(self) -> SeraiCoins<'a> { + SeraiCoins(self) + } + + pub fn in_instructions(self) -> SeraiInInstructions<'a> { + SeraiInInstructions(self) + } + + pub fn validator_sets(self) -> SeraiValidatorSets<'a> { + SeraiValidatorSets(self) + } } #[derive(Clone)] diff --git a/substrate/client/src/serai/tokens.rs b/substrate/client/src/serai/tokens.rs deleted file mode 100644 index 0f84ed01..00000000 --- a/substrate/client/src/serai/tokens.rs +++ /dev/null @@ -1,69 +0,0 @@ -use sp_core::sr25519::Public; -use serai_runtime::{ - primitives::{SeraiAddress, SubstrateAmount, Amount, Coin, Balance}, - assets::{AssetDetails, AssetAccount}, - tokens, Tokens, Runtime, -}; -pub use tokens::primitives; -use primitives::OutInstruction; - -use subxt::tx::Payload; - -use crate::{Serai, SeraiError, Composite, scale_value, scale_composite}; - -const PALLET: &str = "Tokens"; - -pub type TokensEvent = tokens::Event; - -impl Serai { - pub async fn get_mint_events(&self, block: [u8; 32]) -> Result, SeraiError> { - self.events::(block, |event| matches!(event, TokensEvent::Mint { .. })).await - } - - pub async fn get_token_supply(&self, block: [u8; 32], coin: Coin) -> Result { - Ok(Amount( - self - .storage::>( - "Assets", - "Asset", - Some(vec![scale_value(coin)]), - block, - ) - .await? - .map(|token| token.supply) - .unwrap_or(0), - )) - } - - pub async fn get_token_balance( - &self, - block: [u8; 32], - coin: Coin, - address: SeraiAddress, - ) -> Result { - Ok(Amount( - self - .storage::>( - "Assets", - "Account", - Some(vec![scale_value(coin), scale_value(address)]), - block, - ) - .await? - .map(|account| account.balance()) - .unwrap_or(0), - )) - } - - pub fn burn(balance: Balance, instruction: OutInstruction) -> Payload> { - Payload::new( - PALLET, - "burn", - scale_composite(tokens::Call::::burn { balance, instruction }), - ) - } - - pub async fn get_burn_events(&self, block: [u8; 32]) -> Result, SeraiError> { - self.events::(block, |event| matches!(event, TokensEvent::Burn { .. })).await - } -} diff --git a/substrate/client/src/serai/validator_sets.rs b/substrate/client/src/serai/validator_sets.rs index a1920960..8555c090 100644 --- a/substrate/client/src/serai/validator_sets.rs +++ b/substrate/client/src/serai/validator_sets.rs @@ -6,89 +6,67 @@ use primitives::{Session, ValidatorSet, KeyPair}; use subxt::utils::Encoded; -use crate::{primitives::NetworkId, Serai, SeraiError, scale_value}; +use crate::{primitives::NetworkId, Serai, TemporalSerai, SeraiError, scale_value}; const PALLET: &str = "ValidatorSets"; pub type ValidatorSetsEvent = validator_sets::Event; -impl Serai { - pub async fn get_new_set_events( - &self, - block: [u8; 32], - ) -> Result, SeraiError> { +#[derive(Clone, Copy)] +pub struct SeraiValidatorSets<'a>(pub(crate) TemporalSerai<'a>); +impl<'a> SeraiValidatorSets<'a> { + pub fn into_inner(self) -> TemporalSerai<'a> { + self.0 + } + + pub async fn new_set_events(&self) -> Result, SeraiError> { self - .events::(block, |event| matches!(event, ValidatorSetsEvent::NewSet { .. })) + .0 + .events::(|event| matches!(event, ValidatorSetsEvent::NewSet { .. })) .await } - pub async fn get_key_gen_events( - &self, - block: [u8; 32], - ) -> Result, SeraiError> { + pub async fn key_gen_events(&self) -> Result, SeraiError> { self - .events::(block, |event| matches!(event, ValidatorSetsEvent::KeyGen { .. })) + .0 + .events::(|event| matches!(event, ValidatorSetsEvent::KeyGen { .. })) .await } - pub async fn get_session( - &self, - network: NetworkId, - at_hash: [u8; 32], - ) -> Result, SeraiError> { - self.storage(PALLET, "CurrentSession", Some(vec![scale_value(network)]), at_hash).await + pub async fn session(&self, network: NetworkId) -> Result, SeraiError> { + self.0.storage(PALLET, "CurrentSession", Some(vec![scale_value(network)])).await } - pub async fn get_validator_set_participants( - &self, - network: NetworkId, - at_hash: [u8; 32], - ) -> Result>, SeraiError> { - self.storage(PALLET, "Participants", Some(vec![scale_value(network)]), at_hash).await + pub async fn participants(&self, network: NetworkId) -> Result>, SeraiError> { + self.0.storage(PALLET, "Participants", Some(vec![scale_value(network)])).await } - pub async fn get_allocation_per_key_share( + pub async fn allocation_per_key_share( &self, network: NetworkId, - at_hash: [u8; 32], ) -> Result, SeraiError> { - self.storage(PALLET, "AllocationPerKeyShare", Some(vec![scale_value(network)]), at_hash).await + self.0.storage(PALLET, "AllocationPerKeyShare", Some(vec![scale_value(network)])).await } - pub async fn get_allocation( + pub async fn allocation( &self, network: NetworkId, key: Public, - at_hash: [u8; 32], ) -> Result, SeraiError> { - self - .storage(PALLET, "Allocations", Some(vec![scale_value(network), scale_value(key)]), at_hash) - .await + self.0.storage(PALLET, "Allocations", Some(vec![scale_value(network), scale_value(key)])).await } - pub async fn get_validator_set_musig_key( - &self, - set: ValidatorSet, - at_hash: [u8; 32], - ) -> Result, SeraiError> { - self.storage(PALLET, "MuSigKeys", Some(vec![scale_value(set)]), at_hash).await + pub async fn musig_key(&self, set: ValidatorSet) -> Result, SeraiError> { + self.0.storage(PALLET, "MuSigKeys", Some(vec![scale_value(set)])).await } // TODO: Store these separately since we almost never need both at once? - pub async fn get_keys( - &self, - set: ValidatorSet, - at_hash: [u8; 32], - ) -> Result, SeraiError> { - self.storage(PALLET, "Keys", Some(vec![scale_value(set)]), at_hash).await + pub async fn keys(&self, set: ValidatorSet) -> Result, SeraiError> { + self.0.storage(PALLET, "Keys", Some(vec![scale_value(set)])).await } - pub fn set_validator_set_keys( - network: NetworkId, - key_pair: KeyPair, - signature: Signature, - ) -> Encoded { - Self::unsigned::(&validator_sets::Call::::set_keys { + pub fn set_keys(network: NetworkId, key_pair: KeyPair, signature: Signature) -> Encoded { + Serai::unsigned::(&validator_sets::Call::::set_keys { network, key_pair, signature, diff --git a/substrate/client/tests/batch.rs b/substrate/client/tests/batch.rs index 9fef6bed..2b747747 100644 --- a/substrate/client/tests/batch.rs +++ b/substrate/client/tests/batch.rs @@ -13,7 +13,7 @@ use serai_client::{ primitives::{InInstruction, InInstructionWithBalance, Batch}, InInstructionsEvent, }, - tokens::TokensEvent, + coins::TokensEvent, Serai, }; @@ -48,23 +48,25 @@ serai_test!( let block = provide_batch(batch.clone()).await; let serai = serai().await; - assert_eq!(serai.get_latest_block_for_network(block, network).await.unwrap(), Some(block_hash)); - let batches = serai.get_batch_events(block).await.unwrap(); - assert_eq!( - batches, - vec![InInstructionsEvent::Batch { - network, - id, - block: block_hash, - instructions_hash: Blake2b::::digest(batch.instructions.encode()).into(), - }] - ); + let serai = serai.as_of(block); + { + let serai = serai.in_instructions(); + assert_eq!(serai.latest_block_for_network(network).await.unwrap(), Some(block_hash)); + let batches = serai.batch_events().await.unwrap(); + assert_eq!( + batches, + vec![InInstructionsEvent::Batch { + network, + id, + block: block_hash, + instructions_hash: Blake2b::::digest(batch.instructions.encode()).into(), + }] + ); + } - assert_eq!( - serai.get_mint_events(block).await.unwrap(), - vec![TokensEvent::Mint { address, balance }], - ); - assert_eq!(serai.get_token_supply(block, coin).await.unwrap(), amount); - assert_eq!(serai.get_token_balance(block, coin, address).await.unwrap(), amount); + let serai = serai.coins(); + assert_eq!(serai.mint_events().await.unwrap(), vec![TokensEvent::Mint { address, balance }],); + assert_eq!(serai.token_supply(coin).await.unwrap(), amount); + assert_eq!(serai.token_balance(coin, address).await.unwrap(), amount); } ); diff --git a/substrate/client/tests/burn.rs b/substrate/client/tests/burn.rs index 8b7f5cc8..2a03e566 100644 --- a/substrate/client/tests/burn.rs +++ b/substrate/client/tests/burn.rs @@ -19,8 +19,8 @@ use serai_client::{ InInstructionsEvent, primitives::{InInstruction, InInstructionWithBalance, Batch}, }, - tokens::{primitives::OutInstruction, TokensEvent}, - PairSigner, Serai, + coins::{primitives::OutInstruction, TokensEvent}, + PairSigner, Serai, SeraiCoins, }; mod common; @@ -55,7 +55,8 @@ serai_test!( let block = provide_batch(batch.clone()).await; let serai = serai().await; - let batches = serai.get_batch_events(block).await.unwrap(); + let serai = serai.as_of(block); + let batches = serai.in_instructions().batch_events().await.unwrap(); assert_eq!( batches, vec![InInstructionsEvent::Batch { @@ -67,11 +68,11 @@ serai_test!( ); assert_eq!( - serai.get_mint_events(block).await.unwrap(), + serai.coins().mint_events().await.unwrap(), vec![TokensEvent::Mint { address, balance }] ); - assert_eq!(serai.get_token_supply(block, coin).await.unwrap(), amount); - assert_eq!(serai.get_token_balance(block, coin, address).await.unwrap(), amount); + assert_eq!(serai.coins().token_supply(coin).await.unwrap(), amount); + assert_eq!(serai.coins().token_balance(coin, address).await.unwrap(), amount); // Now burn it let mut rand_bytes = vec![0; 32]; @@ -83,11 +84,12 @@ serai_test!( let data = Data::new(rand_bytes).unwrap(); let out = OutInstruction { address: external_address, data: Some(data) }; + let serai = serai.into_inner(); let block = publish_tx( &serai .sign( &PairSigner::new(pair), - &Serai::burn(balance, out.clone()), + &SeraiCoins::burn(balance, out.clone()), 0, BaseExtrinsicParamsBuilder::new(), ) @@ -95,9 +97,10 @@ serai_test!( ) .await; - let events = serai.get_burn_events(block).await.unwrap(); + let serai = serai.as_of(block).coins(); + let events = serai.burn_events().await.unwrap(); assert_eq!(events, vec![TokensEvent::Burn { address, balance, instruction: out }]); - assert_eq!(serai.get_token_supply(block, coin).await.unwrap(), Amount(0)); - assert_eq!(serai.get_token_balance(block, coin, address).await.unwrap(), Amount(0)); + assert_eq!(serai.token_supply(coin).await.unwrap(), Amount(0)); + assert_eq!(serai.token_balance(coin, address).await.unwrap(), Amount(0)); } ); diff --git a/substrate/client/tests/common/in_instructions.rs b/substrate/client/tests/common/in_instructions.rs index 45f903e8..8e26bf8f 100644 --- a/substrate/client/tests/common/in_instructions.rs +++ b/substrate/client/tests/common/in_instructions.rs @@ -14,10 +14,10 @@ use serai_client::{ primitives::{Batch, SignedBatch, batch_message}, InInstructionsEvent, }, - Serai, + SeraiInInstructions, }; -use crate::common::{serai, tx::publish_tx, validator_sets::set_validator_set_keys}; +use crate::common::{serai, tx::publish_tx, validator_sets::set_keys}; #[allow(dead_code)] pub async fn provide_batch(batch: Batch) -> [u8; 32] { @@ -27,23 +27,23 @@ pub async fn provide_batch(batch: Batch) -> [u8; 32] { let set = ValidatorSet { session: Session(0), network: batch.network }; let pair = insecure_pair_from_name(&format!("ValidatorSet {:?}", set)); let keys = if let Some(keys) = - serai.get_keys(set, serai.get_latest_block_hash().await.unwrap()).await.unwrap() + serai.with_current_latest_block().await.unwrap().validator_sets().keys(set).await.unwrap() { keys } else { let keys = (pair.public(), vec![].try_into().unwrap()); - set_validator_set_keys(set, keys.clone()).await; + set_keys(set, keys.clone()).await; keys }; assert_eq!(keys.0, pair.public()); - let block = publish_tx(&Serai::execute_batch(SignedBatch { + let block = publish_tx(&SeraiInInstructions::execute_batch(SignedBatch { batch: batch.clone(), signature: pair.sign(&batch_message(&batch)), })) .await; - let batches = serai.get_batch_events(block).await.unwrap(); + let batches = serai.as_of(block).in_instructions().batch_events().await.unwrap(); // TODO: impl From for BatchEvent? assert_eq!( batches, diff --git a/substrate/client/tests/common/mod.rs b/substrate/client/tests/common/mod.rs index 57e30516..13cf06c8 100644 --- a/substrate/client/tests/common/mod.rs +++ b/substrate/client/tests/common/mod.rs @@ -60,7 +60,7 @@ macro_rules! serai_test { tokio::time::sleep(Duration::from_secs(1)).await; } let serai = serai().await; - while serai.get_latest_block_hash().await.is_err() { + while serai.latest_block_hash().await.is_err() { tokio::time::sleep(Duration::from_secs(1)).await; } // TODO: https://github.com/serai-dex/serai/247 diff --git a/substrate/client/tests/common/tx.rs b/substrate/client/tests/common/tx.rs index 43925096..33ab79d2 100644 --- a/substrate/client/tests/common/tx.rs +++ b/substrate/client/tests/common/tx.rs @@ -11,7 +11,7 @@ pub async fn publish_tx(tx: &Encoded) -> [u8; 32] { let serai = serai().await; let mut latest = - serai.get_block(serai.get_latest_block_hash().await.unwrap()).await.unwrap().unwrap().number(); + serai.block(serai.latest_block_hash().await.unwrap()).await.unwrap().unwrap().number(); serai.publish(tx).await.unwrap(); @@ -24,7 +24,7 @@ pub async fn publish_tx(tx: &Encoded) -> [u8; 32] { let block = { let mut block; while { - block = serai.get_block_by_number(latest).await.unwrap(); + block = serai.block_by_number(latest).await.unwrap(); block.is_none() } { sleep(Duration::from_secs(1)).await; diff --git a/substrate/client/tests/common/validator_sets.rs b/substrate/client/tests/common/validator_sets.rs index 89cf18eb..78f08d2d 100644 --- a/substrate/client/tests/common/validator_sets.rs +++ b/substrate/client/tests/common/validator_sets.rs @@ -15,13 +15,13 @@ use serai_client::{ primitives::{ValidatorSet, KeyPair, musig_context, musig_key, set_keys_message}, ValidatorSetsEvent, }, - Serai, + SeraiValidatorSets, }; use crate::common::{serai, tx::publish_tx}; #[allow(dead_code)] -pub async fn set_validator_set_keys(set: ValidatorSet, key_pair: KeyPair) -> [u8; 32] { +pub async fn set_keys(set: ValidatorSet, key_pair: KeyPair) -> [u8; 32] { let pair = insecure_pair_from_name("Alice"); let public = pair.public(); @@ -29,7 +29,11 @@ pub async fn set_validator_set_keys(set: ValidatorSet, key_pair: KeyPair) -> [u8 let public_key = ::read_G::<&[u8]>(&mut public.0.as_ref()).unwrap(); assert_eq!( serai - .get_validator_set_musig_key(set, serai.get_latest_block_hash().await.unwrap()) + .with_current_latest_block() + .await + .unwrap() + .validator_sets() + .musig_key(set) .await .unwrap() .unwrap(), @@ -45,7 +49,11 @@ pub async fn set_validator_set_keys(set: ValidatorSet, key_pair: KeyPair) -> [u8 musig::(&musig_context(set), &Zeroizing::new(secret_key), &[public_key]).unwrap(); assert_eq!( serai - .get_validator_set_musig_key(set, serai.get_latest_block_hash().await.unwrap()) + .with_current_latest_block() + .await + .unwrap() + .validator_sets() + .musig_key(set) .await .unwrap() .unwrap(), @@ -63,7 +71,7 @@ pub async fn set_validator_set_keys(set: ValidatorSet, key_pair: KeyPair) -> [u8 ); // Vote in a key pair - let block = publish_tx(&Serai::set_validator_set_keys( + let block = publish_tx(&SeraiValidatorSets::set_keys( set.network, key_pair.clone(), Signature(sig.to_bytes()), @@ -71,10 +79,10 @@ pub async fn set_validator_set_keys(set: ValidatorSet, key_pair: KeyPair) -> [u8 .await; assert_eq!( - serai.get_key_gen_events(block).await.unwrap(), + serai.as_of(block).validator_sets().key_gen_events().await.unwrap(), vec![ValidatorSetsEvent::KeyGen { set, key_pair: key_pair.clone() }] ); - assert_eq!(serai.get_keys(set, block).await.unwrap(), Some(key_pair)); + assert_eq!(serai.as_of(block).validator_sets().keys(set).await.unwrap(), Some(key_pair)); block } diff --git a/substrate/client/tests/time.rs b/substrate/client/tests/time.rs index 5eac481d..78ff6f87 100644 --- a/substrate/client/tests/time.rs +++ b/substrate/client/tests/time.rs @@ -11,11 +11,11 @@ serai_test!( async fn time() { let serai = serai().await; - let mut number = serai.get_latest_block().await.unwrap().number(); + let mut number = serai.latest_block().await.unwrap().number(); let mut done = 0; while done < 3 { // Wait for the next block - let block = serai.get_latest_block().await.unwrap(); + let block = serai.latest_block().await.unwrap(); if block.number() == number { sleep(Duration::from_secs(1)).await; continue; diff --git a/substrate/client/tests/validator_sets.rs b/substrate/client/tests/validator_sets.rs index 2a5c9068..de9168b9 100644 --- a/substrate/client/tests/validator_sets.rs +++ b/substrate/client/tests/validator_sets.rs @@ -12,10 +12,10 @@ use serai_client::{ }; mod common; -use common::{serai, validator_sets::set_validator_set_keys}; +use common::{serai, validator_sets::set_keys}; serai_test!( - async fn set_validator_set_keys_test() { + async fn set_keys_test() { let network = NetworkId::Bitcoin; let set = ValidatorSet { session: Session(0), network }; @@ -35,7 +35,9 @@ serai_test!( // Make sure the genesis is as expected assert_eq!( serai - .get_new_set_events(serai.get_block_by_number(0).await.unwrap().unwrap().hash()) + .as_of(serai.block_by_number(0).await.unwrap().unwrap().hash()) + .validator_sets() + .new_set_events() .await .unwrap(), NETWORKS @@ -47,30 +49,23 @@ serai_test!( .collect::>(), ); - let participants = serai - .get_validator_set_participants(set.network, serai.get_latest_block_hash().await.unwrap()) - .await - .unwrap() - .unwrap(); - let participants_ref: &[_] = participants.as_ref(); - assert_eq!(participants_ref, [public].as_ref()); - assert_eq!( - serai - .get_validator_set_musig_key(set, serai.get_latest_block_hash().await.unwrap()) - .await - .unwrap() - .unwrap(), - musig_key(set, &[public]).0 - ); + { + let vs_serai = serai.with_current_latest_block().await.unwrap().validator_sets(); + let participants = vs_serai.participants(set.network).await.unwrap().unwrap(); + let participants_ref: &[_] = participants.as_ref(); + assert_eq!(participants_ref, [public].as_ref()); + assert_eq!(vs_serai.musig_key(set).await.unwrap().unwrap(), musig_key(set, &[public]).0); + } - let block = set_validator_set_keys(set, key_pair.clone()).await; + let block = set_keys(set, key_pair.clone()).await; - // While the set_validator_set_keys function should handle this, it's beneficial to + // While the set_keys function should handle this, it's beneficial to // independently test it + let serai = serai.as_of(block).validator_sets(); assert_eq!( - serai.get_key_gen_events(block).await.unwrap(), + serai.key_gen_events().await.unwrap(), vec![ValidatorSetsEvent::KeyGen { set, key_pair: key_pair.clone() }] ); - assert_eq!(serai.get_keys(set, block).await.unwrap(), Some(key_pair)); + assert_eq!(serai.keys(set).await.unwrap(), Some(key_pair)); } );