Support for type-2 transactions (#1052)

* Type-2 txns

* Minor

* Minor

* Fix add11_yml block metadata

* Fix simple_transfer test

* Minor
This commit is contained in:
wborgeaud 2023-05-23 15:06:26 +02:00 committed by GitHub
parent 9b0092ab1d
commit 6e3036017e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 170 additions and 22 deletions

View File

@ -18,22 +18,27 @@ global process_normalized_txn:
// Assert gas_limit >= intrinsic_gas.
%mload_txn_field(@TXN_FIELD_INTRINSIC_GAS)
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
%assert_ge
%assert_ge(invalid_txn)
// Assert block gas limit >= txn gas limit.
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_LIMIT)
%assert_ge(invalid_txn)
%mload_txn_field(@TXN_FIELD_ORIGIN)
// stack: sender, retdest
// Check that txn nonce matches account nonce.
DUP1 %nonce
DUP1 %eq_const(@MAX_NONCE) %assert_zero // EIP-2681
DUP1 %eq_const(@MAX_NONCE) %assert_zero(invalid_txn) // EIP-2681
// stack: sender_nonce, sender, retdest
%mload_txn_field(@TXN_FIELD_NONCE)
// stack: tx_nonce, sender_nonce, sender, retdest
%assert_eq
%assert_eq(invalid_txn)
// stack: sender, retdest
// Assert sender has no code.
DUP1 %ext_code_empty %assert_nonzero
DUP1 %ext_code_empty %assert_nonzero(invalid_txn)
// stack: sender, retdest
// Assert sender balance >= gas_limit * gas_price + value.
@ -44,7 +49,7 @@ global process_normalized_txn:
MUL
%mload_txn_field(@TXN_FIELD_VALUE)
ADD
%assert_le
%assert_le(invalid_txn)
// stack: retdest
// Assert chain ID matches block metadata
@ -58,7 +63,7 @@ global process_normalized_txn:
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_CHAIN_ID)
MUL
// stack: filtered_block_chain_id, filtered_tx_chain_id, retdest
%assert_eq
%assert_eq(invalid_txn)
// stack: retdest
global buy_gas:
@ -348,8 +353,9 @@ process_message_txn_fail:
%mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS)
%mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS)
// stack: max_fee, max_priority_fee, base_fee
DUP3 DUP2 %assert_ge // Assert max_fee >= base_fee
DUP3 DUP2 %assert_ge(invalid_txn) // Assert max_fee >= base_fee
// stack: max_fee, max_priority_fee, base_fee
DUP2 DUP2 %assert_ge(invalid_txn) // Assert max_fee >= max_priority_fee
%stack (max_fee, max_priority_fee, base_fee) -> (max_fee, base_fee, max_priority_fee, base_fee)
SUB
// stack: max_fee - base_fee, max_priority_fee, base_fee
@ -399,3 +405,7 @@ contract_creation_fault_4:
%delete_all_touched_addresses
%delete_all_selfdestructed_addresses
JUMP
invalid_txn:
%jump(txn_loop)

View File

@ -34,6 +34,113 @@ global process_type_2_txn:
POP
// stack: retdest
// TODO: Check signature.
// From EIP-1559:
// The signature_y_parity, signature_r, signature_s elements of this transaction represent a secp256k1 signature over
// keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))
type_2_compute_signed_data:
%alloc_rlp_block
// stack: rlp_start, retdest
%mload_txn_field(@TXN_FIELD_CHAIN_ID)
// stack: chain_id, rlp_start, retdest
DUP2
// stack: rlp_pos, chain_id, rlp_start, retdest
%encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_NONCE)
SWAP1 %encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS)
SWAP1 %encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS)
SWAP1 %encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
SWAP1 %encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_TO)
DUP1 %jumpi(nonzero_to)
// stack: to, rlp_pos, rlp_start, retdest
SWAP1 %encode_rlp_scalar
%jump(after_to)
nonzero_to:
// stack: to, rlp_pos, rlp_start, retdest
SWAP1 %encode_rlp_160
// stack: rlp_pos, rlp_start, retdest
after_to:
%mload_txn_field(@TXN_FIELD_VALUE)
SWAP1 %encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
// Encode txn data.
%mload_txn_field(@TXN_FIELD_DATA_LEN)
PUSH 0 // ADDR.virt
PUSH @SEGMENT_TXN_DATA
PUSH 0 // ADDR.context
// stack: ADDR: 3, len, rlp_pos, rlp_start, retdest
PUSH after_serializing_txn_data
// stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest
SWAP5
// stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest
%jump(encode_rlp_string)
after_serializing_txn_data:
// Instead of manually encoding the access list, we just copy the raw RLP from the transaction.
%mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START)
%mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN)
%stack (al_len, al_start, rlp_pos, rlp_start, retdest) ->
(
0, @SEGMENT_RLP_RAW, rlp_pos,
0, @SEGMENT_RLP_RAW, al_start,
al_len,
after_serializing_access_list,
rlp_pos, rlp_start, retdest)
%jump(memcpy)
after_serializing_access_list:
// stack: rlp_pos, rlp_start, retdest
%mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD
// stack: rlp_pos, rlp_start, retdest
%prepend_rlp_list_prefix
// stack: prefix_start_pos, rlp_len, retdest
// Store a `2` in front of the RLP
%decrement
%stack (pos) -> (0, @SEGMENT_RLP_RAW, pos, 2, pos)
MSTORE_GENERAL
// stack: pos, rlp_len, retdest
// Hash the RLP + the leading `2`
SWAP1 %increment SWAP1
PUSH @SEGMENT_RLP_RAW
PUSH 0 // context
// stack: ADDR: 3, len, retdest
KECCAK_GENERAL
// stack: hash, retdest
%mload_txn_field(@TXN_FIELD_S)
%mload_txn_field(@TXN_FIELD_R)
%mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27
PUSH store_origin
// stack: store_origin, v, r, s, hash, retdest
SWAP4
// stack: hash, v, r, s, store_origin, retdest
%jump(ecrecover)
store_origin:
// stack: address, retdest
// If ecrecover returned u256::MAX, that indicates failure.
DUP1
%eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
%jumpi(panic)
// stack: address, retdest
%mstore_txn_field(@TXN_FIELD_ORIGIN)
// stack: retdest
%jump(process_normalized_txn)

View File

@ -8,17 +8,31 @@ global panic:
%jumpi(panic)
%endmacro
%macro assert_zero(ret)
%jumpi($ret)
%endmacro
// Consumes the top element and asserts that it is nonzero.
%macro assert_nonzero
ISZERO
%jumpi(panic)
%endmacro
%macro assert_nonzero(ret)
ISZERO
%jumpi($ret)
%endmacro
%macro assert_eq
EQ
%assert_nonzero
%endmacro
%macro assert_eq(ret)
EQ
%assert_nonzero($ret)
%endmacro
%macro assert_lt
// %assert_zero is cheaper than %assert_nonzero, so we will leverage the
// fact that (x < y) == !(x >= y).
@ -26,6 +40,11 @@ global panic:
%assert_zero
%endmacro
%macro assert_lt(ret)
GE
%assert_zero($ret)
%endmacro
%macro assert_le
// %assert_zero is cheaper than %assert_nonzero, so we will leverage the
// fact that (x <= y) == !(x > y).
@ -33,6 +52,11 @@ global panic:
%assert_zero
%endmacro
%macro assert_le(ret)
GT
%assert_zero($ret)
%endmacro
%macro assert_gt
// %assert_zero is cheaper than %assert_nonzero, so we will leverage the
// fact that (x > y) == !(x <= y).
@ -40,6 +64,11 @@ global panic:
%assert_zero
%endmacro
%macro assert_gt(ret)
LE
%assert_zero($ret)
%endmacro
%macro assert_ge
// %assert_zero is cheaper than %assert_nonzero, so we will leverage the
// fact that (x >= y) == !(x < y).
@ -47,6 +76,11 @@ global panic:
%assert_zero
%endmacro
%macro assert_ge(ret)
LT
%assert_zero($ret)
%endmacro
%macro assert_eq_const(c)
%eq_const($c)
%assert_nonzero

View File

@ -81,8 +81,12 @@ fn add11_yml() -> anyhow::Result<()> {
let block_metadata = BlockMetadata {
block_beneficiary: Address::from(beneficiary),
block_timestamp: 0x03e8.into(),
block_number: 1.into(),
block_difficulty: 0x020000.into(),
block_gaslimit: 0xff112233445566u64.into(),
block_chain_id: 1.into(),
block_base_fee: 0xa.into(),
..BlockMetadata::default()
};
let mut contract_code = HashMap::new();

View File

@ -37,15 +37,12 @@ fn test_simple_transfer() -> anyhow::Result<()> {
let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23");
let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0");
let beneficiary_state_key = keccak(beneficiary);
let sender_state_key = keccak(sender);
let to_state_key = keccak(to);
let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap();
let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap();
let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap();
let beneficiary_account_before = AccountRlp::default();
let sender_account_before = AccountRlp {
nonce: 5.into(),
balance: eth_to_wei(100_000.into()),
@ -72,7 +69,12 @@ fn test_simple_transfer() -> anyhow::Result<()> {
let block_metadata = BlockMetadata {
block_beneficiary: Address::from(beneficiary),
..BlockMetadata::default()
block_timestamp: 0x03e8.into(),
block_number: 1.into(),
block_difficulty: 0x020000.into(),
block_gaslimit: 0xff112233445566u64.into(),
block_chain_id: 1.into(),
block_base_fee: 0xa.into(),
};
let mut contract_code = HashMap::new();
@ -94,10 +96,6 @@ fn test_simple_transfer() -> anyhow::Result<()> {
let txdata_gas = 2 * 16;
let gas_used = 21_000 + txdata_gas;
let beneficiary_account_after = AccountRlp {
balance: beneficiary_account_before.balance + gas_used * 10,
..beneficiary_account_before
};
let sender_account_after = AccountRlp {
balance: sender_account_before.balance - value - gas_used * 10,
nonce: sender_account_before.nonce + 1,
@ -109,11 +107,6 @@ fn test_simple_transfer() -> anyhow::Result<()> {
};
let mut children = core::array::from_fn(|_| Node::Empty.into());
children[beneficiary_nibbles.get_nibble(0) as usize] = Node::Leaf {
nibbles: beneficiary_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&beneficiary_account_after).to_vec(),
}
.into();
children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf {
nibbles: sender_nibbles.truncate_n_nibbles_front(1),
value: rlp::encode(&sender_account_after).to_vec(),