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

Withdrawal intent 32byte pubkey #288

Merged
merged 4 commits into from
Sep 18, 2024
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
11 changes: 7 additions & 4 deletions crates/evmexec/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
errors::{EngineError, EngineResult},
messages::{ELDepositData, ExecPayloadData, Op, PayloadEnv},
};
use alpen_express_primitives::buf::Buf64;
use alpen_express_primitives::{
buf::Buf32,
l1::{BitcoinAmount, XOnlyPk},
};
use alpen_express_state::{
block::L2BlockBundle,
bridge_ops,
Expand Down Expand Up @@ -174,7 +177,7 @@

let withdrawal_intents = rpc_withdrawal_intents
.into_iter()
.map(to_bridge_withdrawal_intents)
.map(to_bridge_withdrawal_intent)
.collect();

let update_output =
Expand Down Expand Up @@ -350,11 +353,11 @@
pub finalized_block_hash: Option<B256>,
}

fn to_bridge_withdrawal_intents(
fn to_bridge_withdrawal_intent(

Check warning on line 356 in crates/evmexec/src/engine.rs

View check run for this annotation

Codecov / codecov/patch

crates/evmexec/src/engine.rs#L356

Added line #L356 was not covered by tests
sapinb marked this conversation as resolved.
Show resolved Hide resolved
rpc_withdrawal_intent: express_reth_node::WithdrawalIntent,
) -> bridge_ops::WithdrawalIntent {
let express_reth_node::WithdrawalIntent { amt, dest_pk } = rpc_withdrawal_intent;
bridge_ops::WithdrawalIntent::new(amt, Buf64(dest_pk))
bridge_ops::WithdrawalIntent::new(BitcoinAmount::from_sat(amt), XOnlyPk::new(Buf32(dest_pk)))

Check warning on line 360 in crates/evmexec/src/engine.rs

View check run for this annotation

Codecov / codecov/patch

crates/evmexec/src/engine.rs#L360

Added line #L360 was not covered by tests
}

#[cfg(test)]
Expand Down
10 changes: 9 additions & 1 deletion crates/reth/node/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
pub use crate::precompiles::bridge::BRIDGEOUT_ADDRESS;
use revm_primitives::{address, Address, U256};

use crate::utils::{u256_from, WEI_PER_BTC};

/// The address for the Bridgeout precompile contract.
pub const BRIDGEOUT_ADDRESS: Address = address!("000000000000000000000000000000000b121d9e");

/// The fixed withdrawal amount in wei (10 BTC equivalent).
pub const FIXED_WITHDRAWAL_WEI: U256 = u256_from(10 * WEI_PER_BTC);
6 changes: 4 additions & 2 deletions crates/reth/node/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
};
use revm_primitives::{Address, AnalysisKind, Bytes, CfgEnvWithHandlerCfg, Env, TxEnv, U256};

use crate::{constants::FIXED_WITHDRAWAL_WEI, precompiles};

/// Custom EVM configuration
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
Expand All @@ -33,9 +35,9 @@
handler.pre_execution.load_precompiles = Arc::new(move || {
let mut precompiles = ContextPrecompiles::new(PrecompileSpecId::from_spec_id(spec_id));
precompiles.extend([(
crate::precompiles::bridge::BRIDGEOUT_ADDRESS,
precompiles::bridge::BRIDGEOUT_ADDRESS,

Check warning on line 38 in crates/reth/node/src/evm.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/evm.rs#L38

Added line #L38 was not covered by tests
ContextPrecompile::ContextStateful(Arc::new(
crate::precompiles::bridge::BridgeoutPrecompile::default(),
precompiles::bridge::BridgeoutPrecompile::new(FIXED_WITHDRAWAL_WEI),

Check warning on line 40 in crates/reth/node/src/evm.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/evm.rs#L40

Added line #L40 was not covered by tests
)),
)]);
precompiles
Expand Down
1 change: 1 addition & 0 deletions crates/reth/node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod payload;
mod payload_builder;
mod precompiles;
mod primitives;
mod utils;

pub use engine::ExpressEngineTypes;
pub use node::ExpressEthereumNode;
Expand Down
2 changes: 1 addition & 1 deletion crates/reth/node/src/payload_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@
};
Some(WithdrawalIntent {
amt: evt.amount,
dest_pk: evt.dest_pk.as_ref().try_into().unwrap(),
dest_pk: evt.dest_pk,

Check warning on line 402 in crates/reth/node/src/payload_builder.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/payload_builder.rs#L402

Added line #L402 was not covered by tests
})
})
.collect();
Expand Down
90 changes: 45 additions & 45 deletions crates/reth/node/src/precompiles/bridge.rs
Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
use std::array::TryFromSliceError;

use revm::{ContextStatefulPrecompile, Database};
use revm_primitives::{
address, Address, Bytes, Log, LogData, PrecompileError, PrecompileErrors, PrecompileOutput,
Bytes, FixedBytes, Log, LogData, PrecompileError, PrecompileErrors, PrecompileOutput,
PrecompileResult, U256,
};

use crate::primitives::WithdrawalIntentEvent;
pub use crate::constants::BRIDGEOUT_ADDRESS;
use crate::{primitives::WithdrawalIntentEvent, utils::wei_to_sats};

// TODO: address?
pub const BRIDGEOUT_ADDRESS: Address = address!("000000000000000000000000000000000b121d9e");
const MIN_WITHDRAWAL_WEI: u128 = 1_000_000_000_000_000_000u128;
/// Ensure that input is exactly 32 bytes
fn try_into_pubkey(maybe_pubkey: &Bytes) -> Result<FixedBytes<32>, TryFromSliceError> {
maybe_pubkey.as_ref().try_into()
}

Check warning on line 15 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L13-L15

Added lines #L13 - L15 were not covered by tests

/// 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.
pub struct BridgeoutPrecompile {
min_withdrawal_wei: U256,
fixed_withdrawal_wei: U256,
}

impl Default for BridgeoutPrecompile {
fn default() -> Self {
impl BridgeoutPrecompile {
pub fn new(fixed_withdrawal_wei: U256) -> Self {

Check warning on line 25 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L25

Added line #L25 was not covered by tests
Self {
min_withdrawal_wei: U256::from(MIN_WITHDRAWAL_WEI),
fixed_withdrawal_wei,

Check warning on line 27 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L27

Added line #L27 was not covered by tests
}
}
}

impl<DB: Database> ContextStatefulPrecompile<DB> for BridgeoutPrecompile {
fn call(
&self,
bytes: &Bytes,
dest_pk_bytes: &Bytes,

Check warning on line 35 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L35

Added line #L35 was not covered by tests
_gas_limit: u64,
evmctx: &mut revm::InnerEvmContext<DB>,
) -> PrecompileResult {
// ensure valid calldata
if bytes.len() != 64 {
return Err(PrecompileErrors::Error(PrecompileError::other(
"invalid data",
)));
}
// 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"))?;

Check warning on line 41 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L40-L41

Added lines #L40 - L41 were not covered by tests

// ensure minimum bridgeout amount
let value = evmctx.env.tx.value;
if value < self.min_withdrawal_wei {
return Err(PrecompileErrors::Error(PrecompileError::other(
"below min withdrawal amt",
)));
// Verify that the transaction value matches the required withdrawal amount
let withdrawal_amount = evmctx.env.tx.value;
if withdrawal_amount != self.fixed_withdrawal_wei {
return Err(PrecompileError::other(
"Invalid withdrawal value: must be exactly 10 BTC in wei",
)
.into());

Check warning on line 49 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L44-L49

Added lines #L44 - L49 were not covered by tests
}

let (sats, rem) = value.div_rem(U256::from(10_000_000_000u128));
// Convert wei to satoshis
let (sats, _) = wei_to_sats(withdrawal_amount);

Check warning on line 53 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L52-L53

Added lines #L52 - L53 were not covered by tests

if !rem.is_zero() {
// ensure there are no leftovers that get lost.
// is this important?
return Err(PrecompileErrors::Error(PrecompileError::other(
"value must be exact sats",
)));
}
// Try converting sats (U256) into u64 amount
let amount: u64 = sats.try_into().map_err(|_| PrecompileErrors::Fatal {
msg: "Withdrawal amount exceeds maximum allowed value".into(),
})?;

Check warning on line 58 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L56-L58

Added lines #L56 - L58 were not covered by tests

let Ok(amount) = sats.try_into() else {
// should never happen. 2^64 ~ 8700 x total_btc_stats
return Err(PrecompileErrors::Error(PrecompileError::other(
"above max withdrawal amt",
)));
};

// log bridge withdrawal intent
let evt = WithdrawalIntentEvent {
amount,
dest_pk: bytes.clone(),
};
// Log the bridge withdrawal intent
let evt = WithdrawalIntentEvent { amount, dest_pk };

Check warning on line 61 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L61

Added line #L61 was not covered by tests
let logdata = LogData::from(&evt);

evmctx.journaled_state.log(Log {
address: BRIDGEOUT_ADDRESS,
data: logdata,
});

// TODO: burn value
// Burn value sent to bridge by adjusting the account balance of bridge precompile
let (account, _) = evmctx
.load_account(BRIDGEOUT_ADDRESS)
// Error case should never occur
.map_err(|_| PrecompileErrors::Fatal {
msg: "Failed to load BRIDGEOUT_ADDRESS account".into(),
})?;

Check warning on line 75 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L70-L75

Added lines #L70 - L75 were not covered by tests

account.info.balance = U256::ZERO;

// TODO: Properly calculate and deduct gas for the bridge out operation
let gas_cost = 0;

Check warning on line 80 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L77-L80

Added lines #L77 - L80 were not covered by tests

// TODO: gas for bridge out, using 0 gas currently
Ok(PrecompileOutput::new(0, Bytes::new()))
Ok(PrecompileOutput::new(gas_cost, Bytes::new()))

Check warning on line 82 in crates/reth/node/src/precompiles/bridge.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/precompiles/bridge.rs#L82

Added line #L82 was not covered by tests
}
}
10 changes: 6 additions & 4 deletions crates/reth/node/src/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
use alloy_sol_types::sol;
use reth_primitives::B512;
use reth_primitives::B256;
use serde::{Deserialize, Serialize};

/// Type for withdrawal_intents in rpc.
/// Distinct from [`bridge_ops::WithdrawalIntents`] as this will live in reth repo eventually
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct WithdrawalIntent {
/// Amount of currency to be withdrawn.
/// Amount to be withdrawn in sats.
pub amt: u64,

/// Destination public key for the withdrawal
pub dest_pk: B512,
pub dest_pk: B256,
}

sol! {
#[allow(missing_docs)]
event WithdrawalIntentEvent(
/// Withdrawal amount in sats
uint64 amount,
bytes dest_pk,
/// 32 bytes pubkey for withdrawal address in L1
bytes32 dest_pk,
);
}
17 changes: 17 additions & 0 deletions crates/reth/node/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use revm_primitives::U256;

pub const fn u256_from(val: u128) -> U256 {
U256::from_limbs([(val & ((1 << 64) - 1)) as u64, (val >> 64) as u64, 0, 0])
}

Check warning on line 5 in crates/reth/node/src/utils.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/utils.rs#L3-L5

Added lines #L3 - L5 were not covered by tests

/// Number of wei per rollup BTC (1e18).
pub const WEI_PER_BTC: u128 = 1_000_000_000_000_000_000u128;

/// Number of wei per satoshi (1e10).
const WEI_PER_SAT: U256 = u256_from(10_000_000_000u128);

/// Converts wei to satoshis.
/// Returns a tuple of (satoshis, remainder_in_wei).
pub fn wei_to_sats(wei: U256) -> (U256, U256) {
wei.div_rem(WEI_PER_SAT)
}

Check warning on line 17 in crates/reth/node/src/utils.rs

View check run for this annotation

Codecov / codecov/patch

crates/reth/node/src/utils.rs#L15-L17

Added lines #L15 - L17 were not covered by tests
21 changes: 12 additions & 9 deletions crates/state/src/bridge_ops.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Types for managing pending bridging operations in the CL state.

use alpen_express_primitives::{buf::Buf64, l1::BitcoinAmount};
use alpen_express_primitives::{
buf::Buf32,
l1::{BitcoinAmount, XOnlyPk},
};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};

Expand All @@ -10,26 +13,26 @@
#[derive(Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
pub struct WithdrawalIntent {
/// Quantity of L1 asset, for Bitcoin this is sats.
amt: u64,
amt: BitcoinAmount,

/// Destination public key for the withdrawal
pub dest_pk: Buf64,
pub dest_pk: XOnlyPk,
}

impl WithdrawalIntent {
pub fn new(amt: u64, dest_pk: Buf64) -> Self {
pub fn new(amt: BitcoinAmount, dest_pk: XOnlyPk) -> Self {

Check warning on line 23 in crates/state/src/bridge_ops.rs

View check run for this annotation

Codecov / codecov/patch

crates/state/src/bridge_ops.rs#L23

Added line #L23 was not covered by tests
Self { amt, dest_pk }
}

pub fn into_parts(&self) -> (u64, Buf64) {
(self.amt, self.dest_pk)
pub fn as_parts(&self) -> (u64, &Buf32) {
(self.amt.to_sat(), self.dest_pk.buf32())

Check warning on line 28 in crates/state/src/bridge_ops.rs

View check run for this annotation

Codecov / codecov/patch

crates/state/src/bridge_ops.rs#L27-L28

Added lines #L27 - L28 were not covered by tests
}

pub fn amt(&self) -> &u64 {
pub fn amt(&self) -> &BitcoinAmount {

Check warning on line 31 in crates/state/src/bridge_ops.rs

View check run for this annotation

Codecov / codecov/patch

crates/state/src/bridge_ops.rs#L31

Added line #L31 was not covered by tests
&self.amt
}

pub fn dest_pk(&self) -> &Buf64 {
pub fn dest_pk(&self) -> &XOnlyPk {

Check warning on line 35 in crates/state/src/bridge_ops.rs

View check run for this annotation

Codecov / codecov/patch

crates/state/src/bridge_ops.rs#L35

Added line #L35 was not covered by tests
&self.dest_pk
}
}
Expand All @@ -44,7 +47,7 @@
impl WithdrawalBatch {
/// Gets the total value of the batch. This must be less than the size of
/// the utxo it's assigned to.
pub fn get_total_value(&self) -> u64 {
pub fn get_total_value(&self) -> BitcoinAmount {

Check warning on line 50 in crates/state/src/bridge_ops.rs

View check run for this annotation

Codecov / codecov/patch

crates/state/src/bridge_ops.rs#L50

Added line #L50 was not covered by tests
self.intents.iter().map(|wi| wi.amt).sum()
}

Expand Down
3 changes: 3 additions & 0 deletions functional-tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
"l1_reorg_safe_depth": 4,
"target_l2_batch_size": 5,
}

# custom precompiles
PRECOMPILE_BRIDGEOUT_ADDRESS = "0x000000000000000000000000000000000b121d9e"
Loading
Loading