Skip to content

Commit

Permalink
fix(evm): Using BOSD descriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
storopoli committed Jan 15, 2025
1 parent 956ac52 commit d3143fa
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 115 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 0 additions & 44 deletions crates/bridge-tx-builder/src/withdrawal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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
Expand Down
80 changes: 44 additions & 36 deletions crates/evmexec/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -108,27 +105,35 @@ impl<T: EngineRpc> RpcExecEngineInner<T> {
prev_block: EVML2Block,
) -> EngineResult<u64> {
// 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::<Result<_, _>>()?;
// 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::<Result<_, _>>()?;

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,
});
Expand Down Expand Up @@ -180,7 +185,7 @@ impl<T: EngineRpc> RpcExecEngineInner<T> {
Op::Deposit(ELDepositData::new(
withdrawal.index,
gwei_to_sats(withdrawal.amount),
withdrawal.address.as_slice().to_vec(),
withdrawal.destination.as_slice().to_vec(),
))
})
.collect();
Expand Down Expand Up @@ -218,22 +223,24 @@ impl<T: EngineRpc> RpcExecEngineInner<T> {
.map_err(|_| EngineError::Other("Invalid payload".to_string()))?;

// actually bridge-in deposits
let withdrawals: Vec<Withdrawal> = 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<Withdrawal> = 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;
Expand Down Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion crates/proof-impl/evm-ee-stf/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
13 changes: 5 additions & 8 deletions crates/proof-impl/evm-ee-stf/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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::<Vec<_>>();

Expand All @@ -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::<Vec<_>>();
Expand Down
2 changes: 1 addition & 1 deletion crates/proof-impl/evm-ee-stf/test_data/witness_params.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": []
Expand Down
2 changes: 1 addition & 1 deletion crates/reth/db/test_data/witness_params.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": []
Expand Down
1 change: 1 addition & 0 deletions crates/reth/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 12 additions & 13 deletions crates/reth/evm/src/precompiles/bridge.rs
Original file line number Diff line number Diff line change
@@ -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<FixedBytes<32>, 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.
Expand All @@ -33,13 +27,15 @@ impl BridgeoutPrecompile {
impl<DB: Database> ContextStatefulPrecompile<DB> for BridgeoutPrecompile {
fn call(
&self,
dest_pk_bytes: &Bytes,
destination_bytes: &Bytes,
_gas_limit: u64,
evmctx: &mut revm::InnerEvmContext<DB>,
) -> 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;
Expand All @@ -59,7 +55,10 @@ impl<DB: Database> ContextStatefulPrecompile<DB> 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 {
Expand Down
11 changes: 8 additions & 3 deletions crates/reth/evm/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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()
})
Expand Down
2 changes: 1 addition & 1 deletion crates/reth/exex/src/alloy2reth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl IntoReth<RethWithdrawal> for AlloyWithdrawal {
index: self.index,
validator_index: self.validator_index,
amount: self.amount,
address: self.address,
destination: self.destination,
}
}
}
1 change: 0 additions & 1 deletion crates/reth/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ version = "0.1.0"

[dependencies]
alloy-sol-types.workspace = true
reth-primitives.workspace = true
serde.workspace = true
9 changes: 4 additions & 5 deletions crates/reth/primitives/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -12,16 +11,16 @@ 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<u8>,
}

sol! {
#[allow(missing_docs)]
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,
);
}

0 comments on commit d3143fa

Please sign in to comment.