Skip to content
Closed
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
12 changes: 10 additions & 2 deletions via_verifier/lib/via_da_client/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@ use anyhow::Context;
use byteorder::{BigEndian, ReadBytesExt};
use zksync_types::{u256_to_bytes_be, u256_to_h256, Address, H160, H256, U256};

/// The function selector used in L2 to compute the message.
pub const WITHDRAW_FUNC_SIG: &str = "finalizeEthWithdrawal(uint256,uint256,uint16,bytes,bytes32[])";
/// Legacy function selector used in L2 messages before Bridgehub/L1Nullifier migration.
pub const WITHDRAW_FUNC_SIG_LEGACY: &str =
"finalizeEthWithdrawal(uint256,uint256,uint16,bytes,bytes32[])";
Comment on lines +8 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The constant WITHDRAW_FUNC_SIG has been removed and replaced by WITHDRAW_FUNC_SIG_LEGACY. If this constant is used by other crates in the workspace or by external consumers, this change will break their compilation. Consider keeping WITHDRAW_FUNC_SIG as an alias (possibly marked with #[deprecated]) to maintain backward compatibility during the migration period.


/// New function selector expected after migrating to L1Nullifier finalize flow.
pub const WITHDRAW_FUNC_SIG_NULLIFIER: &str =
"finalizeDeposit(uint256,uint256,uint16,bytes,bytes32[])";

/// Supported L2 message function signatures during migration.
pub const WITHDRAW_FUNC_SIGS: [&str; 2] = [WITHDRAW_FUNC_SIG_LEGACY, WITHDRAW_FUNC_SIG_NULLIFIER];

/// The L2 BaseToken address.
pub const L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR: &str = "000000000000000000000000000000000000800a";
Expand Down
66 changes: 60 additions & 6 deletions via_verifier/lib/via_withdrawal_client/src/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bitcoin::{
Address as BitcoinAddress, Amount, Network,
};
use ethers::abi::{decode, ParamType};
use via_da_client::types::WITHDRAW_FUNC_SIG;
use via_da_client::types::WITHDRAW_FUNC_SIGS;
use via_verifier_types::withdrawal::WithdrawalRequest;
use zksync_basic_types::{web3::keccak256, U256};
use zksync_types::{api::Log, Address};
Expand Down Expand Up @@ -35,7 +35,10 @@ pub fn parse_l2_withdrawal_message(
}

let func_selector_bytes = &l2_to_l1_message[0..4];
if func_selector_bytes != _get_withdraw_function_selector() {
if !_get_supported_withdraw_function_selectors()
.iter()
.any(|selector| func_selector_bytes == selector)
{
return Err(anyhow::format_err!("Invalid message function selector."));
}

Expand Down Expand Up @@ -112,10 +115,15 @@ pub fn parse_l2_withdrawal_message(
})
}

/// Get the withdrawal function selector.
fn _get_withdraw_function_selector() -> Vec<u8> {
let hash = keccak256(WITHDRAW_FUNC_SIG.as_bytes());
hash[0..4].to_vec()
/// Get all supported withdrawal function selectors.
fn _get_supported_withdraw_function_selectors() -> Vec<Vec<u8>> {
WITHDRAW_FUNC_SIGS
.iter()
.map(|sig| {
let hash = keccak256(sig.as_bytes());
hash[0..4].to_vec()
})
.collect()
}
Comment on lines +119 to 127
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The function _get_supported_withdraw_function_selectors is called on every invocation of parse_l2_withdrawal_message. It performs multiple keccak256 hashes and several Vec allocations each time. Since the function signatures are static constants, these 4-byte selectors should be computed once or defined as constants to improve performance, especially when processing large batches of withdrawals. Consider using std::sync::OnceLock (available in Rust 1.70+) or a similar lazy initialization pattern to cache the results.


#[cfg(test)]
Expand Down Expand Up @@ -215,4 +223,50 @@ mod tests {
assert_eq!(res.receiver, expected_receiver);
assert_eq!(res.amount, expected_amount);
}

#[test]
fn test_parse_l2_withdrawal_message_accepts_nullifier_selector() {
let btc_bytes = b"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".to_vec();
let amount = U256::from("0000000000000000000000000000000000000000000000000de0b6b3a7640000");
let encoded_data = encode(&[Token::Bytes(btc_bytes.clone()), Token::Uint(amount.clone())]);
let data = Bytes::from(encoded_data);

let log = Log {
block_timestamp: None,
l1_batch_number: Some(U64::one()),
address: H160::random(),
topics: vec![
H256::from_str(
"0x2d6ef0fc97a54b2a96a5f3c96e3e69dca5b8d5ef4f68f01472c9e7c2b8d1f17b",
)
.unwrap(),
H256::from_str(
"0x000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
)
.unwrap(),
],
data,
block_hash: None,
block_number: Some(U64::one()),
transaction_hash: Some(H256::zero()),
transaction_index: None,
log_index: Some(U256::zero()),
transaction_log_index: Some(U256::zero()),
log_type: None,
removed: None,
};

let mut l2_to_l1_message = hex::decode("6c0960f93141317a5031655035514765666932444d505466544c35534c6d7637446976664e610000000000000000000000000000000000000000000000000de0b6b3a7640000").unwrap();
let supported_selectors = _get_supported_withdraw_function_selectors();
l2_to_l1_message[0..4].copy_from_slice(&supported_selectors[1]);

let expected_receiver = BitcoinAddress::from_str("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
.unwrap()
.assume_checked();
let expected_amount = Amount::from_sat(1000000000000000000);
let res = parse_l2_withdrawal_message(l2_to_l1_message, log, Network::Bitcoin).unwrap();

assert_eq!(res.receiver, expected_receiver);
assert_eq!(res.amount, expected_amount);
}
}
Loading