Snowbridge: Synchronize from Snowfork repository (#3761)

This PR includes the following 2 improvements:

## Ethereum Client

Author: @yrong 
### Original Upstream PRs
- https://github.com/Snowfork/polkadot-sdk/pull/123
- https://github.com/Snowfork/polkadot-sdk/pull/125

### Description
The Ethereum client syncs beacon headers as they are finalized, and
imports every execution header. When a message is received, it is
verified against the import execution header. This is unnecessary, since
the execution header can be sent with the message as proof. The recent
Deneb Ethereum upgrade made it easier to locate the relevant beacon
header from an execution header, and so this improvement was made
possible. This resolves a concern @svyatonik had in our initial Rococo
PR:
https://github.com/paritytech/polkadot-sdk/pull/2522#discussion_r1431270691

## Inbound Queue

Author: @yrong 
### Original Upstream PR
- https://github.com/Snowfork/polkadot-sdk/pull/118

### Description
When the AH sovereign account (who pays relayer rewards) is depleted,
the inbound message will not fail. The relayer just will not receive
rewards.

Both these changes were done by @yrong, many thanks. ❤️

---------

Co-authored-by: claravanstaden <Cats 4 life!>
Co-authored-by: Ron <yrong1997@gmail.com>
Co-authored-by: Vincent Geddes <vincent@snowfork.com>
Co-authored-by: Svyatoslav Nikolsky <svyatonik@gmail.com>
This commit is contained in:
Clara van Staden
2024-04-02 15:53:05 +02:00
committed by GitHub
parent e54279699b
commit 5d9826c262
35 changed files with 1123 additions and 1307 deletions
@@ -19,8 +19,8 @@ mod benchmarks {
let create_message = make_register_token_message();
T::Helper::initialize_storage(
create_message.message.proof.block_hash,
create_message.execution_header,
create_message.finalized_header,
create_message.block_roots_root,
);
let sovereign_account = sibling_sovereign_account::<T>(1000u32.into());
@@ -28,9 +28,6 @@ mod envelope;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(feature = "runtime-benchmarks")]
use snowbridge_beacon_primitives::CompactExecutionHeader;
pub mod weights;
#[cfg(test)]
@@ -44,7 +41,7 @@ use envelope::Envelope;
use frame_support::{
traits::{
fungible::{Inspect, Mutate},
tokens::Preservation,
tokens::{Fortitude, Preservation},
},
weights::WeightToFee,
PalletError,
@@ -52,6 +49,7 @@ use frame_support::{
use frame_system::ensure_signed;
use scale_info::TypeInfo;
use sp_core::{H160, H256};
use sp_runtime::traits::Zero;
use sp_std::{convert::TryFrom, vec};
use xcm::prelude::{
send_xcm, Instruction::SetTopic, Junction::*, Location, SendError as XcmpSendError, SendXcm,
@@ -72,6 +70,9 @@ use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError};
pub use weights::WeightInfo;
#[cfg(feature = "runtime-benchmarks")]
use snowbridge_beacon_primitives::BeaconHeader;
type BalanceOf<T> =
<<T as pallet::Config>::Token as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
@@ -91,7 +92,7 @@ pub mod pallet {
#[cfg(feature = "runtime-benchmarks")]
pub trait BenchmarkHelper<T> {
fn initialize_storage(block_hash: H256, header: CompactExecutionHeader);
fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256);
}
#[pallet::config]
@@ -261,11 +262,19 @@ pub mod pallet {
}
})?;
// Reward relayer from the sovereign account of the destination parachain
// Expected to fail if sovereign account has no funds
// Reward relayer from the sovereign account of the destination parachain, only if funds
// are available
let sovereign_account = sibling_sovereign_account::<T>(channel.para_id);
let delivery_cost = Self::calculate_delivery_cost(message.encode().len() as u32);
T::Token::transfer(&sovereign_account, &who, delivery_cost, Preservation::Preserve)?;
let amount = T::Token::reducible_balance(
&sovereign_account,
Preservation::Preserve,
Fortitude::Polite,
)
.min(delivery_cost);
if !amount.is_zero() {
T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?;
}
// Decode message into XCM
let (xcm, fee) =
@@ -4,11 +4,13 @@ use super::*;
use frame_support::{
derive_impl, parameter_types,
traits::{ConstU128, ConstU32, Everything},
traits::{ConstU32, Everything},
weights::IdentityFee,
};
use hex_literal::hex;
use snowbridge_beacon_primitives::{Fork, ForkVersions};
use snowbridge_beacon_primitives::{
types::deneb, BeaconHeader, ExecutionProof, Fork, ForkVersions, VersionedExecutionPayloadHeader,
};
use snowbridge_core::{
gwei,
inbound::{Log, Proof, VerificationError},
@@ -20,7 +22,7 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify},
BuildStorage, FixedU128, MultiSignature,
};
use sp_std::convert::From;
use sp_std::{convert::From, default::Default};
use xcm::{latest::SendXcm, prelude::*};
use xcm_executor::AssetsInHolding;
@@ -65,6 +67,10 @@ impl frame_system::Config for Test {
type Block = Block;
}
parameter_types! {
pub const ExistentialDeposit: u128 = 1;
}
impl pallet_balances::Config for Test {
type MaxLocks = ();
type MaxReserves = ();
@@ -72,7 +78,7 @@ impl pallet_balances::Config for Test {
type Balance = Balance;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ConstU128<1>;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
type FreezeIdentifier = ();
@@ -82,7 +88,6 @@ impl pallet_balances::Config for Test {
}
parameter_types! {
pub const ExecutionHeadersPruneThreshold: u32 = 10;
pub const ChainForkVersions: ForkVersions = ForkVersions{
genesis: Fork {
version: [0, 0, 0, 1], // 0x00000001
@@ -110,7 +115,6 @@ parameter_types! {
impl snowbridge_pallet_ethereum_client::Config for Test {
type RuntimeEvent = RuntimeEvent;
type ForkVersions = ChainForkVersions;
type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold;
type WeightInfo = ();
}
@@ -139,7 +143,7 @@ parameter_types! {
#[cfg(feature = "runtime-benchmarks")]
impl<T: snowbridge_pallet_ethereum_client::Config> BenchmarkHelper<T> for Test {
// not implemented since the MockVerifier is used for tests
fn initialize_storage(_: H256, _: CompactExecutionHeader) {}
fn initialize_storage(_: BeaconHeader, _: H256) {}
}
// Mock XCM sender that always succeeds
@@ -335,5 +339,32 @@ pub fn mock_event_log_invalid_gateway() -> Log {
}
}
pub fn mock_execution_proof() -> ExecutionProof {
ExecutionProof {
header: BeaconHeader::default(),
ancestry_proof: None,
execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader {
parent_hash: Default::default(),
fee_recipient: Default::default(),
state_root: Default::default(),
receipts_root: Default::default(),
logs_bloom: vec![],
prev_randao: Default::default(),
block_number: 0,
gas_limit: 0,
gas_used: 0,
timestamp: 0,
extra_data: vec![],
base_fee_per_gas: Default::default(),
block_hash: Default::default(),
transactions_root: Default::default(),
withdrawals_root: Default::default(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
execution_branch: vec![],
}
}
pub const ASSET_HUB_PARAID: u32 = 1000u32;
pub const TEMPLATE_PARAID: u32 = 1001u32;
@@ -6,7 +6,7 @@ use frame_support::{assert_noop, assert_ok};
use hex_literal::hex;
use snowbridge_core::{inbound::Proof, ChannelId};
use sp_keyring::AccountKeyring as Keyring;
use sp_runtime::{DispatchError, TokenError};
use sp_runtime::DispatchError;
use sp_std::convert::From;
use crate::{Error, Event as InboundQueueEvent};
@@ -25,9 +25,8 @@ fn test_submit_happy_path() {
let message = Message {
event_log: mock_event_log(),
proof: Proof {
block_hash: Default::default(),
tx_index: Default::default(),
data: Default::default(),
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
@@ -77,9 +76,8 @@ fn test_submit_xcm_invalid_channel() {
let message = Message {
event_log: mock_event_log_invalid_channel(),
proof: Proof {
block_hash: Default::default(),
tx_index: Default::default(),
data: Default::default(),
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
assert_noop!(
@@ -103,9 +101,8 @@ fn test_submit_with_invalid_gateway() {
let message = Message {
event_log: mock_event_log_invalid_gateway(),
proof: Proof {
block_hash: Default::default(),
tx_index: Default::default(),
data: Default::default(),
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
assert_noop!(
@@ -129,9 +126,8 @@ fn test_submit_with_invalid_nonce() {
let message = Message {
event_log: mock_event_log(),
proof: Proof {
block_hash: Default::default(),
tx_index: Default::default(),
data: Default::default(),
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
assert_ok!(InboundQueue::submit(origin.clone(), message.clone()));
@@ -150,12 +146,12 @@ fn test_submit_with_invalid_nonce() {
}
#[test]
fn test_submit_no_funds_to_reward_relayers() {
fn test_submit_no_funds_to_reward_relayers_just_ignore() {
new_tester().execute_with(|| {
let relayer: AccountId = Keyring::Bob.into();
let origin = RuntimeOrigin::signed(relayer);
// Reset balance of sovereign_account to zero so to trigger the FundsUnavailable error
// Reset balance of sovereign_account to zero first
let sovereign_account = sibling_sovereign_account::<Test>(ASSET_HUB_PARAID.into());
Balances::set_balance(&sovereign_account, 0);
@@ -163,15 +159,12 @@ fn test_submit_no_funds_to_reward_relayers() {
let message = Message {
event_log: mock_event_log(),
proof: Proof {
block_hash: Default::default(),
tx_index: Default::default(),
data: Default::default(),
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
assert_noop!(
InboundQueue::submit(origin.clone(), message.clone()),
TokenError::FundsUnavailable
);
// Check submit successfully in case no funds available
assert_ok!(InboundQueue::submit(origin.clone(), message.clone()));
});
}
@@ -183,9 +176,8 @@ fn test_set_operating_mode() {
let message = Message {
event_log: mock_event_log(),
proof: Proof {
block_hash: Default::default(),
tx_index: Default::default(),
data: Default::default(),
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
@@ -210,3 +202,44 @@ fn test_set_operating_mode_root_only() {
);
});
}
#[test]
fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() {
new_tester().execute_with(|| {
let relayer: AccountId = Keyring::Bob.into();
let origin = RuntimeOrigin::signed(relayer);
// Reset balance of sovereign account to (ED+1) first
let sovereign_account = sibling_sovereign_account::<Test>(ASSET_HUB_PARAID.into());
Balances::set_balance(&sovereign_account, ExistentialDeposit::get() + 1);
// Submit message successfully
let message = Message {
event_log: mock_event_log(),
proof: Proof {
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
assert_ok!(InboundQueue::submit(origin.clone(), message.clone()));
// Check balance of sovereign account to ED
let amount = Balances::balance(&sovereign_account);
assert_eq!(amount, ExistentialDeposit::get());
// Submit another message with nonce set as 2
let mut event_log = mock_event_log();
event_log.data[31] = 2;
let message = Message {
event_log,
proof: Proof {
receipt_proof: Default::default(),
execution_proof: mock_execution_proof(),
},
};
assert_ok!(InboundQueue::submit(origin.clone(), message.clone()));
// Check balance of sovereign account as ED does not change
let amount = Balances::balance(&sovereign_account);
assert_eq!(amount, ExistentialDeposit::get());
});
}