7 Commits

Author SHA1 Message Date
Luke Parker
46b1f1b7ec Add test for the integrity of headers 2025-11-14 12:04:21 -05:00
Luke Parker
09113201e7 Fixes to the validator sets RPC 2025-11-14 11:19:02 -05:00
Luke Parker
556d294157 Add pallet-timestamp
Ensures the timestamp is sent, within expected parameters, and the correctness
in relation to `pallet-babe`.
2025-11-14 09:59:32 -05:00
Luke Parker
82ca889ed3 Wrap Proposer so we can add the SeraiPreExecutionDigest (timestamps) 2025-11-14 08:02:54 -05:00
Luke Parker
cde0f753c2 Correct Serai header on genesis
Includes a couple misc fixes for the RPC as well.
2025-11-14 07:22:59 -05:00
Luke Parker
6ff0ef7aa6 Type the errors yielded by serai-node's RPC 2025-11-14 03:52:35 -05:00
Luke Parker
f9e3d1b142 Expand validator sets API with the rest of the events and some getters
We could've added a storage API, and fetched fields that way, except we want
the storage to be opaque. That meant we needed to add the RPC routes to the
node, which also simplifies other people writing RPC code and fetching these
fields. Then the node could've used the storage API, except a lot of the
storage in validator-sets is marked opaque and to only be read via functions,
so extending the runtime made the most sense.
2025-11-14 03:37:06 -05:00
38 changed files with 991 additions and 304 deletions

View File

@@ -6,7 +6,7 @@ license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/networks/bitcoin"
authors = ["Luke Parker <lukeparker5132@gmail.com>", "Vrx <vrx00@proton.me>"]
edition = "2021"
rust-version = "1.85"
rust-version = "1.89"
[package.metadata.docs.rs]
all-features = true

View File

@@ -155,6 +155,7 @@ impl Rpc {
Err(RpcError::RequestError(Error { code, message }))
}
// `invalidateblock` yields this edge case
// TODO: https://github.com/core-json/core-json/issues/18
RpcResponse { result: None, error: None } => {
if core::any::TypeId::of::<Response>() == core::any::TypeId::of::<()>() {
Ok(Default::default())

View File

@@ -22,12 +22,12 @@ workspace = true
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
bitvec = { version = "1", default-features = false, features = ["alloc"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false, features = ["serde"], optional = true }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false, optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, features = ["serde"], optional = true }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, optional = true }
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }

View File

@@ -7,15 +7,26 @@ use crate::{
Transaction,
};
/// The tag for a block's header, forming a leaf of the Merkle tree which is `builds_upon`.
pub const BLOCK_HEADER_LEAF_TAG: u8 = 0;
/// The tag for branch hashes in `builds_upon`.
pub const BLOCK_HEADER_BRANCH_TAG: u8 = 1;
/// The tag for a transaction, forming a leaf of the Merkle tree which is the transactions'
/// commitment.
pub const TRANSACTION_COMMITMENT_LEAF_TAG: u8 = 2;
/// The tag for branch hashes in the transactions' commitment.
pub const TRANSACTION_COMMITMENT_BRANCH_TAG: u8 = 3;
/// The tag for the hash of a transaction's event, forming a leaf of the Merkle tree of its events.
pub const TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG: u8 = 0;
/// The tag for the branch hashes of transaction events.
pub const TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG: u8 = 1;
pub const TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG: u8 = 4;
/// The tag for the branch hashes of the Merkle tree for a transaction's events.
pub const TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG: u8 = 5;
/// The tag for the hash of a transaction's hash and its events' Merkle root, forming a leaf of the
/// Merkle tree which is the events commitment.
pub const EVENTS_COMMITMENT_LEAF_TAG: u8 = 2;
/// The tag for for the branch hashes of the Merkle tree which is the events commitments.
pub const EVENTS_COMMITMENT_BRANCH_TAG: u8 = 3;
/// Merkle tree which is the events' commitment.
pub const EVENTS_COMMITMENT_LEAF_TAG: u8 = 6;
/// The tag for branch hashes in the events' commitment.
pub const EVENTS_COMMITMENT_BRANCH_TAG: u8 = 7;
/// A V1 header for a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
@@ -38,7 +49,8 @@ pub struct HeaderV1 {
pub builds_upon: UnbalancedMerkleTree,
/// The UNIX time in milliseconds this block was created at.
pub unix_time_in_millis: u64,
/// The commitment to the transactions within this block.
/// The commitment to the transactions within this block, including the inherent start/end of
/// block transactions.
pub transactions_commitment: UnbalancedMerkleTree,
/// The commitment to the events within this block.
///
@@ -147,6 +159,29 @@ mod substrate {
pub unix_time_in_millis: u64,
}
impl SeraiPreExecutionDigest {
/// The consensus ID for a Serai pre-execution digest.
pub const CONSENSUS_ID: [u8; 4] = *b"SRIP";
/// Find the pre-execution digest within a `Digest`.
///
/// This will panic if the digest either isn't found or is invalid.
pub fn find(digest: &Digest) -> Self {
for log in digest.logs() {
match log {
DigestItem::PreRuntime(consensus, encoded)
if *consensus == SeraiPreExecutionDigest::CONSENSUS_ID =>
{
return <_>::deserialize_reader(&mut encoded.as_slice())
.expect("invalid `SeraiPreExecutionDigest`");
}
_ => {}
}
}
panic!("missing `SeraiPreExecutionDigest`");
}
}
/// The digest for all of the Serai-specific header fields determined during execution of the
/// block.
#[derive(Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
@@ -159,11 +194,6 @@ mod substrate {
pub events_commitment: UnbalancedMerkleTree,
}
impl SeraiPreExecutionDigest {
/// The consensus ID for a Serai pre-execution digest.
pub const CONSENSUS_ID: [u8; 4] = *b"SRIP";
}
impl SeraiExecutionDigest {
/// The consensus ID for a Serai execution digest.
pub const CONSENSUS_ID: [u8; 4] = *b"SRIE";
@@ -241,7 +271,7 @@ mod substrate {
for log in header.consensus.digest.logs() {
match log {
DigestItem::PreRuntime(consensus, encoded)
if *consensus == SeraiExecutionDigest::CONSENSUS_ID =>
if *consensus == SeraiPreExecutionDigest::CONSENSUS_ID =>
{
pre_execution_digest =
SeraiPreExecutionDigest::deserialize_reader(&mut encoded.as_slice()).ok();

View File

@@ -31,9 +31,9 @@ serde_json = { version = "1", optional = true }
serai-abi = { path = "../abi", version = "0.1" }
multiaddr = { version = "0.18", optional = true }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", optional = true }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", optional = true }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", optional = true }
async-lock = "3"

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/substrate/client/s
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["serai"]
edition = "2021"
rust-version = "1.85"
rust-version = "1.89"
[package.metadata.docs.rs]
all-features = true
@@ -29,6 +29,8 @@ serai-abi = { path = "../../abi", version = "0.1" }
async-lock = "3"
[dev-dependencies]
blake2 = { version = "0.11.0-rc.3", default-features = false }
tokio = { version = "1", default-features = false, features = ["rt", "macros"] }
dockertest = "0.5"
serai-docker-tests = { path = "../../../tests/docker" }

View File

@@ -59,7 +59,7 @@ pub struct Serai {
pub struct TemporalSerai<'a> {
serai: &'a Serai,
block: BlockHash,
events: Arc<RwLock<Option<Vec<Event>>>>,
events: Arc<RwLock<Option<Vec<Vec<Event>>>>>,
}
impl Serai {
@@ -109,10 +109,10 @@ impl Serai {
Response { result: None, error: Some(error) } => {
Err(RpcError::ErrorInResponse(error.message))
}
Response { result: Some(_), error: Some(_) } | Response { result: None, error: None } => {
Err(RpcError::InvalidNode(
"node didn't exclusively provide either `result` or `error`".to_string(),
))
// TODO: https://github.com/core-json/core-json/issues/18
Response { result: None, error: None } => Ok(Default::default()),
Response { result: Some(_), error: Some(_) } => {
Err(RpcError::InvalidNode("node didn't provided both `result` and `error`".to_string()))
}
}
}
@@ -125,12 +125,12 @@ impl Serai {
/// Fetch the latest finalized block number.
pub async fn latest_finalized_block_number(&self) -> Result<u64, RpcError> {
self.call("serai_latestFinalizedBlockNumber", "[]").await
self.call("blockchain/latest_finalized_block_number", "[]").await
}
/// Fetch if a block is finalized.
pub async fn finalized(&self, block: BlockHash) -> Result<bool, RpcError> {
self.call("serai_isFinalized", &format!(r#"["{block}"]"#)).await
self.call("blockchain/is_finalized", &format!(r#"{{ "block": "{block}" }}"#)).await
}
async fn block_internal(
@@ -147,12 +147,13 @@ impl Serai {
/// Fetch a block from the Serai blockchain.
pub async fn block(&self, block: BlockHash) -> Result<Block, RpcError> {
Self::block_internal(self.call("serai_block", &format!(r#"["{block}"]"#))).await
Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": "{block}" }}"#)))
.await
}
/// Fetch a block from the Serai blockchain by its number.
pub async fn block_by_number(&self, block: u64) -> Result<Block, RpcError> {
Self::block_internal(self.call("serai_block", &format!("[{block}]"))).await
Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": {block} }}"#))).await
}
/// Scope this RPC client to the state as of a specific block.
@@ -183,10 +184,20 @@ impl Serai {
}
impl<'a> TemporalSerai<'a> {
async fn call<ResponseValue: Default + JsonDeserialize>(
&self,
method: &str,
params: &str,
) -> Result<ResponseValue, RpcError> {
self.serai.call(method, &format!(r#"{{ "block": "{}" {params} }}"#, self.block)).await
}
/// Fetch the events for this block.
///
/// The returned `Option` will always be `Some(_)`.
async fn events(&self) -> Result<async_lock::RwLockReadGuard<'_, Option<Vec<Event>>>, RpcError> {
async fn events_borrowed(
&self,
) -> Result<async_lock::RwLockReadGuard<'_, Option<Vec<Vec<Event>>>>, RpcError> {
let mut events = self.events.read().await;
if events.is_none() {
drop(events);
@@ -195,21 +206,25 @@ impl<'a> TemporalSerai<'a> {
if events_mut.is_none() {
*events_mut = Some(
self
.serai
.call::<Vec<String>>("serai_events", &format!(r#"["{}"]"#, self.block))
.call::<Vec<Vec<String>>>("blockchain/events", "")
.await?
.into_iter()
.map(|event| {
Event::deserialize(
&mut hex::decode(&event)
.map_err(|_| {
RpcError::InvalidNode("node returned non-hex-encoded event".to_string())
})?
.as_slice(),
)
.map_err(|_| RpcError::InvalidNode("node returned invalid event".to_string()))
.map(|events_per_tx| {
events_per_tx
.into_iter()
.map(|event| {
Event::deserialize(
&mut hex::decode(&event)
.map_err(|_| {
RpcError::InvalidNode("node returned non-hex-encoded event".to_string())
})?
.as_slice(),
)
.map_err(|_| RpcError::InvalidNode("node returned invalid event".to_string()))
})
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<_, _>>()?,
.collect::<Result<Vec<_>, _>>()?,
);
}
}
@@ -218,6 +233,14 @@ impl<'a> TemporalSerai<'a> {
Ok(events)
}
/// Fetch the events for this block.
///
/// These will be grouped by the transactions which emitted them, including the inherent
/// transactions at the start and end of every block.
pub async fn events(&self) -> Result<Vec<Vec<Event>>, RpcError> {
Ok(self.events_borrowed().await?.clone().expect("`TemporalSerai::events` returned None"))
}
/// Scope to the validator sets module.
pub fn validator_sets(&self) -> ValidatorSets<'_> {
ValidatorSets(self)

View File

@@ -1,6 +1,27 @@
pub use serai_abi::validator_sets::Event;
use borsh::BorshDeserialize;
pub use serai_abi::{
primitives::{
crypto::KeyPair,
network_id::{ExternalNetworkId, NetworkId},
validator_sets::{Session, ExternalValidatorSet, ValidatorSet},
balance::Amount,
},
validator_sets::Event,
};
use crate::{RpcError, TemporalSerai};
fn rpc_network(network: impl Into<NetworkId>) -> Result<&'static str, RpcError> {
Ok(match network.into() {
NetworkId::Serai => r#""serai""#,
NetworkId::External(ExternalNetworkId::Bitcoin) => r#""bitcoin""#,
NetworkId::External(ExternalNetworkId::Ethereum) => r#""ethereum""#,
NetworkId::External(ExternalNetworkId::Monero) => r#""monero""#,
_ => Err(RpcError::InternalError("unrecognized network ID".to_string()))?,
})
}
/// A `TemporalSerai` scoped to the validator sets module.
#[derive(Clone)]
pub struct ValidatorSets<'a>(pub(super) &'a TemporalSerai<'a>);
@@ -11,11 +32,12 @@ impl<'a> ValidatorSets<'a> {
Ok(
self
.0
.events()
.events_borrowed()
.await?
.as_ref()
.expect("`TemporalSerai::events` returned None")
.iter()
.flat_map(IntoIterator::into_iter)
.filter_map(|event| match event {
serai_abi::Event::ValidatorSets(event) => Some(event.clone()),
_ => None,
@@ -36,6 +58,18 @@ impl<'a> ValidatorSets<'a> {
)
}
/// The `SetKeys` events from the validator sets module.
pub async fn set_keys_events(&self) -> Result<Vec<Event>, RpcError> {
Ok(
self
.events()
.await?
.into_iter()
.filter(|event| matches!(event, Event::SetKeys { .. }))
.collect(),
)
}
/// The `AcceptedHandover` events from the validator sets module.
pub async fn accepted_handover_events(&self) -> Result<Vec<Event>, RpcError> {
Ok(
@@ -47,4 +81,65 @@ impl<'a> ValidatorSets<'a> {
.collect(),
)
}
/// The `SlashReport` events from the validator sets module.
pub async fn slash_report_events(&self) -> Result<Vec<Event>, RpcError> {
Ok(
self
.events()
.await?
.into_iter()
.filter(|event| matches!(event, Event::SlashReport { .. }))
.collect(),
)
}
/// The current session for the specified network.
pub async fn current_session(&self, network: NetworkId) -> Result<Option<Session>, RpcError> {
Ok(
self
.0
.call::<Option<_>>(
"validator-sets/current_session",
&format!(r#", "network": {} "#, rpc_network(network)?),
)
.await?
.map(Session),
)
}
/// The stake for the current validators for specified network.
pub async fn current_stake(&self, network: NetworkId) -> Result<Option<Amount>, RpcError> {
Ok(
self
.0
.call::<Option<_>>(
"validator-sets/current_stake",
&format!(r#", "network": {} "#, rpc_network(network)?),
)
.await?
.map(Amount),
)
}
/// The keys for the specified validator set.
pub async fn keys(&self, set: ExternalValidatorSet) -> Result<Option<KeyPair>, RpcError> {
let Some(key_pair) = self
.0
.call::<Option<String>>(
"validator-sets/keys",
&format!(r#", "network": {}, "session": {} "#, rpc_network(set.network)?, set.session.0),
)
.await?
else {
return Ok(None);
};
KeyPair::deserialize(
&mut hex::decode(key_pair)
.map_err(|_| RpcError::InvalidNode("validator set's keys weren't valid hex".to_string()))?
.as_slice(),
)
.map(Some)
.map_err(|_| RpcError::InvalidNode("validator set's keys weren't a valid key pair".to_string()))
}
}

View File

@@ -1,3 +1,14 @@
use std::collections::HashSet;
use blake2::{Digest, Blake2b256};
use serai_abi::{
primitives::merkle::UnbalancedMerkleTree, BLOCK_HEADER_LEAF_TAG, BLOCK_HEADER_BRANCH_TAG,
TRANSACTION_COMMITMENT_LEAF_TAG, TRANSACTION_COMMITMENT_BRANCH_TAG,
TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG, TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG,
EVENTS_COMMITMENT_LEAF_TAG, EVENTS_COMMITMENT_BRANCH_TAG,
};
use serai_client_serai::*;
#[tokio::test]
@@ -91,6 +102,99 @@ async fn blockchain() {
test_finalized_block(next_finalized).await;
}
// Check the blocks have the expected headers
{
let mut last_block_number = serai.latest_finalized_block_number().await.unwrap();
let mut observed_consensus_commitments = HashSet::new();
let mut tagged_block_hashes = vec![];
for i in 0 ..= last_block_number {
let block = serai.block_by_number(i).await.unwrap();
assert_eq!(block.header.number(), i);
{
assert_eq!(
UnbalancedMerkleTree::new(BLOCK_HEADER_BRANCH_TAG, tagged_block_hashes.clone()).root,
block.header.builds_upon().root,
);
tagged_block_hashes.push({
let mut tagged = vec![BLOCK_HEADER_LEAF_TAG];
tagged.extend(&block.header.hash().0);
Blake2b256::digest(tagged).into()
});
}
{
let mut start_transaction = [0; 32];
start_transaction[24 ..].copy_from_slice(&i.to_be_bytes());
let mut end_transaction = start_transaction;
end_transaction[.. 16].copy_from_slice(&[0xff; 16]);
let transactions_iter = core::iter::once(start_transaction)
.chain(block.transactions.iter().map(serai_abi::Transaction::hash))
.chain(core::iter::once(end_transaction));
let events = serai.as_of(block.header.hash()).await.unwrap().events().await.unwrap();
assert_eq!(events.len(), 2 + block.transactions.len());
let mut transaction_leaves = vec![];
let mut events_leaves = vec![];
for (transaction, events) in transactions_iter.zip(events) {
{
let mut tagged = vec![TRANSACTION_COMMITMENT_LEAF_TAG];
tagged.extend(&transaction);
transaction_leaves.push(Blake2b256::digest(tagged).into());
}
{
let events = UnbalancedMerkleTree::new(
TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG,
events
.into_iter()
.map(|event| {
let mut tagged = vec![TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG];
tagged.extend(&borsh::to_vec(&event).unwrap());
Blake2b256::digest(tagged).into()
})
.collect(),
)
.root;
let mut tagged = vec![EVENTS_COMMITMENT_LEAF_TAG];
tagged.extend(&transaction);
tagged.extend(&events);
events_leaves.push(Blake2b256::digest(tagged).into());
}
}
assert_eq!(
UnbalancedMerkleTree::new(TRANSACTION_COMMITMENT_BRANCH_TAG, transaction_leaves).root,
block.header.transactions_commitment().root
);
assert_eq!(
UnbalancedMerkleTree::new(EVENTS_COMMITMENT_BRANCH_TAG, events_leaves).root,
block.header.events_commitment().root
);
}
match block.header {
serai_abi::Header::V1(serai_abi::HeaderV1 {
unix_time_in_millis,
consensus_commitment,
..
}) => {
if i == 0 {
assert_eq!(unix_time_in_millis, 0);
} else {
assert!(unix_time_in_millis != 0);
}
// We treat the `consensus_commitment` as opaque, but we do want to make sure it's set
// This check practically ensures it's being properly defined for each block
assert!(!observed_consensus_commitments.contains(&consensus_commitment));
observed_consensus_commitments.insert(consensus_commitment);
}
}
}
}
println!("Finished `serai-client/blockchain` test");
})
.await;

View File

@@ -1,7 +1,8 @@
use serai_abi::{
primitives::{
network_id::{ExternalNetworkId, NetworkId},
validator_sets::{Session, ValidatorSet},
balance::Amount,
validator_sets::{Session, ExternalValidatorSet, ValidatorSet},
},
validator_sets::Event,
};
@@ -125,7 +126,32 @@ async fn validator_sets() {
);
}
println!("Finished `serai-client/blockchain` test");
{
let serai =
serai.as_of(serai.block_by_number(0).await.unwrap().header.hash()).await.unwrap();
let serai = serai.validator_sets();
for network in NetworkId::all() {
match network {
NetworkId::Serai => {
assert_eq!(serai.current_session(network).await.unwrap(), Some(Session(0)));
assert_eq!(serai.current_stake(network).await.unwrap(), Some(Amount(0)));
}
NetworkId::External(external) => {
assert!(serai.current_session(network).await.unwrap().is_none());
assert!(serai.current_stake(network).await.unwrap().is_none());
assert_eq!(
serai
.keys(ExternalValidatorSet { network: external, session: Session(0) })
.await
.unwrap(),
None
);
}
}
}
}
println!("Finished `serai-client/validator_sets` test");
})
.await;
}

View File

@@ -18,10 +18,10 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
serai-core-pallet = { path = "../core", default-features = false }
@@ -29,7 +29,9 @@ serai-core-pallet = { path = "../core", default-features = false }
[dev-dependencies]
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false, features = ["std"] }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, features = ["std"] }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, features = ["std"] }
[features]
std = [

View File

@@ -10,6 +10,7 @@ construct_runtime!(
pub enum Test
{
System: frame_system,
Timestamp: pallet_timestamp,
Core: serai_core_pallet,
Coins: coins::<CoinsInstance>,
}
@@ -22,6 +23,9 @@ impl frame_system::Config for Test {
type Block = frame_system::mocking::MockBlock<Test>;
}
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]
impl pallet_timestamp::Config for Test {}
impl serai_core_pallet::Config for Test {}
impl crate::Config<CoinsInstance> for Test {

View File

@@ -29,6 +29,7 @@ fn mint() {
// test events
let mint_events = Core::events()
.iter()
.flat_map(IntoIterator::into_iter)
.map(|event| borsh::from_slice::<serai_abi::Event>(event.as_slice()).unwrap())
.filter_map(|event| {
if let serai_abi::Event::Coins(e) = &event {
@@ -83,6 +84,7 @@ fn burn_with_instruction() {
let burn_events = Core::events()
.iter()
.flat_map(IntoIterator::into_iter)
.map(|event| borsh::from_slice::<serai_abi::Event>(event.as_slice()).unwrap())
.filter_map(|event| {
if let serai_abi::Event::Coins(e) = &event {

View File

@@ -20,10 +20,12 @@ borsh = { version = "1", default-features = false, features = ["derive", "de_str
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
@@ -37,14 +39,17 @@ std = [
"frame-system/std",
"frame-support/std",
"pallet-timestamp/std",
"serai-abi/std",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
]
try-runtime = ["serai-abi/try-runtime"]
try-runtime = ["pallet-timestamp/try-runtime", "serai-abi/try-runtime"]
default = ["std"]

View File

@@ -8,8 +8,8 @@ use serai_abi::primitives::merkle::{UnbalancedMerkleTree, IncrementalUnbalancedM
/// `IncrementalUnbalancedMerkleTree`.
pub struct IncrementalUnbalancedMerkleTree<
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8 = 1,
const LEAF_TAG: u8 = 0,
const BRANCH_TAG: u8,
const LEAF_TAG: u8,
>(PhantomData<T>);
impl<
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,

View File

@@ -4,18 +4,22 @@
#![cfg_attr(not(feature = "std"), no_std)]
use core::marker::PhantomData;
extern crate alloc;
use frame_support::traits::{PreInherents, PostTransactions};
mod iumt;
pub use iumt::*;
#[expect(clippy::cast_possible_truncation)]
#[frame_support::pallet]
pub mod pallet {
use alloc::vec::Vec;
use alloc::{vec::Vec, vec};
use frame_support::pallet_prelude::*;
use frame_support::{
sp_runtime::traits::{Header, Block},
pallet_prelude::*,
};
use serai_abi::primitives::{prelude::*, merkle::IncrementalUnbalancedMerkleTree as Iumt};
@@ -28,14 +32,21 @@ pub mod pallet {
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlocksCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlocksCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<BlocksCommitment<T>>;
pub(super) type BlocksCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<
BlocksCommitment<T>,
{ serai_abi::BLOCK_HEADER_BRANCH_TAG },
{ serai_abi::BLOCK_HEADER_LEAF_TAG },
>;
/// The Merkle tree of all transactions within the current block.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockTransactionsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockTransactionsCommitmentMerkle<T> =
IncrementalUnbalancedMerkleTree<BlockTransactionsCommitment<T>>;
pub(super) type BlockTransactionsCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<
BlockTransactionsCommitment<T>,
{ serai_abi::TRANSACTION_COMMITMENT_BRANCH_TAG },
{ serai_abi::TRANSACTION_COMMITMENT_LEAF_TAG },
>;
/// The hashes of events caused by the current transaction.
#[pallet::storage]
@@ -64,12 +75,17 @@ pub mod pallet {
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A transaction begun.
BeginTransaction,
/// An event from Serai.
Event(Vec<u8>),
}
#[pallet::config]
pub trait Config: frame_system::Config<Hash: Into<[u8; 32]>> {}
pub trait Config:
frame_system::Config<Hash: Into<[u8; 32]>> + pallet_timestamp::Config<Moment = u64>
{
}
#[pallet::pallet]
pub struct Pallet<T>(_);
@@ -99,10 +115,16 @@ pub mod pallet {
}
/// The code to run on genesis.
pub fn genesis() {
pub fn genesis(config: &impl frame_support::traits::BuildGenesisConfig) {
BlocksCommitmentMerkle::<T>::new_expecting_none();
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none();
Self::start_transaction();
<_>::build(config);
Self::end_transaction([0; 32]);
EndOfBlock::<T>::post_transactions();
}
/// The code to run when beginning execution of a transaction.
@@ -110,6 +132,7 @@ pub mod pallet {
/// The caller MUST ensure two transactions aren't simultaneously started.
pub fn start_transaction() {
TransactionEventsMerkle::<T>::new_expecting_none();
Self::deposit_event(Event::BeginTransaction);
}
/// Emit an event.
@@ -130,19 +153,24 @@ pub mod pallet {
BlockEventsCommitmentMerkle::<T>::append(&(&transaction_hash, &transaction_events_root));
}
/// Fetch all of Serai's events.
/// Fetch all of Serai's events for each transaction.
///
/// This MUST NOT be called during a transaction/block's execution.
pub fn events() -> Vec<Vec<u8>>
pub fn events() -> Vec<Vec<Vec<u8>>>
where
T::RuntimeEvent: TryInto<Event<T>>,
{
frame_system::Pallet::<T>::read_events_no_consensus()
.filter_map(|e| match e.event.try_into() {
Ok(Event::Event(bytes)) => Some(bytes),
_ => None,
})
.collect()
let mut result = vec![];
for event in frame_system::Pallet::<T>::read_events_no_consensus() {
match event.event.try_into() {
Ok(Event::BeginTransaction) => result.push(vec![]),
Ok(Event::Event(bytes)) => {
result.last_mut().expect("Serai event outside of a transaction").push(bytes)
}
Err(_) => {}
}
}
result
}
}
}
@@ -150,7 +178,7 @@ pub use pallet::*;
/// The code to run at the start of a block for this pallet.
pub struct StartOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PreInherents for StartOfBlock<T> {
impl<T: Config> PreInherents for StartOfBlock<T> {
fn pre_inherents() {
use frame_support::pallet_prelude::Zero;
// `Pallet::genesis` is expected to be used for the genesis block
@@ -163,13 +191,44 @@ impl<T: Config> frame_support::traits::PreInherents for StartOfBlock<T> {
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none();
Pallet::<T>::start_transaction();
// Handle the `SeraiPreExecutionDigest`
/*
These calls panic but this is desired behavior. All blocks, except the genesis, should have
this and the timestamp should be valid.
*/
{
let digest = serai_abi::SeraiPreExecutionDigest::find(&frame_system::Pallet::<T>::digest());
pallet_timestamp::Pallet::<T>::set(
frame_system::RawOrigin::None.into(),
digest.unix_time_in_millis,
)
.expect("failed to set timestamp");
}
// Other modules' `PreInherents`
let block_number: sp_core::U256 = frame_system::Pallet::<T>::block_number().into();
let start_of_block_transaction_hash = block_number.to_big_endian();
Pallet::<T>::end_transaction(start_of_block_transaction_hash);
}
}
/// The code to run at the end of a block for this pallet.
pub struct EndOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PostTransactions for EndOfBlock<T> {
impl<T: Config> PostTransactions for EndOfBlock<T> {
fn post_transactions() {
Pallet::<T>::start_transaction();
// Other modules' `PostTransactions`
let block_number: sp_core::U256 = frame_system::Pallet::<T>::block_number().into();
let mut end_of_block_transaction_hash = block_number.to_big_endian();
end_of_block_transaction_hash[.. 16].copy_from_slice(&[0xff; 16]);
Pallet::<T>::end_transaction(end_of_block_transaction_hash);
use serai_abi::SeraiExecutionDigest;
frame_system::Pallet::<T>::deposit_log(
frame_support::sp_runtime::generic::DigestItem::Consensus(

View File

@@ -21,15 +21,15 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false, optional = true }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false, optional = true }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }

View File

@@ -21,8 +21,8 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
@@ -30,16 +30,16 @@ coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-feat
serai-primitives = { path = "../primitives", default-features = false }
[dev-dependencies]
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
[features]
std = [

View File

@@ -21,11 +21,11 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }

View File

@@ -21,12 +21,12 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }

View File

@@ -24,14 +24,14 @@ bitvec = { version = "1", default-features = false, features = ["alloc"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-std = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-primitives = { path = "../primitives", default-features = false }
@@ -42,9 +42,9 @@ genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path =
emissions-pallet = { package = "serai-emissions-pallet", path = "../emissions", default-features = false }
[dev-dependencies]
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../economic-security", default-features = false }

View File

@@ -34,20 +34,20 @@ secq256k1 = { path = "../../crypto/secq256k1" }
libp2p = "0.56"
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-inherents = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-blockchain = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-state-machine = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-keystore = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-inherents = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-blockchain = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-state-machine = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-keystore = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
frame-benchmarking = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
serai-abi = { path = "../abi", features = ["std", "substrate"] }
serai-runtime = { path = "../runtime", features = ["std"] }
@@ -59,23 +59,24 @@ futures-util = "0.3"
tokio = { version = "1", features = ["sync", "rt-multi-thread"] }
jsonrpsee = { version = "0.24", features = ["server"] }
sc-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-transaction-pool-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-basic-authorship = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-executor = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-service = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-client-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-network = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-transaction-pool-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-basic-authorship = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-client-db = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-executor = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-service = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-client-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-network = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-consensus-slots = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-consensus = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-consensus-slots = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-telemetry = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-chain-spec = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-cli = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
sc-telemetry = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-chain-spec = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
sc-cli = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
serai-env = { path = "../../common/env" }

View File

@@ -1,7 +1,15 @@
use core::marker::PhantomData;
use std::sync::Arc;
use sp_core::Pair as PairTrait;
use sp_core::{Decode, storage::Storage, Pair as PairTrait};
use sp_runtime::{
traits::{Block as _, Header as _},
BuildStorage,
};
use sc_client_db::Backend;
use sc_executor::RuntimeVersionOf;
use sc_chain_spec::{BuildGenesisBlock, GenesisBlockBuilder, ChainSpec as ChainSpecTrait};
use sc_client_api::BlockImportOperation;
use sc_service::ChainType;
use rand_core::OsRng;
@@ -201,3 +209,45 @@ pub fn bootnode_multiaddrs(id: &str) -> Vec<libp2p::Multiaddr> {
_ => panic!("unrecognized network ID"),
}
}
struct GenesisBlock<Executor>(
GenesisBlockBuilder<Block, Backend<Block>, Executor>,
sp_runtime::Digest,
);
impl<Executor: RuntimeVersionOf> BuildGenesisBlock<Block> for GenesisBlock<Executor> {
type BlockImportOperation = sc_client_db::BlockImportOperation<Block>;
fn build_genesis_block(self) -> sp_blockchain::Result<(Block, Self::BlockImportOperation)> {
let (genesis_block, op) = self.0.build_genesis_block()?;
let mut header = genesis_block.header().clone();
*header.digest_mut() = self.1;
let genesis_block = Block::new(header, genesis_block.extrinsics().to_vec());
Ok((genesis_block, op))
}
}
pub(super) fn genesis_block(
chain_spec: &dyn ChainSpecTrait,
backend: Arc<Backend<Block>>,
executor: impl RuntimeVersionOf,
) -> Result<
impl BuildGenesisBlock<Block, BlockImportOperation = sc_client_db::BlockImportOperation<Block>>,
sc_service::error::Error,
> {
let storage = chain_spec.as_storage_builder().build_storage()?;
let digest = {
let digest_key = [sp_core::twox_128(b"System"), sp_core::twox_128(b"Digest")].concat();
sp_runtime::Digest::decode(
&mut storage.top.get(&digest_key).expect("System Digest not set").as_slice(),
)
.expect("failed to decode System Digest")
};
let commit_genesis_state = true;
Ok(GenesisBlock(
GenesisBlockBuilder::new_with_storage(storage, commit_genesis_state, backend, executor)?,
digest,
))
}

View File

@@ -15,46 +15,7 @@ use serai_runtime::SeraiApi;
use jsonrpsee::RpcModule;
fn block_hash<
C: HeaderMetadata<Block, Error = BlockchainError>
+ HeaderBackend<Block>
+ BlockBackend<Block>
+ ProvideRuntimeApi<Block>,
>(
client: &C,
params: &jsonrpsee::types::params::Params,
) -> Result<<Block as sp_runtime::traits::Block>::Hash, jsonrpsee::types::error::ErrorObjectOwned> {
Ok(if let Ok(block_hash) = params.sequence().next::<String>() {
let Some(block_hash) = hex::decode(&block_hash).ok().and_then(|bytes| {
<[u8; 32]>::try_from(bytes.as_slice())
.map(<Block as sp_runtime::traits::Block>::Hash::from)
.ok()
}) else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-1,
"requested block hash wasn't a valid hash",
Option::<()>::None,
));
};
block_hash
} else {
let Ok(block_number) = params.sequence().next::<u64>() else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-1,
"requested block wasn't a valid hash nor number",
Option::<()>::None,
));
};
let Ok(Some(block_hash)) = client.block_hash(block_number) else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-2,
"couldn't find requested block's hash",
Option::<()>::None,
));
};
block_hash
})
}
use super::utils::{Error, block_hash};
pub(crate) fn module<
C: 'static
@@ -69,56 +30,48 @@ pub(crate) fn module<
) -> Result<RpcModule<impl 'static + Send + Sync>, Box<dyn std::error::Error + Send + Sync>> {
let mut module = RpcModule::new(client);
module.register_method("serai_latestFinalizedBlockNumber", |_params, client, _ext| {
module.register_method("blockchain/latest_finalized_block_number", |_params, client, _ext| {
client.info().finalized_number
});
module.register_method("serai_isFinalized", |params, client, _ext| {
let block_hash = block_hash(&**client, &params)?;
let finalized = client.info().finalized_number;
let Ok(Some(number)) = client.number(block_hash) else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-2,
"failed to fetch block's number",
Option::<()>::None,
));
};
let Ok(status) = client.block_status(block_hash) else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-3,
"failed to fetch block's status",
Option::<()>::None,
));
};
Ok(
matches!(status, BlockStatus::InChainWithState | BlockStatus::InChainPruned) &&
(number <= finalized),
)
})?;
module.register_method(
"blockchain/is_finalized",
|params, client, _ext| -> Result<_, Error> {
let block_hash = block_hash(&**client, &params)?;
let finalized = client.info().finalized_number;
let Ok(Some(number)) = client.number(block_hash) else {
Err(Error::Missing("failed to fetch block's number"))?
};
let Ok(status) = client.block_status(block_hash) else {
Err(Error::Internal("failed to fetch block's status"))?
};
Ok(
matches!(status, BlockStatus::InChainWithState | BlockStatus::InChainPruned) &&
(number <= finalized),
)
},
)?;
module.register_method("serai_block", |params, client, _ext| {
module.register_method("blockchain/block", |params, client, _ext| -> Result<_, Error> {
let block_hash = block_hash(&**client, &params)?;
let Ok(Some(block)) = client.block(block_hash) else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-2,
"couldn't find requested block",
Option::<()>::None,
));
Err(Error::Missing("couldn't find requested block"))?
};
Ok(hex::encode(borsh::to_vec(&serai_abi::Block::from(block.block)).unwrap()))
})?;
module.register_method("serai_events", |params, client, _ext| {
module.register_method("blockchain/events", |params, client, _ext| -> Result<_, Error> {
let block_hash = block_hash(&**client, &params)?;
let Ok(events) = client.runtime_api().events(block_hash) else {
return Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-2,
"couldn't fetch the events for the requested block",
Option::<()>::None,
));
Err(Error::Missing("couldn't fetch the events for the requested block"))?
};
Ok(events.into_iter().map(hex::encode).collect::<Vec<String>>())
Ok(
events
.into_iter()
.map(|events_per_tx| events_per_tx.into_iter().map(hex::encode).collect::<Vec<_>>())
.collect::<Vec<_>>(),
)
})?;
Ok(module)

View File

@@ -16,7 +16,9 @@ use jsonrpsee::RpcModule;
use sc_client_api::BlockBackend;
use sc_transaction_pool_api::TransactionPool;
mod utils;
mod blockchain;
mod validator_sets;
mod p2p_validators;
pub struct FullDeps<C, P> {
@@ -42,6 +44,7 @@ pub fn create_full<
let mut root = RpcModule::new(());
root.merge(blockchain::module(client.clone())?)?;
root.merge(validator_sets::module(client.clone()))?;
if let Some(authority_discovery) = authority_discovery {
root.merge(p2p_validators::module(id, client, authority_discovery)?)?;
}

View File

@@ -13,6 +13,8 @@ use tokio::sync::RwLock;
use jsonrpsee::RpcModule;
use super::utils::Error;
pub(crate) fn module<
C: 'static + Send + Sync + HeaderBackend<Block> + ProvideRuntimeApi<Block, Api: SeraiApi<Block>>,
>(
@@ -22,34 +24,28 @@ pub(crate) fn module<
) -> Result<RpcModule<impl 'static + Send + Sync>, Box<dyn std::error::Error + Send + Sync>> {
let mut module = RpcModule::new((id, client, RwLock::new(authority_discovery)));
module.register_async_method("p2p_validators", |params, context, _ext| async move {
let [network]: [String; 1] = params.parse()?;
let network = match params.parse::<[String; 1]>() {
Ok([network]) => network,
Err(e) => return Err(e),
};
let network = match network.to_lowercase().as_str() {
"serai" => NetworkId::Serai,
"bitcoin" => ExternalNetworkId::Bitcoin.into(),
"ethereum" => ExternalNetworkId::Ethereum.into(),
"monero" => ExternalNetworkId::Monero.into(),
_ => Err(jsonrpsee::types::error::ErrorObjectOwned::owned(
-1,
"network to fetch the `p2p_validators` of was unrecognized".to_string(),
Option::<()>::None,
))?,
_ => Err(Error::InvalidRequest("network to fetch the `p2p_validators` of was unrecognized"))?,
};
let (id, client, authority_discovery) = &*context;
let latest_block = client.info().best_hash;
let validators = client.runtime_api().validators(latest_block, network).map_err(|_| {
jsonrpsee::types::error::ErrorObjectOwned::owned(
-2,
format!(
"couldn't get validators from the latest block, which is likely a fatal bug. {}",
"please report this at https://github.com/serai-dex/serai",
),
Option::<()>::None,
)
});
let validators = client
.runtime_api()
.validators(latest_block, network)
.map_err(|_| Error::Internal("couldn't get validators from the latest block"));
let validators = match validators {
Ok(validators) => validators,
Err(e) => return Err(e),
Err(e) => Err(e)?,
};
// Always return the protocol's bootnodes
let mut all_p2p_addresses = crate::chain_spec::bootnode_multiaddrs(id);

View File

@@ -0,0 +1,63 @@
use sp_blockchain::{Error as BlockchainError, HeaderMetadata, HeaderBackend};
use sc_client_api::BlockBackend;
use serai_abi::{primitives::prelude::*, SubstrateBlock as Block};
pub(super) enum Error {
Internal(&'static str),
InvalidRequest(&'static str),
Missing(&'static str),
}
impl From<Error> for jsonrpsee::types::error::ErrorObjectOwned {
fn from(error: Error) -> Self {
match error {
Error::Internal(str) => {
jsonrpsee::types::error::ErrorObjectOwned::owned(-1, str, Option::<()>::None)
}
Error::InvalidRequest(str) => {
jsonrpsee::types::error::ErrorObjectOwned::owned(-2, str, Option::<()>::None)
}
Error::Missing(str) => {
jsonrpsee::types::error::ErrorObjectOwned::owned(-3, str, Option::<()>::None)
}
}
}
}
pub(super) fn block_hash<
C: HeaderMetadata<Block, Error = BlockchainError> + HeaderBackend<Block> + BlockBackend<Block>,
>(
client: &C,
params: &jsonrpsee::types::params::Params,
) -> Result<<Block as sp_runtime::traits::Block>::Hash, Error> {
#[derive(sp_core::serde::Deserialize)]
#[serde(crate = "sp_core::serde")]
struct BlockByHash {
block: String,
};
#[derive(sp_core::serde::Deserialize)]
#[serde(crate = "sp_core::serde")]
struct BlockByNumber {
block: u64,
};
Ok(if let Ok(block_hash) = params.parse::<BlockByHash>() {
let Some(block_hash) = hex::decode(&block_hash.block).ok().and_then(|bytes| {
<[u8; 32]>::try_from(bytes.as_slice())
.map(<Block as sp_runtime::traits::Block>::Hash::from)
.ok()
}) else {
return Err(Error::InvalidRequest("requested block hash wasn't a valid hash"));
};
block_hash
} else {
let Ok(block_number) = params.parse::<BlockByNumber>() else {
return Err(Error::InvalidRequest("requested block wasn't a valid hash nor number"));
};
let Ok(Some(block_hash)) = client.block_hash(block_number.block) else {
return Err(Error::Missing("no block hash for that block number"));
};
block_hash
})
}

View File

@@ -0,0 +1,109 @@
use std::{sync::Arc, ops::Deref, convert::AsRef, collections::HashSet};
use rand_core::{RngCore, OsRng};
use sp_core::Encode;
use sp_blockchain::{Error as BlockchainError, HeaderMetadata, HeaderBackend};
use sp_consensus::BlockStatus;
use sp_block_builder::BlockBuilder;
use sp_api::ProvideRuntimeApi;
use sc_client_api::BlockBackend;
use serai_abi::{primitives::prelude::*, SubstrateBlock as Block};
use serai_runtime::SeraiApi;
use jsonrpsee::RpcModule;
use super::utils::{Error, block_hash};
fn network_from_str(network: impl AsRef<str>) -> Result<NetworkId, Error> {
Ok(match network.as_ref().to_lowercase().as_str() {
"serai" => NetworkId::Serai,
"bitcoin" => NetworkId::External(ExternalNetworkId::Bitcoin),
"ethereum" => NetworkId::External(ExternalNetworkId::Ethereum),
"monero" => NetworkId::External(ExternalNetworkId::Monero),
_ => Err(Error::InvalidRequest("unrecognized network requested"))?,
})
}
pub(super) fn network(params: &jsonrpsee::types::params::Params) -> Result<NetworkId, Error> {
#[derive(sp_core::serde::Deserialize)]
#[serde(crate = "sp_core::serde")]
struct Network {
network: String,
}
let Ok(network) = params.parse::<Network>() else {
Err(Error::InvalidRequest(r#"missing `string` "network" field"#))?
};
network_from_str(network.network)
}
pub(super) fn set(params: &jsonrpsee::types::params::Params) -> Result<ValidatorSet, Error> {
#[derive(sp_core::serde::Deserialize)]
#[serde(crate = "sp_core::serde")]
struct Set {
network: String,
session: u32,
}
let Ok(set) = params.parse::<Set>() else {
Err(Error::InvalidRequest(r#"missing `object` "set" field"#))?
};
Ok(ValidatorSet { network: network_from_str(set.network)?, session: Session(set.session) })
}
pub(crate) fn module<
C: 'static
+ Send
+ Sync
+ HeaderMetadata<Block, Error = BlockchainError>
+ HeaderBackend<Block>
+ BlockBackend<Block>
+ ProvideRuntimeApi<Block, Api: SeraiApi<Block>>,
>(
client: Arc<C>,
) -> RpcModule<impl 'static + Send + Sync> {
let mut module = RpcModule::new(client);
module.register_method(
"validator-sets/current_session",
|params, client, _ext| -> Result<_, Error> {
let block_hash = block_hash(&**client, &params)?;
let network = network(&params)?;
let Ok(session) = client.runtime_api().current_session(block_hash, network) else {
Err(Error::Internal("couldn't fetch the session for the requested network"))?
};
Ok(session.map(|session| session.0))
},
);
module.register_method(
"validator-sets/current_stake",
|params, client, _ext| -> Result<_, Error> {
let block_hash = block_hash(&**client, &params)?;
let network = network(&params)?;
let Ok(stake) = client.runtime_api().current_stake(block_hash, network) else {
Err(Error::Internal("couldn't fetch the total allocated stake for the requested network"))?
};
Ok(stake.map(|stake| stake.0))
},
);
module.register_method("validator-sets/keys", |params, client, _ext| -> Result<_, Error> {
let block_hash = block_hash(&**client, &params)?;
let set = set(&params)?;
let Ok(set) = ExternalValidatorSet::try_from(set) else {
Err(Error::InvalidRequest("requested keys for a non-external validator set"))?
};
let Ok(key_pair) = client.runtime_api().keys(block_hash, set) else {
Err(Error::Internal("couldn't fetch the keys for the requested validator set"))?
};
Ok(key_pair.map(|key_pair| hex::encode(borsh::to_vec(&key_pair).unwrap())))
});
module
}

View File

@@ -22,6 +22,8 @@ use serai_runtime::RuntimeApi;
use sc_consensus_babe::{self, SlotProportion};
use sc_consensus_grandpa as grandpa;
mod proposer;
#[cfg(not(feature = "runtime-benchmarks"))]
pub type Executor = WasmExecutor<ExtendedHostFunctions<SubstrateHostFunctions, ()>>;
#[cfg(feature = "runtime-benchmarks")]
@@ -32,6 +34,8 @@ pub type Executor = WasmExecutor<
type FullBackend = sc_service::TFullBackend<Block>;
pub type FullClient = TFullClient<Block, RuntimeApi, Executor>;
pub type TransactionPool = sc_transaction_pool::TransactionPoolWrapper<Block, FullClient>;
type SelectChain = sc_consensus::LongestChain<FullBackend, Block>;
type GrandpaBlockImport = grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, SelectChain>;
type BabeBlockImport<CIDP> =
@@ -42,7 +46,7 @@ type PartialComponents<CIDP> = sc_service::PartialComponents<
FullBackend,
SelectChain,
sc_consensus::DefaultImportQueue<Block>,
sc_transaction_pool::TransactionPoolWrapper<Block, FullClient>,
TransactionPool,
(
BabeBlockImport<CIDP>,
sc_consensus_babe::BabeLink<Block>,
@@ -95,12 +99,19 @@ pub fn new_partial(
config.executor.runtime_cache_size,
);
let (client, backend, keystore_container, task_manager) =
sc_service::new_full_parts::<Block, RuntimeApi, _>(
let (client, backend, keystore_container, task_manager) = {
let telemetry = telemetry.as_ref().map(|(_, telemetry)| telemetry.handle());
let backend = sc_service::new_db_backend(config.db_config())?;
sc_service::new_full_parts_with_genesis_builder::<Block, RuntimeApi, _, _>(
config,
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
executor,
)?;
telemetry,
executor.clone(),
backend.clone(),
super::chain_spec::genesis_block(&*config.chain_spec, backend, executor)?,
false,
)?
};
let client = Arc::new(client);
let keystore: Arc<dyn sp_keystore::Keystore> =
@@ -384,13 +395,13 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
keystore: keystore.clone(),
client: client.clone(),
select_chain,
env: sc_basic_authorship::ProposerFactory::new(
env: proposer::ProposerFactory(sc_basic_authorship::ProposerFactory::new(
task_manager.spawn_handle(),
client,
transaction_pool.clone(),
prometheus_registry.as_ref(),
telemetry.as_ref().map(Telemetry::handle),
),
)),
block_import,
sync_oracle: sync_service.clone(),
justification_sync_link: sync_service.clone(),

View File

@@ -0,0 +1,56 @@
use std::time::Duration;
use sp_runtime::Digest;
use sp_consensus::{InherentData, DisableProofRecording};
use serai_abi::{SeraiPreExecutionDigest, SubstrateHeader as Header, SubstrateBlock as Block};
use super::{FullClient, TransactionPool};
type UnderlyingProposer =
sc_basic_authorship::Proposer<Block, FullClient, TransactionPool, DisableProofRecording>;
pub struct Proposer(UnderlyingProposer);
impl sp_consensus::Proposer<Block> for Proposer {
type Error = <UnderlyingProposer as sp_consensus::Proposer<Block>>::Error;
type Proposal = <UnderlyingProposer as sp_consensus::Proposer<Block>>::Proposal;
type ProofRecording = DisableProofRecording;
type Proof = ();
fn propose(
self,
inherent_data: InherentData,
mut inherent_digests: Digest,
max_duration: Duration,
block_size_limit: Option<usize>,
) -> Self::Proposal {
Box::pin(async move {
// Insert our expected digest
inherent_digests.logs.push(sp_runtime::generic::DigestItem::PreRuntime(
SeraiPreExecutionDigest::CONSENSUS_ID,
borsh::to_vec(&SeraiPreExecutionDigest {
unix_time_in_millis: inherent_data
.get_data::<sp_timestamp::Timestamp>(&sp_timestamp::INHERENT_IDENTIFIER)
.map_err(|err| sp_blockchain::Error::Application(err.into()))?
.ok_or(sp_blockchain::Error::Application("missing timestamp inherent".into()))?
.as_millis(),
})
.unwrap(),
));
// Call the underlying propose function
self.0.propose(inherent_data, inherent_digests, max_duration, block_size_limit).await
})
}
}
type UnderlyingFactory =
sc_basic_authorship::ProposerFactory<TransactionPool, FullClient, DisableProofRecording>;
pub struct ProposerFactory(pub UnderlyingFactory);
impl sp_consensus::Environment<Block> for ProposerFactory {
type CreateProposer = core::future::Ready<Result<Proposer, Self::Error>>;
type Proposer = Proposer;
type Error = <UnderlyingFactory as sp_consensus::Environment<Block>>::Error;
fn init(&mut self, parent_header: &Header) -> Self::CreateProposer {
core::future::ready(self.0.init(parent_header).into_inner().map(Proposer))
}
}

View File

@@ -23,7 +23,7 @@ borsh = { version = "1", default-features = false, features = ["derive", "de_str
bitvec = { version = "1", default-features = false, features = ["alloc"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["alloc"] }
schnorr-signatures = { path = "../../crypto/schnorr", default-features = false }

View File

@@ -20,31 +20,34 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-version = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-version = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-inherents = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-inherents = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }
[target.'cfg(target_family = "wasm")'.dependencies]
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
borsh = { version = "1", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-timestamp = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-core-pallet = { path = "../core", default-features = false }
serai-coins-pallet = { path = "../coins", default-features = false }
@@ -52,11 +55,12 @@ serai-validator-sets-pallet = { path = "../validator-sets", default-features = f
serai-signals-pallet = { path = "../signals", default-features = false }
[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" }
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84" }
[features]
std = [
"scale/std",
"borsh/std",
"sp-core/std",
"sp-session/std",

View File

@@ -6,9 +6,10 @@ extern crate alloc;
use alloc::vec::Vec;
use serai_abi::{
primitives::{
crypto::{Public, SignedEmbeddedEllipticCurveKeys},
crypto::{Public, SignedEmbeddedEllipticCurveKeys, KeyPair},
network_id::NetworkId,
balance::Balance,
validator_sets::{Session, ExternalValidatorSet, ValidatorSet},
balance::{Amount, Balance},
},
Event,
};
@@ -33,8 +34,11 @@ sp_api::decl_runtime_apis! {
fn build(genesis: GenesisConfig);
}
pub trait SeraiApi {
fn events() -> Vec<Vec<u8>>;
fn validators(network_id: NetworkId) -> Vec<Public>;
fn events() -> Vec<Vec<Vec<u8>>>;
fn validators(network: NetworkId) -> Vec<Public>;
fn current_session(network: NetworkId) -> Option<Session>;
fn current_stake(network: NetworkId) -> Option<Amount>;
fn keys(set: ExternalValidatorSet) -> Option<KeyPair>;
}
}
@@ -44,6 +48,8 @@ mod apis {
use alloc::borrow::Cow;
use serai_abi::{SubstrateHeader as Header, SubstrateBlock as Block};
use super::*;
#[sp_version::runtime_version]
pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion {
spec_name: Cow::Borrowed("serai"),
@@ -173,7 +179,7 @@ mod apis {
}
impl crate::SeraiApi<Block> for Runtime {
fn events() -> Vec<Vec<u8>> {
fn events() -> Vec<Vec<Vec<u8>>> {
unimplemented!("runtime is only implemented when WASM")
}
fn validators(
@@ -181,6 +187,15 @@ mod apis {
) -> Vec<serai_abi::primitives::crypto::Public> {
unimplemented!("runtime is only implemented when WASM")
}
fn current_session(network: NetworkId) -> Option<Session> {
unimplemented!("runtime is only implemented when WASM")
}
fn current_stake(network: NetworkId) -> Option<Amount> {
unimplemented!("runtime is only implemented when WASM")
}
fn keys(set: ExternalValidatorSet) -> Option<KeyPair> {
unimplemented!("runtime is only implemented when WASM")
}
}
}
}

View File

@@ -2,23 +2,24 @@ use core::marker::PhantomData;
use alloc::{borrow::Cow, vec, vec::Vec};
use sp_core::{ConstU32, ConstU64, sr25519::Public};
use sp_runtime::{Perbill, Weight};
use sp_runtime::{
Perbill, Weight,
traits::{Header as _, Block as _},
};
use sp_version::RuntimeVersion;
use serai_abi::{
primitives::{
network_id::{ExternalNetworkId, NetworkId},
balance::{Amount, ExternalBalance},
validator_sets::ValidatorSet,
validator_sets::{Session, ExternalValidatorSet, ValidatorSet},
address::SeraiAddress,
},
SubstrateHeader as Header, SubstrateBlock,
SubstrateHeader as Header, SubstrateBlock as Block,
};
use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance};
type Block = SubstrateBlock;
/// The lookup for a SeraiAddress -> Public.
pub struct Lookup;
impl sp_runtime::traits::StaticLookup for Lookup {
@@ -91,6 +92,10 @@ mod runtime {
#[runtime::pallet_index(5)]
pub type LiquidityTokens = serai_coins_pallet::Pallet<Runtime, LiquidityTokensInstance>;
#[runtime::pallet_index(0xfd)]
#[runtime::disable_inherent]
pub type Timestamp = pallet_timestamp::Pallet<Runtime>;
#[runtime::pallet_index(0xfe)]
pub type Babe = pallet_babe::Pallet<Runtime>;
@@ -166,13 +171,6 @@ impl serai_coins_pallet::Config<LiquidityTokensInstance> for Runtime {
type AllowMint = serai_coins_pallet::AlwaysAllowMint;
}
/*
`pallet-babe` requires we implement `pallet-timestamp` for the associated constants. It does not
actually require we offer the timestamp pallet however, and we don't as we follow our methodology
(using the block header for timestamps, not an inherent transaction).
TODO: Set timestamp when executing a block.
*/
impl pallet_timestamp::Config for Runtime {
type Moment = u64;
type OnTimestampSet = Babe;
@@ -181,6 +179,8 @@ impl pallet_timestamp::Config for Runtime {
type WeightInfo = ();
}
// pallet-babe requires `pallet-session` for `GetCurrentSessionForSubstrate` but not it itself
// We ensure this by having patched `pallet-session` to omit the pallet
#[doc(hidden)]
pub struct GetCurrentSessionForSubstrate;
impl pallet_session::GetCurrentSessionForSubstrate for GetCurrentSessionForSubstrate {
@@ -356,14 +356,7 @@ sp_api::impl_runtime_apis! {
grandpa: GrandpaConfig { authorities: vec![], _config: PhantomData },
};
Core::genesis();
Core::start_transaction();
<RuntimeGenesisConfig as frame_support::traits::BuildGenesisConfig>::build(&config);
Core::end_transaction([0; 32]);
<
serai_core_pallet::EndOfBlock<Runtime> as frame_support::traits::PostTransactions
>::post_transactions();
Core::genesis(&config);
}
}
@@ -400,7 +393,59 @@ sp_api::impl_runtime_apis! {
block: Block,
data: sp_inherents::InherentData,
) -> sp_inherents::CheckInherentsResult {
data.check_extrinsics(&block)
let mut result = data.check_extrinsics(&block);
// Handle the `SeraiPreExecutionDigest`
'outer: {
use serai_abi::SeraiPreExecutionDigest;
const INHERENT_ID: [u8; 8] = [
SeraiPreExecutionDigest::CONSENSUS_ID[0],
SeraiPreExecutionDigest::CONSENSUS_ID[1],
SeraiPreExecutionDigest::CONSENSUS_ID[2],
SeraiPreExecutionDigest::CONSENSUS_ID[3],
0, 0, 0, 0
];
for log in block.header().digest().logs() {
match log {
sp_runtime::DigestItem::PreRuntime(consensus, encoded)
if *consensus == SeraiPreExecutionDigest::CONSENSUS_ID =>
{
let Ok(SeraiPreExecutionDigest { unix_time_in_millis }) =
<_ as borsh::BorshDeserialize>::deserialize_reader(&mut encoded.as_slice()) else {
// We don't handle this error as we can't in this position
let _ = result.put_error(
INHERENT_ID,
&sp_inherents::MakeFatalError::from("invalid `SeraiPreExecutionDigest`"),
);
return result;
};
use frame_support::inherent::ProvideInherent;
match pallet_timestamp::Pallet::<Runtime>::check_inherent(
&pallet_timestamp::Call::<Runtime>::set { now: unix_time_in_millis },
&data
) {
Ok(()) => {},
Err(e) => {
let _ = result.put_error(sp_timestamp::INHERENT_IDENTIFIER, &e);
}
}
break 'outer;
}
_ => {}
}
}
let _ = result.put_error(
INHERENT_ID,
&sp_inherents::MakeFatalError::from("missing `SeraiPreExecutionDigest`")
);
}
result
}
}
@@ -505,7 +550,7 @@ sp_api::impl_runtime_apis! {
}
impl crate::SeraiApi<Block> for Runtime {
fn events() -> Vec<Vec<u8>> {
fn events() -> Vec<Vec<Vec<u8>>> {
Core::events()
}
fn validators(network: NetworkId) -> Vec<serai_abi::primitives::crypto::Public> {
@@ -522,6 +567,21 @@ sp_api::impl_runtime_apis! {
.map(|validator| validator.0.into())
.collect()
}
fn current_session(network: NetworkId) -> Option<Session> {
ValidatorSets::current_session(network)
}
fn current_stake(network: NetworkId) -> Option<Amount> {
ValidatorSets::stake_for_current_validator_set(network)
}
fn keys(set: ExternalValidatorSet) -> Option<serai_abi::primitives::crypto::KeyPair> {
ValidatorSets::oraclization_key(set)
.and_then(|oraclization_key| {
ValidatorSets::external_key(set)
.map(|external_key| {
serai_abi::primitives::crypto::KeyPair(oraclization_key.into(), external_key)
})
})
}
}
}
@@ -704,8 +764,6 @@ mod benches {
[system, SystemBench::<Runtime>]
[pallet_timestamp, Timestamp]
[balances, Balances]
[babe, Babe]

View File

@@ -21,10 +21,10 @@ workspace = true
[dependencies]
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }

View File

@@ -20,17 +20,17 @@ bitvec = { version = "1", default-features = false, features = ["alloc", "serde"
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-io = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5", default-features = false }
pallet-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
pallet-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "e01101b68c5b0f588dd4cdee48f801a2c1f75b84", default-features = false }
serai-abi = { path = "../abi", default-features = false, features = ["substrate"] }

View File

@@ -33,6 +33,9 @@ pub(crate) trait Keys {
/// The oraclization key for a validator set.
fn oraclization_key(set: ExternalValidatorSet) -> Option<Public>;
/// The external key for a validator set.
fn external_key(set: ExternalValidatorSet) -> Option<ExternalKey>;
}
impl<S: KeysStorage> Keys for S {
@@ -53,4 +56,8 @@ impl<S: KeysStorage> Keys for S {
fn oraclization_key(set: ExternalValidatorSet) -> Option<Public> {
S::OraclizationKeys::get(set)
}
fn external_key(set: ExternalValidatorSet) -> Option<ExternalKey> {
S::ExternalKeys::get(set)
}
}

View File

@@ -307,6 +307,14 @@ mod pallet {
Abstractions::<T>::selected_validators(set)
}
pub fn oraclization_key(set: ExternalValidatorSet) -> Option<Public> {
Abstractions::<T>::oraclization_key(set)
}
pub fn external_key(set: ExternalValidatorSet) -> Option<ExternalKey> {
Abstractions::<T>::external_key(set)
}
/* TODO
pub fn distribute_block_rewards(
network: NetworkId,