Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add initiated_at block number for egresses #4046

Merged
merged 3 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions state-chain/pallets/cf-broadcast/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ fn insert_transaction_broadcast_attempt<T: pallet::Config<I>, I: 'static>(
);
}

const INITIATED_AT: u32 = 100;

pub type AggKeyFor<T, I> = <<<T as pallet::Config<I>>::TargetChain as cf_chains::Chain>::ChainCrypto as ChainCrypto>::AggKey;

// Generates a new signature ready call.
fn generate_on_signature_ready_call<T: pallet::Config<I>, I>() -> pallet::Call<T, I> {
let threshold_request_id = 1;
Expand All @@ -46,6 +50,7 @@ fn generate_on_signature_ready_call<T: pallet::Config<I>, I>() -> pallet::Call<T
threshold_signature_payload: PayloadFor::<T, I>::benchmark_value(),
api_call: Box::new(ApiCallFor::<T, I>::benchmark_value()),
broadcast_id: 1,
initiated_at: INITIATED_AT.into(),
}
}

Expand All @@ -61,7 +66,7 @@ benchmarks_instance_pallet! {
Timeouts::<T, I>::append(timeout_block, broadcast_attempt_id);
ThresholdSignatureData::<T, I>::insert(i, (ApiCallFor::<T, I>::benchmark_value(), ThresholdSignatureFor::<T, I>::benchmark_value()))
}
let valid_key = <<<T as Config<I>>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value();
let valid_key = AggKeyFor::<T, I>::benchmark_value();
T::KeyProvider::set_key(valid_key);
} : {
Pallet::<T, I>::on_initialize(timeout_block);
Expand All @@ -79,7 +84,7 @@ benchmarks_instance_pallet! {
insert_transaction_broadcast_attempt::<T, I>(caller.clone().into(), broadcast_attempt_id);
generate_on_signature_ready_call::<T, I>().dispatch_bypass_filter(T::EnsureThresholdSigned::try_successful_origin().unwrap())?;
let expiry_block = frame_system::Pallet::<T>::block_number() + T::BroadcastTimeout::get();
let valid_key = <<<T as Config<I>>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value();
let valid_key = AggKeyFor::<T, I>::benchmark_value();
T::KeyProvider::set_key(valid_key);
}: _(RawOrigin::Signed(caller), broadcast_attempt_id)
verify {
Expand All @@ -94,7 +99,7 @@ benchmarks_instance_pallet! {
};
insert_transaction_broadcast_attempt::<T, I>(whitelisted_caller(), broadcast_attempt_id);
let call = generate_on_signature_ready_call::<T, I>();
let valid_key = <<<T as Config<I>>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value();
let valid_key = AggKeyFor::<T, I>::benchmark_value();
T::KeyProvider::set_key(valid_key);
} : { call.dispatch_bypass_filter(T::EnsureThresholdSigned::try_successful_origin().unwrap())? }
verify {
Expand All @@ -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(<<<T as Config<I>>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value());
T::KeyProvider::set_key(AggKeyFor::<T, I>::benchmark_value());
let transaction_payload = TransactionFor::<T, I>::benchmark_value();

} : {
Expand All @@ -130,7 +136,8 @@ benchmarks_instance_pallet! {
transaction_succeeded {
let caller: T::AccountId = whitelisted_caller();
let signer_id = SignerIdFor::<T, I>::benchmark_value();
TransactionOutIdToBroadcastId::<T, I>::insert(TransactionOutIdFor::<T, I>::benchmark_value(), 1);
let initiated_at: ChainBlockNumberFor<T, I> = INITIATED_AT.into();
TransactionOutIdToBroadcastId::<T, I>::insert(TransactionOutIdFor::<T, I>::benchmark_value(), (1, initiated_at));

let broadcast_attempt_id = BroadcastAttemptId {
broadcast_id: 1,
Expand All @@ -142,7 +149,7 @@ benchmarks_instance_pallet! {
signer_id,
tx_fee: TransactionFeeFor::<T, I>::benchmark_value(),
};
let valid_key = <<<T as Config<I>>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::AggKey::benchmark_value();
let valid_key = AggKeyFor::<T, I>::benchmark_value();
T::KeyProvider::set_key(valid_key);
} : { call.dispatch_bypass_filter(T::EnsureWitnessedAtCurrentEpoch::try_successful_origin().unwrap())? }
verify {
Expand Down
55 changes: 48 additions & 7 deletions state-chain/pallets/cf-broadcast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ mod benchmarking;
mod mock;
mod tests;

pub mod migrations;
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);
Expand All @@ -23,7 +24,7 @@ use frame_support::{
dispatch::DispatchResultWithPostInfo,
pallet_prelude::DispatchResult,
sp_runtime::traits::Saturating,
traits::{Get, UnfilteredDispatchable},
traits::{Get, OnRuntimeUpgrade, StorageVersion, UnfilteredDispatchable},
Twox64Concat,
};

Expand Down Expand Up @@ -66,6 +67,8 @@ pub enum PalletOffence {
FailedToBroadcastTransaction,
}

pub const PALLET_VERSION: StorageVersion = StorageVersion::new(1);

#[frame_support::pallet]
pub mod pallet {
use super::*;
Expand Down Expand Up @@ -179,6 +182,9 @@ pub mod pallet {

type BroadcastReadyProvider: OnBroadcastReady<Self::TargetChain, ApiCall = Self::ApiCall>;

/// Get the latest block height of the target chain via Chain Tracking.
type ChainTracking: GetBlockHeight<Self::TargetChain>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: we should consider adding ChainTracking to the Chainflip trait - it's getting its tentacles in everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This made me think that maybe it makes sense to merge the broadcast and ingress-egress pallet actually... after all, the broadcast is egressing transactions... something we can think about anyway.


/// The timeout duration for the broadcast, measured in number of blocks.
#[pallet::constant]
type BroadcastTimeout: Get<BlockNumberFor<Self>>;
Expand All @@ -202,6 +208,7 @@ pub mod pallet {
pub struct Origin<T: Config<I>, I: 'static = ()>(pub(super) PhantomData<(T, I)>);

#[pallet::pallet]
#[pallet::storage_version(PALLET_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);

Expand Down Expand Up @@ -237,8 +244,13 @@ pub mod pallet {

/// Lookup table between TransactionOutId -> Broadcast.
#[pallet::storage]
pub type TransactionOutIdToBroadcastId<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, TransactionOutIdFor<T, I>, BroadcastId, OptionQuery>;
pub type TransactionOutIdToBroadcastId<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Twox64Concat,
TransactionOutIdFor<T, I>,
(BroadcastId, ChainBlockNumberFor<T, I>),
OptionQuery,
>;

/// The list of failed broadcasts pending retry.
#[pallet::storage]
Expand Down Expand Up @@ -368,6 +380,20 @@ pub mod pallet {
Weight::zero()
}
}

fn on_runtime_upgrade() -> Weight {
migrations::PalletMigration::<T, I>::on_runtime_upgrade()
}

#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, DispatchError> {
migrations::PalletMigration::<T, I>::pre_upgrade()
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(state: sp_std::vec::Vec<u8>) -> Result<(), DispatchError> {
migrations::PalletMigration::<T, I>::post_upgrade(state)
}
}

#[pallet::call]
Expand Down Expand Up @@ -452,6 +478,7 @@ pub mod pallet {
threshold_signature_payload: PayloadFor<T, I>,
api_call: Box<<T as Config<I>>::ApiCall>,
broadcast_id: BroadcastId,
initiated_at: ChainBlockNumberFor<T, I>,
) -> DispatchResultWithPostInfo {
let _ = T::EnsureThresholdSigned::ensure_origin(origin)?;

Expand All @@ -474,6 +501,7 @@ pub mod pallet {
signed_api_call,
threshold_signature_payload,
broadcast_id,
initiated_at,
);
Ok(().into())
}
Expand Down Expand Up @@ -502,8 +530,9 @@ pub mod pallet {
) -> DispatchResultWithPostInfo {
T::EnsureWitnessed::ensure_origin(origin.clone())?;

let broadcast_id = TransactionOutIdToBroadcastId::<T, I>::take(&tx_out_id)
.ok_or(Error::<T, I>::InvalidPayload)?;
let (broadcast_id, _initiated_at) =
TransactionOutIdToBroadcastId::<T, I>::take(&tx_out_id)
.ok_or(Error::<T, I>::InvalidPayload)?;

let to_refund = AwaitingBroadcast::<T, I>::get(BroadcastAttemptId {
broadcast_id,
Expand Down Expand Up @@ -616,6 +645,13 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
if let Some(callback) = maybe_callback {
RequestCallbacks::<T, I>::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(),
Expand All @@ -625,6 +661,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
threshold_signature_payload,
api_call: Box::new(api_call),
broadcast_id,
initiated_at,
}
.into()
},
Expand All @@ -643,12 +680,16 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
api_call: <T as Config<I>>::ApiCall,
threshold_signature_payload: <<T::TargetChain as Chain>::ChainCrypto as ChainCrypto>::Payload,
broadcast_id: BroadcastId,
initiated_at: ChainBlockNumberFor<T, I>,
) -> BroadcastAttemptId {
let transaction_out_id = api_call.transaction_out_id();

T::BroadcastReadyProvider::on_broadcast_ready(&api_call);

TransactionOutIdToBroadcastId::<T, I>::insert(&transaction_out_id, broadcast_id);
TransactionOutIdToBroadcastId::<T, I>::insert(
&transaction_out_id,
(broadcast_id, initiated_at),
);

ThresholdSignatureData::<T, I>::insert(broadcast_id, (api_call, signature));

Expand Down
6 changes: 6 additions & 0 deletions state-chain/pallets/cf-broadcast/src/migrations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod add_initiated_at;

use cf_runtime_upgrade_utilities::VersionedMigration;

pub type PalletMigration<T, I> =
(VersionedMigration<crate::Pallet<T, I>, add_initiated_at::Migration<T, I>, 0, 1>,);
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::*;
#[cfg(feature = "try-runtime")]
use frame_support::dispatch::DispatchError;
use frame_support::{traits::OnRuntimeUpgrade, weights::Weight};
use sp_std::marker::PhantomData;

mod old {
use frame_support::pallet_prelude::OptionQuery;

use super::*;

#[frame_support::storage_alias]
pub type TransactionOutIdToBroadcastId<T: Config<I>, I: 'static> =
StorageMap<Pallet<T, I>, Twox64Concat, TransactionOutIdFor<T, I>, BroadcastId, OptionQuery>;
}

pub struct Migration<T: Config<I>, I: 'static>(PhantomData<(T, I)>);

impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for Migration<T, I> {
fn on_runtime_upgrade() -> frame_support::weights::Weight {
let chain_height = T::ChainTracking::get_block_height();

TransactionOutIdToBroadcastId::<T, I>::translate::<BroadcastId, _>(|_id, old| {
Some((old, chain_height))
});

Weight::zero()
}

#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, DispatchError> {
use frame_support::ensure;

let chain_height = T::ChainTracking::get_block_height();
// If it's at 0 something went wrong with the initialisation. Also since initiated_at is the
// last thing being decoded, this acts as a check that the rest of the decoding worked.
ensure!(chain_height > 0u32.into(), "chain_height is 0");
Ok(chain_height.encode())
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), DispatchError> {
use frame_support::ensure;

let pre_upgrade_height = ChainBlockNumberFor::<T, I>::decode(&mut &state[..])
.map_err(|_| "Failed to decode pre-upgrade state.")?;

for (_out_id, (_b_id, initiated_at)) in TransactionOutIdToBroadcastId::<T, I>::iter() {
ensure!(initiated_at >= pre_upgrade_height, "initiated_at is 0");
}
Ok(())
}
}
6 changes: 5 additions & 1 deletion state-chain/pallets/cf-broadcast/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -152,6 +155,7 @@ impl pallet_cf_broadcast::Config<Instance1> for Test {
type SafeMode = MockRuntimeSafeMode;
type BroadcastReadyProvider = MockBroadcastReadyProvider;
type SafeModeBlockMargin = ConstU64<10>;
type ChainTracking = BlockHeightProvider<MockEthereum>;
}

impl_mock_chainflip!(Test);
Expand Down
1 change: 1 addition & 0 deletions state-chain/pallets/cf-broadcast/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}

Expand Down
3 changes: 3 additions & 0 deletions state-chain/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ impl pallet_cf_broadcast::Config<EthereumInstance> for Runtime {
type SafeMode = RuntimeSafeMode;
type SafeModeBlockMargin = ConstU32<10>;
type KeyProvider = EthereumVault;
type ChainTracking = EthereumChainTracking;
}

impl pallet_cf_broadcast::Config<PolkadotInstance> for Runtime {
Expand All @@ -704,6 +705,7 @@ impl pallet_cf_broadcast::Config<PolkadotInstance> for Runtime {
type SafeMode = RuntimeSafeMode;
type SafeModeBlockMargin = ConstU32<10>;
type KeyProvider = PolkadotVault;
type ChainTracking = PolkadotChainTracking;
}

impl pallet_cf_broadcast::Config<BitcoinInstance> for Runtime {
Expand All @@ -726,6 +728,7 @@ impl pallet_cf_broadcast::Config<BitcoinInstance> for Runtime {
type SafeMode = RuntimeSafeMode;
type SafeModeBlockMargin = ConstU32<10>;
type KeyProvider = BitcoinVault;
type ChainTracking = BitcoinChainTracking;
}

impl pallet_cf_chain_tracking::Config<EthereumInstance> for Runtime {
Expand Down