diff --git a/Cargo.lock b/Cargo.lock index 5cd99057b..7a2117e2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13144,6 +13144,7 @@ name = "strata-reth-evm" version = "0.1.0" dependencies = [ "alloy-sol-types", + "hex", "reth-primitives", "revm", "revm-primitives", @@ -13215,7 +13216,6 @@ name = "strata-reth-primitives" version = "0.1.0" dependencies = [ "alloy-sol-types", - "reth-primitives", "serde", ] diff --git a/crates/bridge-tx-builder/src/withdrawal.rs b/crates/bridge-tx-builder/src/withdrawal.rs index 7762f4b36..705230607 100644 --- a/crates/bridge-tx-builder/src/withdrawal.rs +++ b/crates/bridge-tx-builder/src/withdrawal.rs @@ -193,14 +193,12 @@ mod tests { use strata_primitives::{ bridge::OperatorIdx, buf::Buf32, - errors::ParseError, l1::{TaprootSpendPath, XOnlyPk}, }; use strata_test_utils::bridge::{generate_keypairs, generate_pubkey_table}; use crate::{ context::TxBuildContext, - errors::{BridgeTxBuilderError, CooperativeWithdrawalError}, prelude::{CooperativeWithdrawalInfo, BRIDGE_DENOMINATION}, TxKind, }; @@ -268,48 +266,6 @@ mod tests { ); } - #[test] - fn test_construct_signing_data_invalid_user_pk() { - // Arrange - let (pubkeys, _seckeys) = generate_keypairs(2); - let pubkey_table = generate_pubkey_table(&pubkeys[..]); - let deposit_outpoint = - OutPoint::new(Txid::from_raw_hash(sha256d::Hash::hash(&[2u8; 32])), 2); - - let user_index = 1usize; - let assigned_operator_idx = 0usize; - assert_ne!( - user_index, assigned_operator_idx, - "use separate indexes for user and assigned operator" - ); - - // Create an invalid XOnlyPublicKey by using an all-zero buffer - let invalid_user_pk = XOnlyPk::new(Buf32::zero()); - let invalid_user_descriptor = invalid_user_pk.to_descriptor(); - let assigned_operator_idx = assigned_operator_idx as OperatorIdx; - - let withdrawal_info = CooperativeWithdrawalInfo::new( - deposit_outpoint, - invalid_user_descriptor, - assigned_operator_idx, - 0, - ); - - let build_context = - TxBuildContext::new(Network::Regtest, pubkey_table, assigned_operator_idx); - - // Act - let signing_data_result = withdrawal_info.construct_signing_data(&build_context); - - // Assert - assert!(signing_data_result.is_err_and(|e| matches!( - e, - BridgeTxBuilderError::CooperativeWithdrawalTransaction( - CooperativeWithdrawalError::InvalidUserPk(ParseError::InvalidPubkey(_)), - ), - ))); - } - #[test] fn test_create_prevout_success() { // Arrange diff --git a/crates/evmexec/src/engine.rs b/crates/evmexec/src/engine.rs index c3f2a38e5..9d2760328 100644 --- a/crates/evmexec/src/engine.rs +++ b/crates/evmexec/src/engine.rs @@ -13,10 +13,7 @@ use strata_eectl::{ errors::{EngineError, EngineResult}, messages::{ExecPayloadData, PayloadEnv}, }; -use strata_primitives::{ - buf::Buf32, - l1::{BitcoinAmount, XOnlyPk}, -}; +use strata_primitives::{bitcoin_bosd::Descriptor, l1::BitcoinAmount}; use strata_reth_evm::constants::COINBASE_ADDRESS; use strata_reth_node::{ ExecutionPayloadFieldV2, StrataExecutionPayloadEnvelopeV2, StrataPayloadAttributes, @@ -108,27 +105,35 @@ impl RpcExecEngineInner { prev_block: EVML2Block, ) -> EngineResult { // TODO: pass other fields from payload_env - let withdrawals = payload_env - .el_ops() - .iter() - .map(|op| match op { - Op::Deposit(deposit_data) => Ok(Withdrawal { - index: deposit_data.intent_idx(), - address: address_from_slice(deposit_data.dest_addr()).ok_or_else(|| { - EngineError::InvalidAddress(deposit_data.dest_addr().to_vec()) - })?, - amount: sats_to_gwei(deposit_data.amt()) - .ok_or(EngineError::AmountConversion(deposit_data.amt()))?, - ..Default::default() - }), - }) - .collect::>()?; + // FIXME: I have no idea how to appease EVM here. + // let withdrawals = payload_env + // .el_ops() + // .iter() + // .map(|op| match op { + // // Op::Deposit(deposit_data) => Ok(Withdrawal { + // // index: deposit_data.intent_idx(), + // // address: address_from_slice(deposit_data.dest_addr()).ok_or_else(|| { + // // EngineError::InvalidAddress(deposit_data.dest_addr().to_vec()) + // // })?, + // // amount: sats_to_gwei(deposit_data.amt()) + // // .ok_or(EngineError::AmountConversion(deposit_data.amt()))?, + // // ..Default::default() + // // }), + // Op::Deposit(deposit_data) => Ok(ELDepositData::new( + // deposit_data.intent_idx(), + // sats_to_gwei(deposit_data.amt()) + // .ok_or(EngineError::AmountConversion(deposit_data.amt()))?, + // deposit_data.dest_addr().to_vec(), + // )), + // }) + // .collect::>()?; let payload_attributes = StrataPayloadAttributes::new_from_eth(PayloadAttributes { // evm expects timestamp in seconds timestamp: payload_env.timestamp() / 1000, prev_randao: B256::ZERO, - withdrawals: Some(withdrawals), + // withdrawals: Some(withdrawals), FIXME: I have no idea how to appease EVM here. + withdrawals: None, parent_beacon_block_root: None, suggested_fee_recipient: COINBASE_ADDRESS, }); @@ -180,7 +185,7 @@ impl RpcExecEngineInner { Op::Deposit(ELDepositData::new( withdrawal.index, gwei_to_sats(withdrawal.amount), - withdrawal.address.as_slice().to_vec(), + withdrawal.destination.as_slice().to_vec(), )) }) .collect(); @@ -218,22 +223,24 @@ impl RpcExecEngineInner { .map_err(|_| EngineError::Other("Invalid payload".to_string()))?; // actually bridge-in deposits - let withdrawals: Vec = payload - .ops() - .iter() - .filter_map(|op| match op { - Op::Deposit(deposit_data) => Some(Withdrawal { - index: deposit_data.intent_idx(), - address: address_from_slice(deposit_data.dest_addr())?, - amount: sats_to_gwei(deposit_data.amt())?, - validator_index: 0, - }), - }) - .collect(); + // FIXME: I have no idea how to appease EVM here. + // let withdrawals: Vec = payload + // .ops() + // .iter() + // .filter_map(|op| match op { + // Op::Deposit(deposit_data) => Some(Withdrawal { + // index: deposit_data.intent_idx(), + // address: address_from_slice(deposit_data.dest_addr())?, + // amount: sats_to_gwei(deposit_data.amt())?, + // validator_index: 0, + // }), + // }) + // .collect(); let v2_payload = ExecutionPayloadInputV2 { execution_payload: el_payload.into(), - withdrawals: Some(withdrawals), + // withdrawals: Some(withdrawals), + withdrawals: None, // FIXME: I have no idea how to appease EVM here. }; let payload_status_result = self.client.new_payload_v2(v2_payload).await; @@ -397,8 +404,9 @@ struct ForkchoiceStatePartial { fn to_bridge_withdrawal_intent( rpc_withdrawal_intent: strata_reth_node::WithdrawalIntent, ) -> bridge_ops::WithdrawalIntent { - let strata_reth_node::WithdrawalIntent { amt, dest_pk } = rpc_withdrawal_intent; - bridge_ops::WithdrawalIntent::new(BitcoinAmount::from_sat(amt), XOnlyPk::new(Buf32(*dest_pk))) + let strata_reth_node::WithdrawalIntent { amt, destination } = rpc_withdrawal_intent; + let destination = Descriptor::from_bytes(destination.as_ref()).expect("valid descriptor"); + bridge_ops::WithdrawalIntent::new(BitcoinAmount::from_sat(amt), destination) } #[cfg(test)] diff --git a/crates/proof-impl/evm-ee-stf/src/processor.rs b/crates/proof-impl/evm-ee-stf/src/processor.rs index 84ab2a85c..5f0618017 100644 --- a/crates/proof-impl/evm-ee-stf/src/processor.rs +++ b/crates/proof-impl/evm-ee-stf/src/processor.rs @@ -239,7 +239,7 @@ where .checked_mul(withdrawal.amount.try_into().unwrap()) .unwrap(); - increase_account_balance(&mut evm.context.evm.db, withdrawal.address, amount_wei) + increase_account_balance(&mut evm.context.evm.db, withdrawal.destination, amount_wei) .unwrap(); } diff --git a/crates/proof-impl/evm-ee-stf/src/utils.rs b/crates/proof-impl/evm-ee-stf/src/utils.rs index 22b6b51cd..c334aac24 100644 --- a/crates/proof-impl/evm-ee-stf/src/utils.rs +++ b/crates/proof-impl/evm-ee-stf/src/utils.rs @@ -1,7 +1,5 @@ use strata_primitives::{ - buf::Buf32, - evm_exec::create_evm_extra_payload, - l1::{BitcoinAmount, XOnlyPk}, + bitcoin_bosd::Descriptor, buf::Buf32, evm_exec::create_evm_extra_payload, l1::BitcoinAmount, }; use strata_state::{ block::ExecSegment, @@ -17,10 +15,9 @@ pub fn generate_exec_update(el_proof_pp: &EvmBlockStfOutput) -> ExecSegment { .withdrawal_intents .iter() .map(|intent| { - bridge_ops::WithdrawalIntent::new( - BitcoinAmount::from_sat(intent.amt), - XOnlyPk::new(intent.dest_pk.into()), - ) + let descriptor = + Descriptor::from_bytes(intent.destination.as_ref()).expect("valid descriptor"); + bridge_ops::WithdrawalIntent::new(BitcoinAmount::from_sat(intent.amt), descriptor) }) .collect::>(); @@ -31,7 +28,7 @@ pub fn generate_exec_update(el_proof_pp: &EvmBlockStfOutput) -> ExecSegment { Op::Deposit(ELDepositData::new( request.index, gwei_to_sats(request.amount), - request.address.as_slice().to_vec(), + request.destination.as_slice().to_vec(), )) }) .collect::>(); diff --git a/crates/proof-impl/evm-ee-stf/test_data/witness_params.json b/crates/proof-impl/evm-ee-stf/test_data/witness_params.json index 8b78e0b25..131bf6877 100644 --- a/crates/proof-impl/evm-ee-stf/test_data/witness_params.json +++ b/crates/proof-impl/evm-ee-stf/test_data/witness_params.json @@ -212,7 +212,7 @@ "withdrawal_intents": [ { "amt": 1000000000, - "dest_pk": "0xffab0d5c1a6a719fb807387792d63c7f30f6c8205e4150e0267ec95774106d53" + "destination": [4, 255, 171, 13, 92, 26, 106, 113, 159, 184, 7, 56, 119, 146, 214, 60, 127, 48, 246, 200, 32, 94, 65, 80, 224, 38, 126, 201, 87, 116, 16, 109, 83] } ], "deposit_requests": [] diff --git a/crates/reth/db/test_data/witness_params.json b/crates/reth/db/test_data/witness_params.json index 8b78e0b25..131bf6877 100644 --- a/crates/reth/db/test_data/witness_params.json +++ b/crates/reth/db/test_data/witness_params.json @@ -212,7 +212,7 @@ "withdrawal_intents": [ { "amt": 1000000000, - "dest_pk": "0xffab0d5c1a6a719fb807387792d63c7f30f6c8205e4150e0267ec95774106d53" + "destination": [4, 255, 171, 13, 92, 26, 106, 113, 159, 184, 7, 56, 119, 146, 214, 60, 127, 48, 246, 200, 32, 94, 65, 80, 224, 38, 126, 201, 87, 116, 16, 109, 83] } ], "deposit_requests": [] diff --git a/crates/reth/evm/Cargo.toml b/crates/reth/evm/Cargo.toml index 6b4d7358c..1a5d7a9cd 100644 --- a/crates/reth/evm/Cargo.toml +++ b/crates/reth/evm/Cargo.toml @@ -9,6 +9,7 @@ strata-primitives.workspace = true strata-reth-primitives.workspace = true alloy-sol-types.workspace = true +hex.workspace = true reth-primitives.workspace = true revm.workspace = true revm-primitives.workspace = true diff --git a/crates/reth/evm/src/precompiles/bridge.rs b/crates/reth/evm/src/precompiles/bridge.rs index afcc5b701..a3e98f05e 100644 --- a/crates/reth/evm/src/precompiles/bridge.rs +++ b/crates/reth/evm/src/precompiles/bridge.rs @@ -1,20 +1,14 @@ -use std::array::TryFromSliceError; - use revm::{ primitives::{PrecompileError, PrecompileErrors, PrecompileOutput, PrecompileResult}, ContextStatefulPrecompile, Database, }; -use revm_primitives::{Bytes, FixedBytes, Log, LogData, U256}; +use revm_primitives::{Bytes, Log, LogData, U256}; +use strata_primitives::bitcoin_bosd::Descriptor; use strata_reth_primitives::WithdrawalIntentEvent; pub use crate::constants::BRIDGEOUT_ADDRESS; use crate::utils::wei_to_sats; -/// Ensure that input is exactly 32 bytes -fn try_into_pubkey(maybe_pubkey: &Bytes) -> Result, TryFromSliceError> { - maybe_pubkey.as_ref().try_into() -} - /// Custom precompile to burn rollup native token and add bridge out intent of equal amount. /// Bridge out intent is created during block payload generation. /// This precompile validates transaction and burns the bridge out amount. @@ -33,13 +27,15 @@ impl BridgeoutPrecompile { impl ContextStatefulPrecompile for BridgeoutPrecompile { fn call( &self, - dest_pk_bytes: &Bytes, + destination_bytes: &Bytes, _gas_limit: u64, evmctx: &mut revm::InnerEvmContext, ) -> PrecompileResult { - // Validate the length of the destination public key - let dest_pk = try_into_pubkey(dest_pk_bytes) - .map_err(|_| PrecompileError::other("Invalid public key length: expected 32 bytes"))?; + // Validate the descriptor + let destination = Descriptor::from_bytes(destination_bytes.as_ref()) + .map_err(|_| PrecompileError::other("Invalid destination descriptor"))?; + + let destination_str = destination.to_string(); // Verify that the transaction value matches the required withdrawal amount let withdrawal_amount = evmctx.env.tx.value; @@ -59,7 +55,10 @@ impl ContextStatefulPrecompile for BridgeoutPrecompile { })?; // Log the bridge withdrawal intent - let evt = WithdrawalIntentEvent { amount, dest_pk }; + let evt = WithdrawalIntentEvent { + amount, + destination: destination_str, + }; let logdata = LogData::from(&evt); evmctx.journaled_state.log(Log { diff --git a/crates/reth/evm/src/utils.rs b/crates/reth/evm/src/utils.rs index a9473fb20..b255d52aa 100644 --- a/crates/reth/evm/src/utils.rs +++ b/crates/reth/evm/src/utils.rs @@ -1,4 +1,5 @@ use alloy_sol_types::SolEvent; +use hex::decode; use reth_primitives::Receipt; use revm_primitives::U256; use strata_reth_primitives::{WithdrawalIntent, WithdrawalIntentEvent}; @@ -32,9 +33,13 @@ pub fn collect_withdrawal_intents( .filter(|log| log.address == BRIDGEOUT_ADDRESS) .filter_map(|log| { WithdrawalIntentEvent::decode_log(&log, true) - .map(|evt| WithdrawalIntent { - amt: evt.amount, - dest_pk: evt.dest_pk, + .map(|evt| { + let destination_bytes = + decode(&evt.destination).expect("valid bytes from a hex string"); + WithdrawalIntent { + amt: evt.amount, + destination: destination_bytes, + } }) .ok() }) diff --git a/crates/reth/exex/src/alloy2reth.rs b/crates/reth/exex/src/alloy2reth.rs index 21676a9d3..4ea5fbb0c 100644 --- a/crates/reth/exex/src/alloy2reth.rs +++ b/crates/reth/exex/src/alloy2reth.rs @@ -12,7 +12,7 @@ impl IntoReth for AlloyWithdrawal { index: self.index, validator_index: self.validator_index, amount: self.amount, - address: self.address, + destination: self.destination, } } } diff --git a/crates/reth/primitives/Cargo.toml b/crates/reth/primitives/Cargo.toml index cc7734034..e9491fbe4 100644 --- a/crates/reth/primitives/Cargo.toml +++ b/crates/reth/primitives/Cargo.toml @@ -5,5 +5,4 @@ version = "0.1.0" [dependencies] alloy-sol-types.workspace = true -reth-primitives.workspace = true serde.workspace = true diff --git a/crates/reth/primitives/src/lib.rs b/crates/reth/primitives/src/lib.rs index 04b781af2..ccff7da12 100644 --- a/crates/reth/primitives/src/lib.rs +++ b/crates/reth/primitives/src/lib.rs @@ -1,7 +1,6 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] use alloy_sol_types::sol; -use reth_primitives::revm_primitives::alloy_primitives::B256; use serde::{Deserialize, Serialize}; /// Type for withdrawal_intents in rpc. @@ -12,8 +11,8 @@ pub struct WithdrawalIntent { /// Amount to be withdrawn in sats. pub amt: u64, - /// Destination public key for the withdrawal - pub dest_pk: B256, + /// Dynamic-sized bytes BOSD descriptor for the withdrawal destinations in L1. + pub destination: Vec, } sol! { @@ -21,7 +20,7 @@ sol! { event WithdrawalIntentEvent( /// Withdrawal amount in sats uint64 amount, - /// 32 bytes pubkey for withdrawal address in L1 - bytes32 dest_pk, + /// hex-encoded string BOSD descriptor for withdrawal destinations in L1 + string destination, ); }