Skip to content

Commit

Permalink
more progress on runtime apis
Browse files Browse the repository at this point in the history
  • Loading branch information
Agusrodri committed Nov 26, 2024
1 parent b79e861 commit 8ccc5a7
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 93 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion pallets/external-validators-rewards/runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ workspace = true

[dependencies]
parity-scale-codec = { workspace = true }
snowbridge-outbound-queue-merkle-tree = { workspace = true }
sp-api = { workspace = true }
sp-core = { workspace = true }

[features]
default = [ "std" ]
std = [
"parity-scale-codec/std",
"snowbridge-outbound-queue-merkle-tree/std",
"sp-api/std",
"sp-core/std"
"sp-core/std",
]
10 changes: 7 additions & 3 deletions pallets/external-validators-rewards/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@

#![cfg_attr(not(feature = "std"), no_std)]

use snowbridge_outbound_queue_merkle_tree::MerkleProof;

sp_api::decl_runtime_apis! {
pub trait ExternalValidatorsRewardsApi<EraIndex>
pub trait ExternalValidatorsRewardsApi<AccountId, EraIndex>
where
AccountId: parity_scale_codec::Codec,
EraIndex: parity_scale_codec::Codec,
{
fn generate_rewards_merkle_proof(era_index: EraIndex) -> sp_core::H256;
fn generate_rewards_merkle_proof(account_id: AccountId, era_index: EraIndex) -> Option<MerkleProof>;
fn verify_rewards_merkle_proof(merkle_proof: MerkleProof) -> bool;
}
}
}
132 changes: 90 additions & 42 deletions pallets/external-validators-rewards/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,20 @@ use {
polkadot_primitives::ValidatorIndex,
runtime_parachains::session_info,
snowbridge_core::ChannelId,
snowbridge_outbound_queue_merkle_tree::merkle_root,
snowbridge_outbound_queue_merkle_tree::{merkle_proof, merkle_root, verify_proof, MerkleProof},
sp_core::H256,
sp_runtime::{DigestItem, traits::Hash},
sp_runtime::traits::Hash,
sp_staking::SessionIndex,
sp_std::collections::btree_set::BTreeSet,
sp_std::vec,
sp_std::vec::Vec,
tp_bridge::{Command, Message, ValidateMessage, DeliverMessage},
tp_bridge::{Command, DeliverMessage, Message, ValidateMessage},
};

#[frame_support::pallet]
pub mod pallet {
use {
super::*,
frame_support::pallet_prelude::*, sp_std::collections::btree_map::BTreeMap,
super::*, frame_support::pallet_prelude::*, sp_std::collections::btree_map::BTreeMap,
tp_traits::EraIndexProvider,
};

Expand Down Expand Up @@ -123,6 +122,63 @@ pub mod pallet {
}
})
}

pub fn generate_era_rewards_utils(
era_index: EraIndex,
maybe_account_id_check: Option<T::AccountId>,
) -> Option<(H256, Vec<H256>, Option<u64>, u128)> {
let era_rewards = RewardPointsForEra::<T>::get(&era_index);
let total_points: u128 = era_rewards.total as u128;
let mut leaves = Vec::with_capacity(era_rewards.individual.len());
let mut leaf_index = None;

for (index, (account_id, reward_points)) in era_rewards.individual.iter().enumerate() {
let encoded = (account_id, reward_points).encode();
let hashed = <T as Config>::Hashing::hash(&encoded);
leaves.push(hashed);

if let Some(ref check_account_id) = maybe_account_id_check {
if account_id == check_account_id {
leaf_index = Some(index as u64);
}
}
}

// If a specific account is checked but not found, return None
if maybe_account_id_check.is_some() && leaf_index.is_none() {
log::error!(
target: "ext_validators_rewards",
"AccountId {:?} not found for era {:?}!",
maybe_account_id_check,
era_index
);
return None;
}

let rewards_merkle_root =
merkle_root::<<T as Config>::Hashing, _>(leaves.iter().cloned());
Some((rewards_merkle_root, leaves, leaf_index, total_points))
}

pub fn generate_rewards_merkle_proof(
account_id: T::AccountId,
era_index: EraIndex,
) -> Option<MerkleProof> {
let (_, leaves, leaf_index, _) =
Self::generate_era_rewards_utils(era_index, Some(account_id))?;
leaf_index
.map(|index| merkle_proof::<<T as Config>::Hashing, _>(leaves.into_iter(), index))
}

pub fn verify_rewards_merkle_proof(merkle_proof: MerkleProof) -> bool {
verify_proof::<<T as Config>::Hashing, _, _>(
&merkle_proof.root,
merkle_proof.proof,
merkle_proof.number_of_leaves,
merkle_proof.leaf_index,
merkle_proof.leaf,
)
}
}

impl<T: Config> tp_traits::OnEraStart for Pallet<T> {
Expand All @@ -137,44 +193,36 @@ pub mod pallet {

impl<T: Config> tp_traits::OnEraEnd for Pallet<T> {
fn on_era_end(era_index: EraIndex) {
let era_rewards = RewardPointsForEra::<T>::get(&era_index);
let total_points: u128 = era_rewards.total as u128;
let mut rewards_info_to_merkelize: Vec<H256> = vec![];

for (account_id, reward_points) in era_rewards.individual {
let encoded = (account_id, reward_points).encode();
let hashed = <T as Config>::Hashing::hash(&encoded);
rewards_info_to_merkelize.push(hashed);
}

let rewards_merkle_root = merkle_root::<<T as Config>::Hashing, _>(rewards_info_to_merkelize.into_iter());

let command = Command::ReportRewards {
timestamp: T::TimestampProvider::get(),
era_index,
total_points,
// TODO: manage this in a proper way.
tokens_inflated: 0u128,
rewards_merkle_root
};

let channel_id: ChannelId = snowbridge_core::PRIMARY_GOVERNANCE_CHANNEL;

let outbound_message = Message {
id: None,
channel_id,
command,
};

// Validate and deliver the message
match T::ValidateMessage::validate(&outbound_message) {
Ok((ticket, _fee)) => {
if let Err(err) = T::OutboundQueue::deliver(ticket) {
log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue delivery of message failed. {err:?}");
if let Some((rewards_merkle_root, _, _, total_points)) =
Self::generate_era_rewards_utils(era_index, None)
{
let command = Command::ReportRewards {
timestamp: T::TimestampProvider::get(),
era_index,
total_points,
// TODO: manage this in a proper way.
tokens_inflated: 0u128,
rewards_merkle_root,
};

let channel_id: ChannelId = snowbridge_core::PRIMARY_GOVERNANCE_CHANNEL;

let outbound_message = Message {
id: None,
channel_id,
command,
};

// Validate and deliver the message
match T::ValidateMessage::validate(&outbound_message) {
Ok((ticket, _fee)) => {
if let Err(err) = T::OutboundQueue::deliver(ticket) {
log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue delivery of message failed. {err:?}");
}
}
Err(err) => {
log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed. {err:?}");
}
},
Err(err) => {
log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed. {err:?}");
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion primitives/bridge/src/custom_do_process_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl ConstantGasMeter {
fn maximum_dispatch_gas_used_at_most(command: &Command) -> u64 {
match command {
Command::Test { .. } => 60_000,
Command::ReportRewards { .. } => 60_000
Command::ReportRewards { .. } => 60_000,
}
}
}
25 changes: 18 additions & 7 deletions primitives/bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ pub enum Command {
timestamp: u64,
// index of the era we are sending info of
era_index: u32,
// total_points for the era
// total_points for the era
total_points: u128,
// new tokens inflated during the era
tokens_inflated: u128,
// merkle root of vec![(validatorId, rewardPoints)]
rewards_merkle_root: H256
}
rewards_merkle_root: H256,
},
}

impl Command {
Expand All @@ -86,14 +86,26 @@ impl Command {
match self {
Command::Test(payload) => {
ethabi::encode(&[Token::Tuple(vec![Token::Bytes(payload.clone())])])
},
Command::ReportRewards { timestamp, era_index, total_points, tokens_inflated, rewards_merkle_root } => {
}
Command::ReportRewards {
timestamp,
era_index,
total_points,
tokens_inflated,
rewards_merkle_root,
} => {
let timestamp_token = Token::Uint(U256::from(*timestamp));
let era_index_token = Token::Uint(U256::from(*era_index));
let total_points_token = Token::Uint(U256::from(*total_points));
let tokens_inflated_token = Token::Uint(U256::from(*tokens_inflated));
let rewards_mr_token = Token::FixedBytes(rewards_merkle_root.0.to_vec());
ethabi::encode(&[Token::Tuple(vec![timestamp_token, era_index_token, total_points_token, tokens_inflated_token, rewards_mr_token])])
ethabi::encode(&[Token::Tuple(vec![
timestamp_token,
era_index_token,
total_points_token,
tokens_inflated_token,
rewards_mr_token,
])])
}
}
}
Expand Down Expand Up @@ -231,4 +243,3 @@ pub trait DeliverMessage {

fn deliver(ticket: Self::Ticket) -> Result<H256, SendError>;
}

14 changes: 9 additions & 5 deletions solo-chains/runtime/dancelight/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ use {
scale_info::TypeInfo,
serde::{Deserialize, Serialize},
snowbridge_core::ChannelId,
snowbridge_pallet_outbound_queue::MerkleProof,
sp_core::{storage::well_known_keys as StorageWellKnownKeys, Get},
sp_genesis_builder::PresetId,
sp_runtime::{
Expand Down Expand Up @@ -2706,13 +2707,16 @@ sp_api::impl_runtime_apis! {
}
}

impl pallet_external_validators_rewards_runtime_api::ExternalValidatorsRewardsApi<Block, EraIndex> for Runtime
impl pallet_external_validators_rewards_runtime_api::ExternalValidatorsRewardsApi<Block, AccountId, EraIndex> for Runtime
where
EraIndex: parity_scale_codec::Codec,
EraIndex: parity_scale_codec::Codec,
{
fn generate_rewards_merkle_proof(era_index: EraIndex) -> H256 {
//ExternalValidatorsRewards::get_rewards_merkle_root_and_total_points(era_index).0
H256::default()
fn generate_rewards_merkle_proof(account_id: AccountId, era_index: EraIndex) -> Option<MerkleProof> {
ExternalValidatorsRewards::generate_rewards_merkle_proof(account_id, era_index)
}

fn verify_rewards_merkle_proof(merkle_proof: MerkleProof) -> bool {
ExternalValidatorsRewards::verify_rewards_merkle_proof(merkle_proof)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@

use {
crate::{
tests::common::*, EthereumSystem, ExternalValidators, ExternalValidatorsRewards, MaxExternalValidators, RuntimeEvent, SessionKeys, SessionsPerEra, System
}, frame_support::{assert_ok, traits::fungible::Mutate}, pallet_external_validators::Forcing, parity_scale_codec::Encode,
snowbridge_core::{Channel, PRIMARY_GOVERNANCE_CHANNEL},
sp_core::H256, sp_io::hashing::twox_64, std::{collections::HashMap, ops::RangeInclusive}, tp_traits::OnEraEnd
tests::common::*, EthereumSystem, ExternalValidators, ExternalValidatorsRewards,
MaxExternalValidators, RuntimeEvent, SessionKeys, SessionsPerEra, System,
},
frame_support::{assert_ok, traits::fungible::Mutate},
pallet_external_validators::Forcing,
parity_scale_codec::Encode,
snowbridge_core::{Channel, PRIMARY_GOVERNANCE_CHANNEL},
sp_core::H256,
sp_io::hashing::twox_64,
std::{collections::HashMap, ops::RangeInclusive},
tp_traits::OnEraEnd,
};

fn assert_validators_do_not_change(
Expand Down Expand Up @@ -714,27 +721,35 @@ fn external_validators_rewards_sends_message_on_era_end() {
combined_channel_id_key.extend_from_slice(PRIMARY_GOVERNANCE_CHANNEL.as_ref());

let mut full_storage_key = Vec::new();
full_storage_key.extend_from_slice(&frame_support::storage::storage_prefix(b"EthereumSystem", b"Channels"));
full_storage_key.extend_from_slice(&frame_support::storage::storage_prefix(
b"EthereumSystem",
b"Channels",
));
full_storage_key.extend_from_slice(&combined_channel_id_key);

let channel = Channel {
agent_id: H256::default(),
para_id: 1000u32.into()
para_id: 1000u32.into(),
};

frame_support::storage::unhashed::put(&full_storage_key, &channel);

// This will call on_era_end for era 0
run_to_session(sessions_per_era);

let outbound_msg_queue_event = System::events()
.iter()
.filter(|r| match r.event {
RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued { .. }) => true,
_ => false,
})
.count();

assert_eq!(outbound_msg_queue_event, 1, "MessageQueued event should be emitted");
.iter()
.filter(|r| match r.event {
RuntimeEvent::EthereumOutboundQueue(
snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
) => true,
_ => false,
})
.count();

assert_eq!(
outbound_msg_queue_event, 1,
"MessageQueued event should be emitted"
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describeSuite({
title: "Should succeed calling runtime api",
test: async function () {

await polkadotJs.call.externalValidatorsRewardsApi.generate_rewards_merkle_root(0);
await polkadotJs.call.externalValidatorsRewardsApi.generate_rewards_merkle_proof(0);

},
});
Expand Down
Loading

0 comments on commit 8ccc5a7

Please sign in to comment.