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: BTC vault deposit electoral system #5640

Merged
merged 2 commits into from
Feb 19, 2025
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
2 changes: 1 addition & 1 deletion engine/src/elections/voter_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,6 @@ macro_rules! generate_voter_api_tuple_impls {
}

generate_voter_api_tuple_impls!(tuple_1_impls: ((A, A0)));
generate_voter_api_tuple_impls!(tuple_2_impls: ((A, A0), (B, B0)));
generate_voter_api_tuple_impls!(tuple_3_impls: ((A, A0), (B, B0), (C, C0)));
generate_voter_api_tuple_impls!(tuple_4_impls: ((A, A0), (B, B0), (C, C0), (D, D0)));
generate_voter_api_tuple_impls!(tuple_7_impls: ((A, A0), (B, B0), (C, C0), (D, D0), (EE, E0), (FF, F0), (GG, G0)));
147 changes: 3 additions & 144 deletions engine/src/witness/btc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,13 @@ pub mod deposits;
pub mod source;
pub mod vault_swaps;

use crate::{
btc::{
retry_rpc::{BtcRetryRpcApi, BtcRetryRpcClient},
rpc::VerboseTransaction,
},
db::PersistentKeyDB,
state_chain_observer::client::{
extrinsic_api::signed::SignedExtrinsicApi,
storage_api::StorageApi,
stream_api::{StreamApi, FINALIZED, UNFINALIZED},
},
};
use crate::btc::rpc::VerboseTransaction;
use bitcoin::{hashes::Hash, BlockHash};
use cf_chains::btc::{self, deposit_address::DepositAddress, BlockNumber, CHANGE_ADDRESS_SALT};
use cf_primitives::{EpochIndex, NetworkEnvironment};
use cf_utilities::task_scope::Scope;
use cf_primitives::EpochIndex;
use futures_core::Future;
use source::BtcSource;
use std::sync::Arc;

use super::common::{
chain_source::{extension::ChainSourceExt, Header},
epoch_source::{EpochSourceBuilder, Vault},
};

use anyhow::Result;
use super::common::{chain_source::Header, epoch_source::Vault};

pub async fn process_egress<ProcessCall, ProcessingFut, ExtraInfo, ExtraHistoricInfo>(
epoch: Vault<cf_chains::Bitcoin, ExtraInfo, ExtraHistoricInfo>,
Expand Down Expand Up @@ -64,128 +45,6 @@ pub async fn process_egress<ProcessCall, ProcessingFut, ExtraInfo, ExtraHistoric
}
}

pub async fn start<
StateChainClient,
StateChainStream,
ProcessCall,
ProcessingFut,
PrewitnessCall,
PrewitnessFut,
>(
scope: &Scope<'_, anyhow::Error>,
btc_client: BtcRetryRpcClient,
process_call: ProcessCall,
prewitness_call: PrewitnessCall,
state_chain_client: Arc<StateChainClient>,
state_chain_stream: StateChainStream,
unfinalised_state_chain_stream: impl StreamApi<UNFINALIZED> + Clone,
epoch_source: EpochSourceBuilder<'_, '_, StateChainClient, (), ()>,
db: Arc<PersistentKeyDB>,
) -> Result<()>
where
StateChainClient: StorageApi + SignedExtrinsicApi + 'static + Send + Sync,
StateChainStream: StreamApi<FINALIZED> + Clone,
ProcessCall: Fn(state_chain_runtime::RuntimeCall, EpochIndex) -> ProcessingFut
+ Send
+ Sync
+ Clone
+ 'static,
ProcessingFut: Future<Output = ()> + Send + 'static,
PrewitnessCall: Fn(state_chain_runtime::RuntimeCall, EpochIndex) -> PrewitnessFut
+ Send
+ Sync
+ Clone
+ 'static,
PrewitnessFut: Future<Output = ()> + Send + 'static,
{
let btc_source = BtcSource::new(btc_client.clone()).strictly_monotonic().shared(scope);

btc_source
.clone()
.chunk_by_time(epoch_source.clone(), scope)
.chain_tracking(state_chain_client.clone(), btc_client.clone())
.logging("chain tracking")
.spawn(scope);

let vaults = epoch_source.vaults::<cf_chains::Bitcoin>().await;

let block_source = btc_source
.then({
let btc_client = btc_client.clone();
move |header| {
let btc_client = btc_client.clone();
async move {
let block = btc_client.block(header.hash).await.expect("TODO: Delete this");
(header.data, block.txdata)
}
}
})
.shared(scope);

// Pre-witnessing stream.
block_source
.clone()
.chunk_by_vault(vaults.clone(), scope)
.deposit_addresses(
scope,
unfinalised_state_chain_stream.clone(),
state_chain_client.clone(),
)
.await
.private_deposit_channels(scope, unfinalised_state_chain_stream, state_chain_client.clone())
.await
.btc_deposits(prewitness_call)
.logging("pre-witnessing")
.spawn(scope);

let btc_safety_margin = match state_chain_client
.storage_value::<pallet_cf_ingress_egress::WitnessSafetyMargin<
state_chain_runtime::Runtime,
state_chain_runtime::BitcoinInstance,
>>(state_chain_stream.cache().hash)
.await?
{
Some(margin) => margin,
None => {
use chainflip_node::chain_spec::{berghain, devnet, perseverance};
match state_chain_client
.storage_value::<pallet_cf_environment::ChainflipNetworkEnvironment<state_chain_runtime::Runtime>>(
state_chain_stream.cache().hash,
)
.await?
{
NetworkEnvironment::Mainnet => berghain::BITCOIN_SAFETY_MARGIN,
NetworkEnvironment::Testnet => perseverance::BITCOIN_SAFETY_MARGIN,
NetworkEnvironment::Development => devnet::BITCOIN_SAFETY_MARGIN,
}
},
};

tracing::info!("Safety margin for Bitcoin is set to {btc_safety_margin} blocks.",);

// Full witnessing stream.
block_source
.lag_safety(btc_safety_margin)
.logging("safe block produced")
.chunk_by_vault(vaults, scope)
.deposit_addresses(scope, state_chain_stream.clone(), state_chain_client.clone())
.await
.private_deposit_channels(scope, state_chain_stream.clone(), state_chain_client.clone())
.await
.btc_deposits(process_call.clone())
.egress_items(scope, state_chain_stream, state_chain_client.clone())
.await
.then({
let process_call = process_call.clone();
move |epoch, header| process_egress(epoch, header, process_call.clone())
})
.continuous("Bitcoin".to_string(), db)
.logging("witnessing")
.spawn(scope);

Ok(())
}

fn success_witnesses<'a>(
monitored_tx_hashes: impl Iterator<Item = &'a btc::Hash> + Clone,
txs: Vec<VerboseTransaction>,
Expand Down
29 changes: 24 additions & 5 deletions engine/src/witness/btc/deposits.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::collections::HashMap;

use cf_primitives::EpochIndex;
use cf_primitives::{AccountId, ChannelId, EpochIndex};
use futures_core::Future;
use itertools::Itertools;
use pallet_cf_ingress_egress::{DepositChannelDetails, DepositWitness};
use pallet_cf_ingress_egress::{DepositChannelDetails, DepositWitness, VaultDepositWitness};
use state_chain_runtime::BitcoinInstance;

use super::{
Expand All @@ -15,9 +15,12 @@ use super::{
};
use crate::{
btc::rpc::VerboseTransaction,
witness::common::{
chunked_chain_source::chunked_by_vault::deposit_addresses::Addresses, RuntimeCallHasChain,
RuntimeHasChain,
witness::{
btc::vault_swaps::try_extract_vault_swap_witness,
common::{
chunked_chain_source::chunked_by_vault::deposit_addresses::Addresses,
RuntimeCallHasChain, RuntimeHasChain,
},
},
};
use bitcoin::{hashes::Hash, BlockHash};
Expand Down Expand Up @@ -128,6 +131,22 @@ impl<Inner: ChunkedByVault> ChunkedByVaultBuilder<Inner> {
}
}

pub fn vault_deposits(
txs: &[VerboseTransaction],
vaults: &Vec<(DepositAddress, AccountId, ChannelId)>,
) -> Vec<VaultDepositWitness<state_chain_runtime::Runtime, BitcoinInstance>> {
txs.iter()
.filter_map(|tx| {
for (vault, broker, id) in vaults {
if let Some(vault_swap) = try_extract_vault_swap_witness(tx, vault, *id, broker) {
return Some(vault_swap);
}
}
None
})
.collect()
}

pub fn deposit_witnesses(
txs: &[VerboseTransaction],
deposit_addresses: &HashMap<Vec<u8>, DepositAddress>,
Expand Down
47 changes: 40 additions & 7 deletions engine/src/witness/btc_e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ use crate::{
use anyhow::Result;

use sp_core::H256;
use state_chain_runtime::chainflip::bitcoin_elections::BitcoinVaultDepositWitnessingES;
use std::sync::Arc;

use crate::btc::retry_rpc::BtcRetryRpcClient;
use crate::{btc::retry_rpc::BtcRetryRpcClient, witness::btc::deposits::vault_deposits};

#[derive(Clone)]
pub struct BitcoinDepositChannelWitnessingVoter {
Expand All @@ -59,7 +60,8 @@ impl VoterApi<BitcoinDepositChannelWitnessingES> for BitcoinDepositChannelWitnes
deposit_addresses: <BitcoinDepositChannelWitnessingES as ElectoralSystemTypes>::ElectionProperties,
) -> Result<Option<VoteOf<BitcoinDepositChannelWitnessingES>>, anyhow::Error> {
let (witness_range, deposit_addresses, _extra) = deposit_addresses;
let witness_range = BlockWitnessRange::try_new(witness_range).unwrap();
let witness_range = BlockWitnessRange::try_new(witness_range)
.map_err(|_| anyhow::anyhow!("Failed to create witness range"))?;
tracing::info!("Deposit channel witnessing properties: {:?}", deposit_addresses);

let mut txs = vec![];
Expand All @@ -80,13 +82,43 @@ impl VoterApi<BitcoinDepositChannelWitnessingES> for BitcoinDepositChannelWitnes

let witnesses = deposit_witnesses(&txs, &deposit_addresses);

if witnesses.is_empty() {
tracing::info!("No witnesses found for BTCE");
} else {
tracing::info!("Witnesses from BTCE: {:?}", witnesses);
Ok(Some(ConstantIndex::new(witnesses)))
}
}

#[derive(Clone)]
pub struct BitcoinVaultDepositWitnessingVoter {
client: BtcRetryRpcClient,
}

#[async_trait::async_trait]
impl VoterApi<BitcoinVaultDepositWitnessingES> for BitcoinVaultDepositWitnessingVoter {
async fn vote(
&self,
_settings: <BitcoinVaultDepositWitnessingES as ElectoralSystemTypes>::ElectoralSettings,
properties: <BitcoinVaultDepositWitnessingES as ElectoralSystemTypes>::ElectionProperties,
) -> Result<Option<VoteOf<BitcoinVaultDepositWitnessingES>>, anyhow::Error> {
let (witness_range, vaults, _extra) = properties;
let witness_range = BlockWitnessRange::try_new(witness_range)
.map_err(|_| anyhow::anyhow!("Failed to create witness range"))?;

let mut txs = vec![];
// we only ever expect this to be one for bitcoin, but for completeness, we loop.
tracing::info!("Witness range: {:?}", witness_range);
for block in BlockWitnessRange::<cf_chains::Bitcoin>::into_range_inclusive(witness_range) {
tracing::info!("Checking block {:?}", block);

// TODO: these queries should not be infinite
let block_hash = self.client.block_hash(block).await;

let block = self.client.block(block_hash).await?;

txs.extend(block.txdata);
}

Ok(Some(ConstantIndex { data: witnesses, _phantom: Default::default() }))
let witnesses = vault_deposits(&txs, &vaults);

Ok(Some(ConstantIndex::new(witnesses)))
}
}

Expand Down Expand Up @@ -216,6 +248,7 @@ where
CompositeVoter::<BitcoinElectoralSystemRunner, _>::new((
BitcoinBlockHeightTrackingVoter { client: client.clone() },
BitcoinDepositChannelWitnessingVoter { client: client.clone() },
BitcoinVaultDepositWitnessingVoter { client: client.clone() },
BitcoinLivenessVoter { client },
)),
)
Expand Down
15 changes: 14 additions & 1 deletion state-chain/chains/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,20 @@ use sp_std::{convert::TryFrom, str, vec};

use crate::DepositDetailsToTransactionInId;

#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, Default)]
#[derive(
Clone,
Debug,
PartialEq,
Eq,
Encode,
Decode,
TypeInfo,
Default,
Serialize,
Deserialize,
Ord,
PartialOrd,
)]
pub struct DepositDetails {
// In the case of EVM Native Deposits (ETH or arbETH), because we need to detect ingresses by
// checking balances rather than using events, there can be more than one hash associated with
Expand Down
20 changes: 16 additions & 4 deletions state-chain/chains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,9 @@ pub trait Chain: Member + Parameter + ChainInstanceAlias {
+ Into<cf_primitives::ForeignChain>
+ TryFrom<cf_primitives::Asset, Error: Debug>
+ IntoEnumIterator
+ Unpin;
+ Unpin
+ Ord
+ PartialOrd;

type ChainAssetMap<
T: Member
Expand Down Expand Up @@ -456,7 +458,9 @@ pub trait Chain: Member + Parameter + ChainInstanceAlias {
+ TryFrom<ForeignChainAddress>
+ IntoForeignChainAddress<Self>
+ Unpin
+ ToHumanreadableAddress;
+ ToHumanreadableAddress
+ Serialize
+ for<'a> Deserialize<'a>;

type DepositFetchId: Member
+ Parameter
Expand All @@ -471,7 +475,11 @@ pub trait Chain: Member + Parameter + ChainInstanceAlias {
type DepositDetails: Member
+ Parameter
+ BenchmarkValue
+ DepositDetailsToTransactionInId<Self::ChainCrypto>;
+ DepositDetailsToTransactionInId<Self::ChainCrypto>
+ Serialize
+ for<'a> Deserialize<'a>
+ Ord
+ PartialOrd;

type Transaction: Member + Parameter + BenchmarkValue + FeeRefundCalculator<Self>;

Expand Down Expand Up @@ -510,7 +518,11 @@ pub trait ChainCrypto: ChainCryptoInstanceAlias + Sized {
+ Parameter
+ Unpin
+ IntoTransactionInIdForAnyChain<Self>
+ BenchmarkValue;
+ BenchmarkValue
+ Serialize
+ for<'a> Deserialize<'a>
+ Ord
+ PartialOrd;

/// Uniquely identifies a transaction on the outgoing direction.
type TransactionOutId: Member + Parameter + Unpin + BenchmarkValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,6 @@ pub trait BWProcessorTypes: Sized {
+ 'static;
type BlockData: PartialEq + Clone + Debug + Eq + Serde + 'static;

// type ChainBlockNumber: Serde
Copy link
Contributor

Choose a reason for hiding this comment

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

?

// + Copy
// + Eq
// + Ord
// + SaturatingStep
// + Step
// + BlockZero
// + Debug
// + Default
// + 'static;

// type BlockData: Serde + Clone;

type Event: Serde + Debug + Clone + Eq;
type Rules: Hook<HookTypeFor<Self, RulesHook>> + Default + Serde + Debug + Clone + Eq;
type Execute: Hook<HookTypeFor<Self, ExecuteHook>> + Default + Serde + Debug + Clone + Eq;
Expand Down
Loading