Skip to content

Commit

Permalink
chore: Solana skip preflight only for CCM (#5480)
Browse files Browse the repository at this point in the history
* chore: skip preflight only ccm

* chore: set preflight commitment only with skip_preflight

* chore: nit

* chore: update test

* chore: add migration

* chore: run upgrade test

* chore: restore upgrade flag

* chore: use matches!

* chore: print sol tx as hex

---------

Co-authored-by: Daniel <daniel@chainflip.io>
  • Loading branch information
albert-llimos and dandanlen authored Dec 10, 2024
1 parent e8cbf84 commit 2232236
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 24 deletions.
37 changes: 26 additions & 11 deletions engine/src/sol/retry_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
witness::common::chain_source::{ChainClient, Header},
};
use cf_chains::{
sol::{SolAddress, SolHash, SolSignature},
sol::{SolAddress, SolHash, SolSignature, SolanaTransactionData},
Solana,
};
use cf_utilities::{make_periodic_tick, task_scope::Scope};
Expand All @@ -15,7 +15,7 @@ use anyhow::{anyhow, Result};
use base64::{prelude::BASE64_STANDARD, Engine};

use super::{
commitment_config::CommitmentConfig,
commitment_config::{CommitmentConfig, CommitmentLevel},
rpc::{SolRpcApi, SolRpcClient},
rpc_client_api::*,
};
Expand Down Expand Up @@ -88,8 +88,10 @@ pub trait SolRetryRpcApi: Clone {
config: RpcTransactionConfig,
) -> EncodedConfirmedTransactionWithStatusMeta;

async fn broadcast_transaction(&self, raw_transaction: Vec<u8>)
-> anyhow::Result<SolSignature>;
async fn broadcast_transaction(
&self,
transaction: SolanaTransactionData,
) -> anyhow::Result<SolSignature>;
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -227,11 +229,20 @@ impl SolRetryRpcApi for SolRetryRpcClient {
)
.await
}
async fn broadcast_transaction(&self, transaction: Vec<u8>) -> anyhow::Result<SolSignature> {
let encoded_transaction = BASE64_STANDARD.encode(&transaction);
async fn broadcast_transaction(
&self,
transaction: SolanaTransactionData,
) -> anyhow::Result<SolSignature> {
let encoded_transaction = BASE64_STANDARD.encode(&transaction.serialized_transaction);
let config = RpcSendTransactionConfig {
skip_preflight: true,
preflight_commitment: None,
skip_preflight: transaction.skip_preflight,
preflight_commitment: if transaction.skip_preflight {
None
} else {
// 'Confirmed' for preflight commitment is enough, no need for
// 'Finalised' when broadcasting.
Some(CommitmentLevel::Confirmed)
},
encoding: Some(UiTransactionEncoding::Base64),
max_retries: None,
min_context_slot: None,
Expand All @@ -241,7 +252,11 @@ impl SolRetryRpcApi for SolRetryRpcClient {
.request_with_limit(
RequestLog::new(
"sendTransaction".to_string(),
Some(format!("{:?}, {:?}", transaction, config)),
Some(format!(
"0x{}, {:?}",
hex::encode(&transaction.serialized_transaction),
config
)),
),
Box::pin(move |client| {
let encoded_transaction = encoded_transaction.clone();
Expand Down Expand Up @@ -342,7 +357,7 @@ pub mod mocks {
config: RpcTransactionConfig,
) -> EncodedConfirmedTransactionWithStatusMeta;

async fn broadcast_transaction(&self, transaction: Vec<u8>)
async fn broadcast_transaction(&self, transaction: SolanaTransactionData)
-> anyhow::Result<SolSignature>;
}
}
Expand Down Expand Up @@ -483,7 +498,7 @@ mod tests {

// Checking that encoding and sending the transaction works.
let tx_signature = retry_client
.broadcast_transaction(signed_and_serialized_tx).await.unwrap();
.broadcast_transaction(SolanaTransactionData {serialized_transaction: signed_and_serialized_tx, skip_preflight: true}).await.unwrap();

println!("tx_signature: {:?}", tx_signature);

Expand Down
2 changes: 1 addition & 1 deletion engine/src/state_chain_observer/sc_observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ where
let sol_rpc = sol_rpc.clone();
let state_chain_client = state_chain_client.clone();
scope.spawn(async move {
match sol_rpc.broadcast_transaction(payload.serialized_transaction).await {
match sol_rpc.broadcast_transaction(payload).await {
Ok(tx_signature) => info!("Solana TransactionBroadcastRequest {broadcast_id:?} success: tx_signature: {tx_signature:?}"),
Err(error) => {
error!("Error on Solana TransactionBroadcastRequest {broadcast_id:?}: {error:?}");
Expand Down
5 changes: 3 additions & 2 deletions state-chain/cfe-events/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,10 @@ fn event_decoding() {
data: vec![31,41,51,61]
}],
},
}).finalize_and_serialize().unwrap()
}).finalize_and_serialize().unwrap(),
skip_preflight: false,
}
}), "0f01000000010101010101010101010101010101010101010101010101010101010101010139020109090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909020202010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b01020101041f29333d");
}), "0f01000000010101010101010101010101010101010101010101010101010101010101010139020109090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909020202010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b01020101041f29333d00");
}

// P2P registration/deregistration
Expand Down
1 change: 1 addition & 0 deletions state-chain/chains/src/sol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub const MAX_CCM_BYTES_USDC: usize = MAX_TRANSACTION_LENGTH - 740usize; // 492
#[derive(Encode, Decode, TypeInfo, Clone, RuntimeDebug, Default, PartialEq, Eq)]
pub struct SolanaTransactionData {
pub serialized_transaction: Vec<u8>,
pub skip_preflight: bool,
}

/// A Solana transaction in id is a tuple of the AccountAddress and the slot number.
Expand Down
1 change: 1 addition & 0 deletions state-chain/chains/src/sol/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl BenchmarkValue for SolanaTransactionData {
serialized_transaction: SolTransaction::benchmark_value()
.finalize_and_serialize()
.expect("Failed to serialize payload"),
skip_preflight: false,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion state-chain/pallets/cf-broadcast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub enum PalletConfigUpdate {
BroadcastTimeout { blocks: u32 },
}

pub const PALLET_VERSION: StorageVersion = StorageVersion::new(10);
pub const PALLET_VERSION: StorageVersion = StorageVersion::new(11);

#[frame_support::pallet]
pub mod pallet {
Expand Down
12 changes: 10 additions & 2 deletions state-chain/runtime/src/chainflip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ use cf_traits::{
ScheduledEgressDetails,
};

use cf_chains::{btc::ScriptPubkey, instances::BitcoinInstance};
use cf_chains::{btc::ScriptPubkey, instances::BitcoinInstance, sol::api::SolanaTransactionType};
use codec::{Decode, Encode};
use eth::Address as EvmAddress;
use frame_support::{
Expand Down Expand Up @@ -339,7 +339,15 @@ impl TransactionBuilder<Solana, SolanaApi<SolEnvironment>> for SolanaTransaction
fn build_transaction(
signed_call: &SolanaApi<SolEnvironment>,
) -> <Solana as Chain>::Transaction {
SolanaTransactionData { serialized_transaction: signed_call.chain_encoded() }
SolanaTransactionData {
serialized_transaction: signed_call.chain_encoded(),
// skip_preflight when broadcasting ccm transfers to consume the nonce even if the
// transaction reverts
skip_preflight: matches!(
signed_call.call_type,
SolanaTransactionType::CcmTransfer { .. }
),
}
}

fn refresh_unsigned_data(_tx: &mut <Solana as Chain>::Transaction) {
Expand Down
60 changes: 53 additions & 7 deletions state-chain/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
},
Offence,
},
migrations::solana_transaction_data_migration::NoopUpgrade,
monitoring_apis::{
ActivateKeysBroadcastIds, AuthoritiesInfo, BtcUtxos, EpochState, ExternalChainsBlockHeight,
FeeImbalance, FlipSupply, LastRuntimeUpgradeInfo, MonitoringDataV2, OpenDepositChannels,
Expand Down Expand Up @@ -1293,13 +1294,58 @@ type PalletMigrations = (
pallet_cf_cfe_interface::migrations::PalletMigration<Runtime>,
);

type MigrationsForV1_8 = VersionedMigration<
2,
3,
migrations::solana_vault_swaps_migration::SolanaVaultSwapsMigration,
pallet_cf_elections::Pallet<Runtime, SolanaInstance>,
DbWeight,
>;
type MigrationsForV1_8 = (
VersionedMigration<
2,
3,
migrations::solana_vault_swaps_migration::SolanaVaultSwapsMigration,
pallet_cf_elections::Pallet<Runtime, SolanaInstance>,
DbWeight,
>,
// Only the Solana Transaction type has changed
VersionedMigration<
10,
11,
migrations::solana_transaction_data_migration::SolanaTransactionDataMigration,
pallet_cf_broadcast::Pallet<Runtime, SolanaInstance>,
DbWeight,
>,
VersionedMigration<
10,
11,
NoopUpgrade,
pallet_cf_broadcast::Pallet<Runtime, SolanaInstance>,
DbWeight,
>,
VersionedMigration<
10,
11,
NoopUpgrade,
pallet_cf_broadcast::Pallet<Runtime, EthereumInstance>,
DbWeight,
>,
VersionedMigration<
10,
11,
NoopUpgrade,
pallet_cf_broadcast::Pallet<Runtime, PolkadotInstance>,
DbWeight,
>,
VersionedMigration<
10,
11,
NoopUpgrade,
pallet_cf_broadcast::Pallet<Runtime, BitcoinInstance>,
DbWeight,
>,
VersionedMigration<
10,
11,
NoopUpgrade,
pallet_cf_broadcast::Pallet<Runtime, ArbitrumInstance>,
DbWeight,
>,
);

#[cfg(feature = "runtime-benchmarks")]
#[macro_use]
Expand Down
1 change: 1 addition & 0 deletions state-chain/runtime/src/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
pub mod housekeeping;
pub mod reap_old_accounts;
pub mod solana_transaction_data_migration;
pub mod solana_vault_swaps_migration;
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use frame_support::traits::UncheckedOnRuntimeUpgrade;
use pallet_cf_broadcast::BroadcastData;

use crate::*;
use frame_support::pallet_prelude::Weight;
#[cfg(feature = "try-runtime")]
use sp_runtime::DispatchError;

use cf_chains::sol::SolanaTransactionData;
use codec::{Decode, Encode};

pub mod old {
use cf_chains::sol::{SolMessage, SolSignature};
use cf_primitives::BroadcastId;
use frame_support::{pallet_prelude::OptionQuery, Twox64Concat};

use super::*;

#[derive(PartialEq, Eq, Encode, Decode)]
pub struct SolanaTransactionData {
pub serialized_transaction: Vec<u8>,
}

#[derive(PartialEq, Eq, Encode, Decode)]
pub struct SolanaBroadcastData {
pub broadcast_id: BroadcastId,
pub transaction_payload: SolanaTransactionData,
pub threshold_signature_payload: SolMessage,
pub transaction_out_id: SolSignature,
pub nominee: Option<<Runtime as frame_system::Config>::AccountId>,
}

#[frame_support::storage_alias]
pub type AwaitingBroadcast =
StorageMap<SolanaBroadcaster, Twox64Concat, BroadcastId, SolanaBroadcastData, OptionQuery>;
}

pub struct SolanaTransactionDataMigration;

impl UncheckedOnRuntimeUpgrade for SolanaTransactionDataMigration {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, DispatchError> {
Ok((old::AwaitingBroadcast::iter().count() as u64).encode())
}

fn on_runtime_upgrade() -> Weight {
pallet_cf_broadcast::AwaitingBroadcast::<Runtime, SolanaInstance>::translate_values::<
old::SolanaBroadcastData,
_,
>(|old_sol_broadcast_data| {
Some(BroadcastData::<Runtime, SolanaInstance> {
broadcast_id: old_sol_broadcast_data.broadcast_id,
transaction_payload: SolanaTransactionData {
serialized_transaction: old_sol_broadcast_data
.transaction_payload
.serialized_transaction,
skip_preflight: true,
},
threshold_signature_payload: old_sol_broadcast_data.threshold_signature_payload,
transaction_out_id: old_sol_broadcast_data.transaction_out_id,
nominee: old_sol_broadcast_data.nominee,
})
});

Weight::zero()
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), DispatchError> {
let pre_awaiting_broadcast_count = <u64>::decode(&mut state.as_slice())
.map_err(|_| DispatchError::from("Failed to decode state"))?;

let post_awaiting_broadcast_count =
pallet_cf_broadcast::AwaitingBroadcast::<Runtime, SolanaInstance>::iter().count()
as u64;

assert_eq!(pre_awaiting_broadcast_count, post_awaiting_broadcast_count);
Ok(())
}
}

pub struct NoopUpgrade;

impl UncheckedOnRuntimeUpgrade for NoopUpgrade {
fn on_runtime_upgrade() -> Weight {
Weight::zero()
}
}

0 comments on commit 2232236

Please sign in to comment.