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: Re-sign failed CCMs #4277

Merged
merged 17 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 3 additions & 1 deletion state-chain/chains/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,9 @@ impl Tokenizable for AggKey {
}
}

#[derive(Encode, Decode, TypeInfo, Copy, Clone, RuntimeDebug, PartialEq, Eq)]
#[derive(
Encode, Decode, TypeInfo, Copy, Clone, RuntimeDebug, PartialEq, Eq, Serialize, Deserialize,
)]
syan095 marked this conversation as resolved.
Show resolved Hide resolved
pub struct SchnorrVerificationComponents {
/// Scalar component
pub s: [u8; 32],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use sp_std::{vec, vec::Vec};
/// function.
#[derive(Encode, Decode, TypeInfo, Clone, RuntimeDebug, PartialEq, Eq)]
pub struct ExecutexSwapAndCall {
/// The egress Id. Used to query Gas budge stored in the Ccm Pallet.
/// The egress Id. Used to query Gas budge stored in the Swapping Pallet.
egress_id: EgressId,
/// A single transfer that need to be made to given addresses.
transfer_param: EncodableTransferAssetParams,
Expand Down
17 changes: 15 additions & 2 deletions state-chain/custom-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use cf_chains::{
eth::Address as EthereumAddress,
};
use cf_primitives::{
AccountRole, Asset, AssetAmount, ForeignChain, NetworkEnvironment, SemVer, SwapOutput,
AccountRole, Asset, AssetAmount, BroadcastId, ForeignChain, NetworkEnvironment, SemVer,
SwapOutput,
};
use cf_utilities::rpc::NumberOrHex;
use core::ops::Range;
Expand All @@ -26,7 +27,9 @@ use sp_runtime::DispatchError;
use state_chain_runtime::{
chainflip::Offence,
constants::common::TX_FEE_MULTIPLIER,
runtime_apis::{CustomRuntimeApi, LiquidityProviderInfo, RuntimeApiAccountInfoV2},
runtime_apis::{
CustomRuntimeApi, FailedCcmCall, LiquidityProviderInfo, RuntimeApiAccountInfoV2,
},
};
use std::{
collections::{BTreeMap, HashMap},
Expand Down Expand Up @@ -505,6 +508,9 @@ pub trait CustomApi {

#[method(name = "supported_assets")]
fn cf_supported_assets(&self) -> RpcResult<HashMap<ForeignChain, Vec<Asset>>>;

#[method(name = "failed_ccm")]
fn cf_failed_ccm_call(&self, broadcast_id: BroadcastId) -> RpcResult<Option<FailedCcmCall>>;
}

/// An RPC extension for the state chain node.
Expand Down Expand Up @@ -1113,6 +1119,13 @@ where
});
Ok(chain_to_asset)
}

fn cf_failed_ccm_call(&self, broadcast_id: BroadcastId) -> RpcResult<Option<FailedCcmCall>> {
self.client
.runtime_api()
.cf_failed_ccm_call(self.unwrap_or_best(None), broadcast_id)
.map_err(to_rpc_error)
}
}

impl<C, B> CustomRpc<C, B>
Expand Down
1 change: 1 addition & 0 deletions state-chain/pallets/cf-broadcast/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ fn generate_on_signature_ready_call<T: pallet::Config<I>, I>() -> pallet::Call<T
api_call: Box::new(ApiCallFor::<T, I>::benchmark_value()),
broadcast_id: 1,
initiated_at: INITIATED_AT.into(),
should_broadcast: true,
}
}

Expand Down
128 changes: 98 additions & 30 deletions state-chain/pallets/cf-broadcast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub enum PalletOffence {
FailedToBroadcastTransaction,
}

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

#[frame_support::pallet]
pub mod pallet {
Expand Down Expand Up @@ -238,8 +238,14 @@ pub mod pallet {

/// Callbacks to be dispatched when the SignatureAccepted event has been witnessed.
#[pallet::storage]
#[pallet::getter(fn request_callback)]
pub type RequestCallbacks<T: Config<I>, I: 'static = ()> =
#[pallet::getter(fn request_success_callback)]
pub type RequestSuccessCallbacks<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, BroadcastId, <T as Config<I>>::BroadcastCallable>;

/// Callbacks to be dispatched when a broadcast failure has been witnessed.
#[pallet::storage]
#[pallet::getter(fn request_failed_callback)]
pub type RequestFailedCallbacks<T: Config<I>, I: 'static = ()> =
syan095 marked this conversation as resolved.
Show resolved Hide resolved
StorageMap<_, Twox64Concat, BroadcastId, <T as Config<I>>::BroadcastCallable>;

/// The last attempt number for a particular broadcast.
Expand Down Expand Up @@ -287,6 +293,7 @@ pub mod pallet {

/// Stores all needed information to be able to re-request the signature
#[pallet::storage]
#[pallet::getter(fn threshold_signature_data)]
pub type ThresholdSignatureData<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Twox64Concat,
Expand Down Expand Up @@ -339,6 +346,8 @@ pub mod pallet {
},
/// The fee paid for broadcasting a transaction has been refused.
TransactionFeeDeficitRefused { beneficiary: SignerIdFor<T, I> },
/// A Call has been re-threshold-signed, and its signature data is inserted into storage.
CallResigned { broadcast_id: BroadcastId },
}

#[pallet::error]
Expand Down Expand Up @@ -435,7 +444,7 @@ pub mod pallet {
origin: OriginFor<T>,
broadcast_attempt_id: BroadcastAttemptId,
) -> DispatchResultWithPostInfo {
let extrinsic_signer = T::AccountRoleRegistry::ensure_validator(origin)?.into();
let extrinsic_signer = T::AccountRoleRegistry::ensure_validator(origin.clone())?.into();

let signing_attempt = AwaitingBroadcast::<T, I>::get(broadcast_attempt_id)
.ok_or(Error::<T, I>::InvalidBroadcastAttemptId)?;
Expand All @@ -458,11 +467,28 @@ pub mod pallet {
.checked_sub(1)
.expect("We must have at least one authority")
{
let broadcast_id =
signing_attempt.broadcast_attempt.broadcast_attempt_id.broadcast_id;

// We want to keep the broadcast details, but we don't need the list of failed
// broadcasters any more.
FailedBroadcasters::<T, I>::remove(
signing_attempt.broadcast_attempt.broadcast_attempt_id.broadcast_id,
);
FailedBroadcasters::<T, I>::remove(broadcast_id);

// Call the failed callback and clean up the callback storage.
if let Some(callback) = RequestFailedCallbacks::<T, I>::take(broadcast_id) {
Self::deposit_event(Event::<T, I>::BroadcastCallbackExecuted {
broadcast_id,
result: callback.dispatch_bypass_filter(origin).map(|_| ()).map_err(|e| {
syan095 marked this conversation as resolved.
Show resolved Hide resolved
log::warn!(
"Broadcast failure callback execution has failed for broadcast {}.",
broadcast_id
);
e.error
}),
});
}
RequestSuccessCallbacks::<T, I>::remove(broadcast_id);

Self::deposit_event(Event::<T, I>::BroadcastAborted {
broadcast_id: signing_attempt
.broadcast_attempt
Expand All @@ -477,12 +503,14 @@ pub mod pallet {
}

/// A callback to be used when a threshold signature request completes. Retrieves the
/// requested signature, uses the configured [TransactionBuilder] to build the transaction
/// and then initiates the broadcast sequence.
/// requested signature, uses the configured [TransactionBuilder] to build the transaction.
/// Initiates the broadcast sequence if `should_broadcast` is set to true, otherwise insert
/// the signature result into the `ThresholdSignatureData` storage.
///
/// ## Events
///
/// - See [Call::start_broadcast].
/// - If `should_broadcast` see [Call::start_broadcast]
///
///
/// ## Errors
///
Expand All @@ -496,6 +524,7 @@ pub mod pallet {
api_call: Box<<T as Config<I>>::ApiCall>,
broadcast_id: BroadcastId,
initiated_at: ChainBlockNumberFor<T, I>,
should_broadcast: bool,
) -> DispatchResultWithPostInfo {
let _ = T::EnsureThresholdSigned::ensure_origin(origin)?;

Expand All @@ -512,14 +541,21 @@ pub mod pallet {

let signed_api_call = api_call.signed(&signature);

Self::start_broadcast(
&signature,
T::TransactionBuilder::build_transaction(&signed_api_call),
signed_api_call,
threshold_signature_payload,
broadcast_id,
initiated_at,
);
// If a signed call already exists, update the storage and do not broadcast.
if should_broadcast {
Self::start_broadcast(
&signature,
T::TransactionBuilder::build_transaction(&signed_api_call),
signed_api_call,
threshold_signature_payload,
broadcast_id,
initiated_at,
);
} else {
ThresholdSignatureData::<T, I>::insert(broadcast_id, (signed_api_call, signature));
Self::deposit_event(Event::<T, I>::CallResigned { broadcast_id });
}

Ok(().into())
}

Expand Down Expand Up @@ -587,7 +623,7 @@ pub mod pallet {
);
}

if let Some(callback) = RequestCallbacks::<T, I>::get(broadcast_id) {
if let Some(callback) = RequestSuccessCallbacks::<T, I>::get(broadcast_id) {
Self::deposit_event(Event::<T, I>::BroadcastCallbackExecuted {
broadcast_id,
result: callback.dispatch_bypass_filter(origin.clone()).map(|_| ()).map_err(
Expand Down Expand Up @@ -655,7 +691,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
}

TransactionMetadata::<T, I>::remove(broadcast_id);
RequestCallbacks::<T, I>::remove(broadcast_id);
RequestSuccessCallbacks::<T, I>::remove(broadcast_id);
RequestFailedCallbacks::<T, I>::remove(broadcast_id);
ThresholdSignatureData::<T, I>::remove(broadcast_id);
}

Expand All @@ -677,24 +714,40 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Request a threshold signature, providing [Call::on_signature_ready] as the callback.
pub fn threshold_sign_and_broadcast(
api_call: <T as Config<I>>::ApiCall,
maybe_callback: Option<<T as Config<I>>::BroadcastCallable>,
maybe_success_callback: Option<<T as Config<I>>::BroadcastCallable>,
maybe_failed_callback_generator: impl FnOnce(
BroadcastId,
) -> Option<<T as Config<I>>::BroadcastCallable>,
) -> (BroadcastId, ThresholdSignatureRequestId) {
let broadcast_id = BroadcastIdCounter::<T, I>::mutate(|id| {
*id += 1;
*id
});
if let Some(callback) = maybe_callback {
RequestCallbacks::<T, I>::insert(broadcast_id, callback);
if let Some(callback) = maybe_success_callback {
RequestSuccessCallbacks::<T, I>::insert(broadcast_id, callback);
}
if let Some(callback) = maybe_failed_callback_generator(broadcast_id) {
RequestFailedCallbacks::<T, I>::insert(broadcast_id, callback);
}

(broadcast_id, Self::threshold_sign(api_call, broadcast_id, true))
}

/// Signs a API call, use `Call::on_signature_ready` as the callback, and returns the signature
/// request ID.
fn threshold_sign(
api_call: <T as Config<I>>::ApiCall,
broadcast_id: BroadcastId,
should_broadcast: bool,
) -> ThresholdSignatureRequestId {
// 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(
T::ThresholdSigner::request_signature_with_callback(
threshold_signature_payload.clone(),
|threshold_request_id| {
Call::on_signature_ready {
Expand All @@ -703,11 +756,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
api_call: Box::new(api_call),
broadcast_id,
initiated_at,
should_broadcast,
}
.into()
},
);
(broadcast_id, signature_request_id)
)
}

/// Begin the process of broadcasting a transaction.
Expand Down Expand Up @@ -782,7 +835,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Self::clean_up_broadcast_storage(broadcast_id);
let (retry_broadcast_id, _) = Self::threshold_sign_and_broadcast(
api_call,
RequestCallbacks::<T, I>::get(broadcast_id),
RequestSuccessCallbacks::<T, I>::get(broadcast_id),
|_broadcast_id| RequestFailedCallbacks::<T, I>::get(broadcast_id),
);
log::info!(
"Signature is invalid -> rescheduled threshold signature for broadcast id {}.",
Expand Down Expand Up @@ -863,13 +917,27 @@ impl<T: Config<I>, I: 'static> Broadcaster<T::TargetChain> for Pallet<T, I> {
fn threshold_sign_and_broadcast(
api_call: Self::ApiCall,
) -> (BroadcastId, ThresholdSignatureRequestId) {
Self::threshold_sign_and_broadcast(api_call, None)
Self::threshold_sign_and_broadcast(api_call, None, |_| None)
}

fn threshold_sign_and_broadcast_with_callback(
api_call: Self::ApiCall,
callback: Self::Callback,
success_callback: Option<Self::Callback>,
failed_callback_generator: impl FnOnce(BroadcastId) -> Option<Self::Callback>,
) -> (BroadcastId, ThresholdSignatureRequestId) {
Self::threshold_sign_and_broadcast(api_call, Some(callback))
Self::threshold_sign_and_broadcast(api_call, success_callback, failed_callback_generator)
}

fn threshold_resign(broadcast_id: BroadcastId) -> Option<ThresholdSignatureRequestId> {
if let Some((api_call, _signature)) = ThresholdSignatureData::<T, I>::get(broadcast_id) {
Some(Self::threshold_sign(api_call, broadcast_id, false))
} else {
None
}
}

/// Clean up storage data related to a broadcast ID.
fn clean_up_broadcast_storage(broadcast_id: BroadcastId) {
Self::clean_up_broadcast_storage(broadcast_id);
}
}
4 changes: 2 additions & 2 deletions state-chain/pallets/cf-broadcast/src/migrations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod add_initiated_at;
pub mod v2;

use cf_runtime_upgrade_utilities::VersionedMigration;

pub type PalletMigration<T, I> =
(VersionedMigration<crate::Pallet<T, I>, add_initiated_at::Migration<T, I>, 0, 1>,);
(VersionedMigration<crate::Pallet<T, I>, v2::Migration<T, I>, 1, 2>,);

This file was deleted.

Loading
Loading