From 0f2f8d16427641c8f4e6e346bffc1f72d9dfd65b Mon Sep 17 00:00:00 2001 From: kylezs Date: Mon, 25 Sep 2023 15:41:32 +0200 Subject: [PATCH] feat: add initiated_at block number for egresses --- .../pallets/cf-broadcast/src/benchmarking.rs | 21 +++++++---- state-chain/pallets/cf-broadcast/src/lib.rs | 35 +++++++++++++++---- state-chain/pallets/cf-broadcast/src/mock.rs | 6 +++- state-chain/pallets/cf-broadcast/src/tests.rs | 1 + state-chain/runtime/src/lib.rs | 3 ++ 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/state-chain/pallets/cf-broadcast/src/benchmarking.rs b/state-chain/pallets/cf-broadcast/src/benchmarking.rs index 73f0e42c07..e9564119a3 100644 --- a/state-chain/pallets/cf-broadcast/src/benchmarking.rs +++ b/state-chain/pallets/cf-broadcast/src/benchmarking.rs @@ -34,6 +34,10 @@ fn insert_transaction_broadcast_attempt, I: 'static>( ); } +const INITIATED_AT: u32 = 100; + +pub type AggKeyFor = <<>::TargetChain as cf_chains::Chain>::ChainCrypto as ChainCrypto>::AggKey; + // Generates a new signature ready call. fn generate_on_signature_ready_call, I>() -> pallet::Call { let threshold_request_id = 1; @@ -46,6 +50,7 @@ fn generate_on_signature_ready_call, I>() -> pallet::Call::benchmark_value(), api_call: Box::new(ApiCallFor::::benchmark_value()), broadcast_id: 1, + initiated_at: INITIATED_AT.into(), } } @@ -61,7 +66,7 @@ benchmarks_instance_pallet! { Timeouts::::append(timeout_block, broadcast_attempt_id); ThresholdSignatureData::::insert(i, (ApiCallFor::::benchmark_value(), ThresholdSignatureFor::::benchmark_value())) } - let valid_key = <<>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value(); + let valid_key = AggKeyFor::::benchmark_value(); T::KeyProvider::set_key(valid_key); } : { Pallet::::on_initialize(timeout_block); @@ -79,7 +84,7 @@ benchmarks_instance_pallet! { insert_transaction_broadcast_attempt::(caller.clone().into(), broadcast_attempt_id); generate_on_signature_ready_call::().dispatch_bypass_filter(T::EnsureThresholdSigned::try_successful_origin().unwrap())?; let expiry_block = frame_system::Pallet::::block_number() + T::BroadcastTimeout::get(); - let valid_key = <<>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value(); + let valid_key = AggKeyFor::::benchmark_value(); T::KeyProvider::set_key(valid_key); }: _(RawOrigin::Signed(caller), broadcast_attempt_id) verify { @@ -94,7 +99,7 @@ benchmarks_instance_pallet! { }; insert_transaction_broadcast_attempt::(whitelisted_caller(), broadcast_attempt_id); let call = generate_on_signature_ready_call::(); - let valid_key = <<>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value(); + let valid_key = AggKeyFor::::benchmark_value(); T::KeyProvider::set_key(valid_key); } : { call.dispatch_bypass_filter(T::EnsureThresholdSigned::try_successful_origin().unwrap())? } verify { @@ -110,10 +115,11 @@ benchmarks_instance_pallet! { BenchmarkValue::benchmark_value(), signed_api_call, BenchmarkValue::benchmark_value(), - 1 + 1, + INITIATED_AT.into(), ); - T::KeyProvider::set_key(<<>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value()); + T::KeyProvider::set_key(AggKeyFor::::benchmark_value()); let transaction_payload = TransactionFor::::benchmark_value(); } : { @@ -130,7 +136,8 @@ benchmarks_instance_pallet! { transaction_succeeded { let caller: T::AccountId = whitelisted_caller(); let signer_id = SignerIdFor::::benchmark_value(); - TransactionOutIdToBroadcastId::::insert(TransactionOutIdFor::::benchmark_value(), 1); + let initiated_at: ChainBlockNumberFor = INITIATED_AT.into(); + TransactionOutIdToBroadcastId::::insert(TransactionOutIdFor::::benchmark_value(), (1, initiated_at)); let broadcast_attempt_id = BroadcastAttemptId { broadcast_id: 1, @@ -142,7 +149,7 @@ benchmarks_instance_pallet! { signer_id, tx_fee: TransactionFeeFor::::benchmark_value(), }; - let valid_key = <<>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value(); + let valid_key = AggKeyFor::::benchmark_value(); T::KeyProvider::set_key(valid_key); } : { call.dispatch_bypass_filter(T::EnsureWitnessedAtCurrentEpoch::try_successful_origin().unwrap())? } verify { diff --git a/state-chain/pallets/cf-broadcast/src/lib.rs b/state-chain/pallets/cf-broadcast/src/lib.rs index fd3c0c0923..5f18824bb2 100644 --- a/state-chain/pallets/cf-broadcast/src/lib.rs +++ b/state-chain/pallets/cf-broadcast/src/lib.rs @@ -8,7 +8,7 @@ mod tests; pub mod weights; use cf_primitives::{BroadcastId, ThresholdSignatureRequestId}; -use cf_traits::impl_pallet_safe_mode; +use cf_traits::{impl_pallet_safe_mode, GetBlockHeight}; pub use weights::WeightInfo; impl_pallet_safe_mode!(PalletSafeMode; retry_enabled); @@ -179,6 +179,9 @@ pub mod pallet { type BroadcastReadyProvider: OnBroadcastReady; + /// Get the latest block height of the target chain via Chain Tracking. + type ChainTracking: GetBlockHeight; + /// The timeout duration for the broadcast, measured in number of blocks. #[pallet::constant] type BroadcastTimeout: Get>; @@ -237,8 +240,13 @@ pub mod pallet { /// Lookup table between TransactionOutId -> Broadcast. #[pallet::storage] - pub type TransactionOutIdToBroadcastId, I: 'static = ()> = - StorageMap<_, Twox64Concat, TransactionOutIdFor, BroadcastId, OptionQuery>; + pub type TransactionOutIdToBroadcastId, I: 'static = ()> = StorageMap< + _, + Twox64Concat, + TransactionOutIdFor, + (BroadcastId, ChainBlockNumberFor), + OptionQuery, + >; /// The list of failed broadcasts pending retry. #[pallet::storage] @@ -452,6 +460,7 @@ pub mod pallet { threshold_signature_payload: PayloadFor, api_call: Box<>::ApiCall>, broadcast_id: BroadcastId, + initiated_at: ChainBlockNumberFor, ) -> DispatchResultWithPostInfo { let _ = T::EnsureThresholdSigned::ensure_origin(origin)?; @@ -474,6 +483,7 @@ pub mod pallet { signed_api_call, threshold_signature_payload, broadcast_id, + initiated_at, ); Ok(().into()) } @@ -502,8 +512,9 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { T::EnsureWitnessed::ensure_origin(origin.clone())?; - let broadcast_id = TransactionOutIdToBroadcastId::::take(&tx_out_id) - .ok_or(Error::::InvalidPayload)?; + let (broadcast_id, _initiated_at) = + TransactionOutIdToBroadcastId::::take(&tx_out_id) + .ok_or(Error::::InvalidPayload)?; let to_refund = AwaitingBroadcast::::get(BroadcastAttemptId { broadcast_id, @@ -616,6 +627,13 @@ impl, I: 'static> Pallet { if let Some(callback) = maybe_callback { RequestCallbacks::::insert(broadcast_id, callback); } + + // We must set this here because after the threshold signature is requested, it's + // possible that an authority submits the transaction themselves, not going through the + // standard path. This protects against that, to ensure we always set the earliest possible + // block number we could have broadcast at, so that we can ensure we witness it. + let initiated_at = T::ChainTracking::get_block_height(); + let threshold_signature_payload = api_call.threshold_signature_payload(); let signature_request_id = T::ThresholdSigner::request_signature_with_callback( threshold_signature_payload.clone(), @@ -625,6 +643,7 @@ impl, I: 'static> Pallet { threshold_signature_payload, api_call: Box::new(api_call), broadcast_id, + initiated_at, } .into() }, @@ -643,12 +662,16 @@ impl, I: 'static> Pallet { api_call: >::ApiCall, threshold_signature_payload: <::ChainCrypto as ChainCrypto>::Payload, broadcast_id: BroadcastId, + initiated_at: ChainBlockNumberFor, ) -> BroadcastAttemptId { let transaction_out_id = api_call.transaction_out_id(); T::BroadcastReadyProvider::on_broadcast_ready(&api_call); - TransactionOutIdToBroadcastId::::insert(&transaction_out_id, broadcast_id); + TransactionOutIdToBroadcastId::::insert( + &transaction_out_id, + (broadcast_id, initiated_at), + ); ThresholdSignatureData::::insert(broadcast_id, (api_call, signature)); diff --git a/state-chain/pallets/cf-broadcast/src/mock.rs b/state-chain/pallets/cf-broadcast/src/mock.rs index a3a52538c3..a56c03096c 100644 --- a/state-chain/pallets/cf-broadcast/src/mock.rs +++ b/state-chain/pallets/cf-broadcast/src/mock.rs @@ -13,7 +13,10 @@ use cf_chains::{ }; use cf_traits::{ impl_mock_chainflip, impl_mock_runtime_safe_mode, - mocks::{signer_nomination::MockNominator, threshold_signer::MockThresholdSigner}, + mocks::{ + block_height_provider::BlockHeightProvider, signer_nomination::MockNominator, + threshold_signer::MockThresholdSigner, + }, AccountRoleRegistry, EpochKey, KeyState, OnBroadcastReady, }; use codec::{Decode, Encode}; @@ -152,6 +155,7 @@ impl pallet_cf_broadcast::Config for Test { type SafeMode = MockRuntimeSafeMode; type BroadcastReadyProvider = MockBroadcastReadyProvider; type SafeModeBlockMargin = ConstU64<10>; + type ChainTracking = BlockHeightProvider; } impl_mock_chainflip!(Test); diff --git a/state-chain/pallets/cf-broadcast/src/tests.rs b/state-chain/pallets/cf-broadcast/src/tests.rs index 87ea7dd75b..3c9d89c36e 100644 --- a/state-chain/pallets/cf-broadcast/src/tests.rs +++ b/state-chain/pallets/cf-broadcast/src/tests.rs @@ -128,6 +128,7 @@ fn start_mock_broadcast_tx_out_id( MockApiCall { tx_out_id, payload: Default::default(), sig: Default::default() }, Default::default(), 1, + 100u64.into(), ) } diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 83ab32f5a6..3d11882869 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -682,6 +682,7 @@ impl pallet_cf_broadcast::Config for Runtime { type SafeMode = RuntimeSafeMode; type SafeModeBlockMargin = ConstU32<10>; type KeyProvider = EthereumVault; + type ChainTracking = EthereumChainTracking; } impl pallet_cf_broadcast::Config for Runtime { @@ -704,6 +705,7 @@ impl pallet_cf_broadcast::Config for Runtime { type SafeMode = RuntimeSafeMode; type SafeModeBlockMargin = ConstU32<10>; type KeyProvider = PolkadotVault; + type ChainTracking = PolkadotChainTracking; } impl pallet_cf_broadcast::Config for Runtime { @@ -726,6 +728,7 @@ impl pallet_cf_broadcast::Config for Runtime { type SafeMode = RuntimeSafeMode; type SafeModeBlockMargin = ConstU32<10>; type KeyProvider = BitcoinVault; + type ChainTracking = BitcoinChainTracking; } impl pallet_cf_chain_tracking::Config for Runtime {