From 8f3ae0e636ebe826da8fb7ba7043c28471fd3c05 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 29 Apr 2024 12:40:47 +0200 Subject: [PATCH 1/3] EVM/Node: transaction object's block related fields are nullable --- etherlink/bin_node/lib_dev/durable_storage.ml | 15 +++++++++++---- .../lib_dev/encodings/ethereum_types.ml | 18 +++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/etherlink/bin_node/lib_dev/durable_storage.ml b/etherlink/bin_node/lib_dev/durable_storage.ml index 3ebadcefab0f..0890e6fed354 100644 --- a/etherlink/bin_node/lib_dev/durable_storage.ml +++ b/etherlink/bin_node/lib_dev/durable_storage.ml @@ -116,17 +116,22 @@ module Make (Reader : READER) = struct let* opt_object = inspect_durable_and_decode_opt (Durable_storage_path.Transaction_object.object_ tx_hash) - (Ethereum_types.transaction_object_from_rlp mock_block_hash) + (Ethereum_types.transaction_object_from_rlp (Some mock_block_hash)) in match opt_object with | Some temp_object -> + let*? (blockNumber : quantity) = + match temp_object.blockNumber with + | None -> error_with "Unexpected null blockNumber in valid object" + | Some n -> Ok n + in let+ blockHash = inspect_durable_and_decode (Durable_storage_path.Indexes.block_by_number - (Nth (un_qty temp_object.blockNumber))) + (Nth (un_qty blockNumber))) decode_block_hash in - Some {temp_object with blockHash} + Some {temp_object with blockHash = Some blockHash} | None -> return_none let transaction_object_with_block_hash block_hash tx_hash = @@ -149,7 +154,9 @@ module Make (Reader : READER) = struct let populate_tx_objects ~full_transaction_object block = let open Lwt_result_syntax in if full_transaction_object then - let* transactions = full_transactions block.hash block.transactions in + let* transactions = + full_transactions (Some block.hash) block.transactions + in return {block with transactions} else return block diff --git a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml index dd90c20dd278..821cd7f1623b 100644 --- a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml @@ -497,8 +497,8 @@ let transaction_receipt_encoding = (req "contractAddress" (option address_encoding)))) type transaction_object = { - blockHash : block_hash; - blockNumber : quantity; + blockHash : block_hash option; + blockNumber : quantity option; from : address; gas : quantity; gasPrice : quantity; @@ -506,8 +506,8 @@ type transaction_object = { input : hash; nonce : quantity; to_ : address option; - transactionIndex : quantity; - (* It can be null if it's in a pending block, but we don't have a notion of pending. *) + transactionIndex : quantity option; + (* It can be null if it's in a pending block. *) value : quantity; v : quantity; r : hash; @@ -548,7 +548,7 @@ let transaction_object_from_rlp block_hash bytes = let s = decode_hash s in { blockHash = block_hash; - blockNumber = block_number; + blockNumber = Some block_number; from; gas; gasPrice = gas_price; @@ -556,7 +556,7 @@ let transaction_object_from_rlp block_hash bytes = input; nonce; to_; - transactionIndex = index; + transactionIndex = Some index; value; v; r; @@ -623,8 +623,8 @@ let transaction_object_encoding = }) (merge_objs (obj10 - (req "blockHash" block_hash_encoding) - (req "blockNumber" quantity_encoding) + (req "blockHash" (option block_hash_encoding)) + (req "blockNumber" (option quantity_encoding)) (req "from" address_encoding) (req "gas" quantity_encoding) (req "gasPrice" quantity_encoding) @@ -632,7 +632,7 @@ let transaction_object_encoding = (req "input" hash_encoding) (req "nonce" quantity_encoding) (req "to" (option address_encoding)) - (req "transactionIndex" quantity_encoding)) + (req "transactionIndex" (option quantity_encoding))) (obj4 (req "value" quantity_encoding) (req "v" quantity_encoding) -- GitLab From 298d172b67fe9b1307391e61cc3bd84d93ec0b9a Mon Sep 17 00:00:00 2001 From: Hantang Sun Date: Fri, 3 May 2024 12:02:05 +0100 Subject: [PATCH 2/3] Etherlink: Implement txpool_content rpc --- .../lib_dev/encodings/ethereum_types.ml | 65 +++++++------ etherlink/bin_node/lib_dev/services.ml | 5 +- etherlink/bin_node/lib_dev/simulation.ml | 10 +- etherlink/bin_node/lib_dev/tx_pool.ml | 97 ++++++++++++++++++- etherlink/bin_node/lib_dev/tx_pool.mli | 2 + etherlink/kernel_evm/kernel/src/simulation.rs | 49 ++++++++-- 6 files changed, 182 insertions(+), 46 deletions(-) diff --git a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml index 821cd7f1623b..c0cec201b21d 100644 --- a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml @@ -251,6 +251,10 @@ let encode_u16_le (Qty n) = let bits = Z.to_bits n |> Bytes.of_string in pad_to_n_bytes_le bits 2 +let hash_raw_tx str = + str |> Bytes.of_string |> Tezos_crypto.Hacl.Hash.Keccak_256.digest + |> Bytes.to_string + type transaction_log = { address : address; topics : hash list; @@ -514,26 +518,29 @@ type transaction_object = { s : hash; } -let transaction_object_from_rlp block_hash bytes = - match Rlp.decode bytes with - | Ok - (Rlp.List - [ - Value block_number; - Value from; - Value gas_used; - Value gas_price; - Value hash; - Value input; - Value nonce; - Value to_; - Value index; - Value value; - Value v; - Value r; - Value s; - ]) -> - let block_number = decode_number block_number in +let transaction_object_from_rlp_item block_hash rlp_item = + let decode_optional_number bytes = + if block_hash == None then None else Some (decode_number bytes) + in + + match rlp_item with + | Rlp.List + [ + Value block_number; + Value from; + Value gas_used; + Value gas_price; + Value hash; + Value input; + Value nonce; + Value to_; + Value index; + Value value; + Value v; + Value r; + Value s; + ] -> + let block_number = decode_optional_number block_number in let from = decode_address from in let gas = decode_number gas_used in let gas_price = decode_number gas_price in @@ -541,14 +548,17 @@ let transaction_object_from_rlp block_hash bytes = let input = decode_hash input in let nonce = decode_number nonce in let to_ = if to_ = Bytes.empty then None else Some (decode_address to_) in - let index = decode_number index in + let index = decode_optional_number index in let value = decode_number value in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/7194 + v is currently incorrectly encoded as big-endian by the kernel, + causing the decoded value to be incorrect *) let v = decode_number v in let r = decode_hash r in let s = decode_hash s in { blockHash = block_hash; - blockNumber = Some block_number; + blockNumber = block_number; from; gas; gasPrice = gas_price; @@ -556,7 +566,7 @@ let transaction_object_from_rlp block_hash bytes = input; nonce; to_; - transactionIndex = Some index; + transactionIndex = index; value; v; r; @@ -564,6 +574,11 @@ let transaction_object_from_rlp block_hash bytes = } | _ -> raise (Invalid_argument "Expected a List of 13 elements") +let transaction_object_from_rlp block_hash bytes = + match Rlp.decode bytes with + | Ok rlp_item -> transaction_object_from_rlp_item block_hash rlp_item + | _ -> raise (Invalid_argument "Expected a List of 13 elements") + let transaction_object_encoding = let open Data_encoding in conv @@ -1064,10 +1079,6 @@ let txpool_encoding = (fun (pending, queued) -> {pending; queued}) (obj2 (req "pending" field_encoding) (req "queued" field_encoding)) -let hash_raw_tx str = - str |> Bytes.of_string |> Tezos_crypto.Hacl.Hash.Keccak_256.digest - |> Bytes.to_string - (** [transaction_nonce bytes] returns the nonce of a given raw transaction. *) let transaction_nonce bytes = let open Result_syntax in diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index 43738f77c84c..afc10163bbc1 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -356,9 +356,8 @@ let dispatch_request (config : Configuration.t) build_with_input ~f module_ parameters | Method (Txpool_content.Method, module_) -> let f (_ : unit option) = - rpc_ok - Ethereum_types. - {pending = AddressMap.empty; queued = AddressMap.empty} + let* txpool_content = Tx_pool.get_tx_pool_content () in + rpc_ok txpool_content in build ~f module_ parameters | Method (Web3_clientVersion.Method, module_) -> diff --git a/etherlink/bin_node/lib_dev/simulation.ml b/etherlink/bin_node/lib_dev/simulation.ml index 42cd04eb33a8..bd2d70311af5 100644 --- a/etherlink/bin_node/lib_dev/simulation.ml +++ b/etherlink/bin_node/lib_dev/simulation.ml @@ -123,7 +123,7 @@ type execution_result = {value : hash option; gas_used : quantity option} type call_result = (execution_result, hash) result -type validation_result = {address : address} +type validation_result = transaction_object type 'a simulation_result = ('a, string) result @@ -233,11 +233,11 @@ module Encodings = struct in Rlp.decode_result decode_execution_result decode_revert v - let decode_validation_result = + let decode_validation_result bytes = let open Result_syntax in - function - | Rlp.Value address -> return {address = decode_address address} - | _ -> error_with "The transaction pool returned an illformed value" + try return (transaction_object_from_rlp_item None bytes) + with _ -> + error_with "The simulation returned an ill-encoded validation result" let simulation_result_from_rlp decode_payload bytes = let open Result_syntax in diff --git a/etherlink/bin_node/lib_dev/tx_pool.ml b/etherlink/bin_node/lib_dev/tx_pool.ml index aadd8c3cad75..c43fa3583d04 100644 --- a/etherlink/bin_node/lib_dev/tx_pool.ml +++ b/etherlink/bin_node/lib_dev/tx_pool.ml @@ -17,7 +17,8 @@ module Pool = struct gas_price : Z.t; (* The maximum price the user can pay for fees. *) gas_limit : Z.t; (* The maximum limit the user can reach in terms of gas. *) inclusion_timestamp : Time.Protocol.t; - (* Time of inclusion in the transaction pool. *) + (* Time of inclusion in the transaction pool. *) + transaction_object : Ethereum_types.transaction_object; } type t = { @@ -27,8 +28,58 @@ module Pool = struct let empty : t = {transactions = Pkey_map.empty; global_index = Int64.zero} + let get_pool {transactions; global_index = _} addr_balance_nonce_map = + let find_balance_and_nonce address = + match Ethereum_types.AddressMap.find address addr_balance_nonce_map with + | Some nonce -> nonce + | None -> (Z.zero, Z.zero) + in + let is_transaction_pending + (transaction_object : Ethereum_types.transaction_object) + (address_balance : Z.t) (address_nonce : Z.t) : bool = + let (Qty gas_limit) = transaction_object.gas in + let (Qty gas_price) = transaction_object.gasPrice in + transaction_object.nonce == Ethereum_types.quantity_of_z address_nonce + && address_balance >= Z.(gas_limit * gas_price) + in + let add_transaction_object_to_map nonce transaction_object pending_map + queued_map address_balance address_nonce = + if is_transaction_pending transaction_object address_balance address_nonce + then + ( Ethereum_types.NonceMap.add nonce transaction_object pending_map, + queued_map ) + else + ( pending_map, + Ethereum_types.NonceMap.add nonce transaction_object queued_map ) + in + let add_if_non_empty address nonce_map acc_address_map = + if Ethereum_types.NonceMap.cardinal nonce_map == 0 then acc_address_map + else Ethereum_types.AddressMap.add address nonce_map acc_address_map + in + Pkey_map.fold + (fun address nonce_tx_map (acc_address_map_pending, acc_address_map_queued) + -> + let address_balance, address_nonce = find_balance_and_nonce address in + let pending, queued = + Nonce_map.fold + (fun nonce transaction (pending_map, queued_map) -> + add_transaction_object_to_map + nonce + transaction.transaction_object + pending_map + queued_map + address_balance + address_nonce) + nonce_tx_map + (Ethereum_types.NonceMap.empty, Ethereum_types.NonceMap.empty) + in + ( add_if_non_empty address pending acc_address_map_pending, + add_if_non_empty address queued acc_address_map_queued )) + transactions + (Ethereum_types.AddressMap.empty, Ethereum_types.AddressMap.empty) + (** Add a transaction to the pool. *) - let add {transactions; global_index} pkey raw_tx = + let add {transactions; global_index} pkey raw_tx transaction_object = let open Result_syntax in let* nonce = Ethereum_types.transaction_nonce raw_tx in let* gas_price = Ethereum_types.transaction_gas_price raw_tx in @@ -43,6 +94,7 @@ module Pool = struct gas_price; gas_limit; inclusion_timestamp; + transaction_object; } in Pkey_map.update @@ -305,6 +357,7 @@ let on_normal_transaction state tx_raw = } = state in + let* is_valid = Rollup_node.is_tx_valid tx_raw in match is_valid with | Error err -> @@ -313,7 +366,8 @@ let on_normal_transaction state tx_raw = ~transaction:(Hex.of_string tx_raw |> Hex.show) in return (Error err) - | Ok {address} -> + | Ok transaction_object -> + let address = transaction_object.from in let*? tx_data = Ethereum_types.transaction_data tx_raw in if tx_data_size_limit_reached ~max_number_of_chunks ~tx_data then let*! () = Tx_pool_events.tx_data_size_limit_reached () in @@ -329,12 +383,15 @@ let on_normal_transaction state tx_raw = if address_boundaries_are_reached then return @@ Error error_msg else (* Add the transaction to the pool *) - let*? pool = Pool.add pool address tx_raw in + let address = transaction_object.from in (* Compute the hash *) let tx_hash = Ethereum_types.hash_raw_tx tx_raw in let hash = Ethereum_types.hash_of_string Hex.(of_string tx_hash |> show) in + + let*? pool = Pool.add pool address tx_raw transaction_object in + let*! () = Tx_pool_events.add_transaction ~transaction:(Ethereum_types.hash_to_string hash) @@ -709,3 +766,35 @@ let size_info () = let*? worker = Lazy.force worker in Worker.Queue.push_request_and_wait worker Request.Size_info |> handle_request_error + +let get_tx_pool_content () = + let open Lwt_result_syntax in + let*? w = Lazy.force worker in + let Types.{rollup_node = (module Rollup_node); pool; _} = Worker.state w in + let addresses = Pool.addresses pool in + let*! addr_with_balance_nonces = + Lwt_list.map_p + (fun address -> + let* nonce = + Rollup_node.nonce + address + Ethereum_types.Block_parameter.(Block_parameter Latest) + in + let (Qty nonce) = Option.value ~default:(Qty Z.zero) nonce in + let* (Qty balance) = + Rollup_node.balance + address + Ethereum_types.Block_parameter.(Block_parameter Latest) + in + Lwt.return_ok (address, balance, nonce)) + addresses + in + let addr_balance_nonce_map = + List.fold_left + (fun acc (address, balance, nonce) -> + Ethereum_types.AddressMap.add address (balance, nonce) acc) + Ethereum_types.AddressMap.empty + (List.filter_ok addr_with_balance_nonces) + in + let pending, queued = Pool.get_pool pool addr_balance_nonce_map in + return Ethereum_types.{pending; queued} diff --git a/etherlink/bin_node/lib_dev/tx_pool.mli b/etherlink/bin_node/lib_dev/tx_pool.mli index 6311e4f81b88..50b2921644b5 100644 --- a/etherlink/bin_node/lib_dev/tx_pool.mli +++ b/etherlink/bin_node/lib_dev/tx_pool.mli @@ -65,3 +65,5 @@ val is_locked : unit -> bool tzresult Lwt.t type size_info = {number_of_addresses : int; number_of_transactions : int} val size_info : unit -> size_info tzresult Lwt.t + +val get_tx_pool_content : unit -> Ethereum_types.txpool tzresult Lwt.t diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 93dd9f54a2f4..365c23878354 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -23,11 +23,13 @@ use evm_execution::{account_storage, handler::ExecutionOutcome, precompiles}; use evm_execution::{run_transaction, EthereumError}; use primitive_types::{H160, U256}; use rlp::{Decodable, DecoderError, Encodable, Rlp}; +use sha3::{Digest, Keccak256}; use tezos_ethereum::block::BlockConstants; use tezos_ethereum::rlp_helpers::{ append_option_u64_le, check_list, decode_field, decode_option, decode_option_u64_le, next, }; +use tezos_ethereum::transaction::TransactionObject; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; use tezos_smart_rollup_host::runtime::Runtime; @@ -72,9 +74,9 @@ pub struct ExecutionResult { type CallResult = SimulationResult>; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Clone)] pub struct ValidationResult { - address: H160, + transaction_object: TransactionObject, } impl Encodable for SimulationResult { @@ -127,14 +129,14 @@ impl Decodable for ExecutionResult { impl Encodable for ValidationResult { fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.append(&self.address); + self.transaction_object.rlp_append(stream); } } impl Decodable for ValidationResult { fn decode(decoder: &Rlp) -> Result { Ok(ValidationResult { - address: decode_field(decoder, "caller")?, + transaction_object: TransactionObject::decode(decoder)?, }) } } @@ -405,7 +407,21 @@ impl TxValidation { .. })) => Self::to_error(OUT_OF_TICKS_MSG), Ok(None) => Self::to_error(CANNOT_PREPAY), - _ => Ok(SimulationResult::Ok(ValidationResult { address: *caller })), + _ => Ok(SimulationResult::Ok(ValidationResult { + transaction_object: TransactionObject { + block_number: U256::zero(), + from: *caller, + to: transaction.to, + gas_used: transaction.gas_limit_with_fees().into(), + gas_price: transaction.max_fee_per_gas, + hash: Keccak256::digest(transaction.to_bytes()).into(), + input: transaction.data.clone(), + nonce: transaction.nonce, + index: 0, + value: transaction.value, + signature: transaction.signature.clone(), + }, + })), } } @@ -1104,8 +1120,27 @@ mod tests { fn test_simulation_result_encoding_roundtrip() { let valid: SimulationResult = SimulationResult::Ok(ValidationResult { - address: address_from_str("f95abdf6ede4c3703e0e9453771fbee8592d31e9") - .unwrap(), + transaction_object: TransactionObject { + block_number: U256::from(532532), + from: address_of_str("3535353535353535353535353535353535353535") + .unwrap(), + gas_used: U256::from(32523), + gas_price: U256::from(100432432), + hash: [5; 32], + input: vec![], + nonce: 8888, + to: address_of_str("3635353535353535353535353535353535353536"), + index: 15u32, + value: U256::from(0), + signature: Some( + TxSignature::new( + U256::from(1337), + H256::from_low_u64_be(1), + H256::from_low_u64_be(2), + ) + .unwrap(), + ), + }, }); let call: SimulationResult = SimulationResult::Ok(SimulationResult::Ok(ExecutionResult { -- GitLab From cdc7baaa0dd0c5a62e66bf635d27732191785e2b Mon Sep 17 00:00:00 2001 From: Hantang Sun Date: Fri, 3 May 2024 12:02:31 +0100 Subject: [PATCH 3/3] Etherlink: tezt for txpool_content rpc --- etherlink/tezt/tests/evm_rollup.ml | 224 ++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 7 deletions(-) diff --git a/etherlink/tezt/tests/evm_rollup.ml b/etherlink/tezt/tests/evm_rollup.ml index cc478a8b88bb..18ced4a47884 100644 --- a/etherlink/tezt/tests/evm_rollup.ml +++ b/etherlink/tezt/tests/evm_rollup.ml @@ -494,6 +494,33 @@ let register_proxy ~title ~tags ?uses ?admin ?commitment_period protocols ~setup_mode:(Setup_proxy {devmode = true}) +let register_sequencer ~title ~tags ?uses ?additional_config ?admin + ?commitment_period ?challenge_window ?bootstrap_accounts ?da_fee_per_byte + ?minimum_base_fee_per_gas ?time_between_blocks ?whitelist + ?rollup_operator_key ?maximum_allowed_ticks f protocols = + let register = + register_test + ~title + ~tags + ?uses + ?additional_config + ?admin + ?commitment_period + ?challenge_window + ?bootstrap_accounts + ?da_fee_per_byte + ?minimum_base_fee_per_gas + ?whitelist + ?rollup_operator_key + ?maximum_allowed_ticks + f + protocols + in + register + ~setup_mode: + (Setup_sequencer + {time_between_blocks; sequencer = Constant.bootstrap1; devmode = true}) + let register_both ~title ~tags ?uses ?additional_config ?admin ?commitment_period ?challenge_window ?bootstrap_accounts ?da_fee_per_byte ?minimum_base_fee_per_gas ?time_between_blocks ?whitelist @@ -1392,15 +1419,198 @@ let test_chunked_transaction = register_both ~title ~tags ~da_fee_per_byte test_f let test_rpc_txpool_content = - register_both + register_sequencer ~tags:["evm"; "rpc"; "txpool_content"] ~title:"Check RPC txpool_content is available" - @@ fun ~protocol:_ ~evm_setup:{evm_node; _} -> - (* The content of the txpool is not relevant for now, this test only checks - the the RPC is correct, i.e. an object containing both the `pending` and - `queued` fields, containing the correct objects: addresses pointing to a - mapping of nonces to transactions. *) - let* _result = Evm_node.txpool_content evm_node in + ~minimum_base_fee_per_gas:base_fee_for_hardcoded_tx + ~time_between_blocks:Nothing + @@ fun ~protocol:_ ~evm_setup:{evm_node; sc_rollup_node; client; _} -> + let get_transaction_field transaction_content field_name = + transaction_content |> JSON.get field_name |> JSON.as_string_opt + |> Option.value ~default:"null" + in + let check_transaction_content ~transaction_content ~blockHash ~blockNumber + ~from ~gas ~gasPrice ~hash ~input ~nonce ~to_ ~transactionIndex ~value ~v + ~r ~s = + Check.( + (get_transaction_field transaction_content "blockHash" = blockHash) string) + ~error_msg:"Expected block hash to be %%R, got %%L." ; + Check.( + (get_transaction_field transaction_content "blockNumber" = blockNumber) + string) + ~error_msg:"Expected block number to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "from" = from) string) + ~error_msg:"Expected caller to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "gas" = gas) string) + ~error_msg:"Expected gas to be %%R, got %%L." ; + Check.( + (get_transaction_field transaction_content "gasPrice" = gasPrice) string) + ~error_msg:"Expected gas price to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "hash" = hash) string) + ~error_msg:"Expected hash to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "input" = input) string) + ~error_msg:"Expected input to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "nonce" = nonce) string) + ~error_msg:"Expected nonce to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "to" = to_) string) + ~error_msg:"Expected callee to be %%R, got %%L." ; + Check.( + (get_transaction_field transaction_content "transactionIndex" + = transactionIndex) + string) + ~error_msg:"Expected transaction index to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "value" = value) string) + ~error_msg:"Expected value to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "v" = v) string) + ~error_msg:"Expected v to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "r" = r) string) + ~error_msg:"Expected r to be %%R, got %%L." ; + Check.((get_transaction_field transaction_content "s" = s) string) + ~error_msg:"Expected s to be %%R, got %%L." + in + let tx1 = + (* { + "chainId": "1337", + "type": "LegacyTransaction", + "valid": true, + "hash": "0xb4c823c72996be6f4767997f21dac443568f3d0a1cd24f3b29eeb66cb5aca2f8", + "nonce": "0", + "gasPrice": "100000", + "gasLimit": "23300", + "from": "0x6ce4d79d4E77402e1ef3417Fdda433aA744C6e1c", + "to": "0x11D3C9168db9d12a3C591061D555870969b43dC9", + "v": "0a96", + "r": "4217494c4c98d5f8015399c004e088d094fcee43bcb9a4a6b29bdff27d6f1079", + "s": "23ca4eeac30b72e7582f2fcd9a151a855ae943ffb40f4a3ef616f5ae5483a592", + "value": "100", + "data": "" + } *) + "f86480830186a0825b049411d3c9168db9d12a3c591061d555870969b43dc96480820a96a04217494c4c98d5f8015399c004e088d094fcee43bcb9a4a6b29bdff27d6f1079a023ca4eeac30b72e7582f2fcd9a151a855ae943ffb40f4a3ef616f5ae5483a592" + in + + let tx2 = + (* { + "chainId": "1337", + "type": "LegacyTransaction", + "valid": true, + "hash": "0xb4c823c72996be6f4767997f21dac443568f3d0a1cd24f3b29eeb66cb5aca2f8", + "nonce": "1", + "gasPrice": "100000", + "gasLimit": "23300", + "from": "0x6ce4d79d4E77402e1ef3417Fdda433aA744C6e1c", + "to": "0x11D3C9168db9d12a3C591061D555870969b43dC9", + "v": "0a95", + "r": "30d35547c7d39738a85fd6e96d9c9308070b83f334d64f51a94404d20902f970", + "s": "45ccee6d401d77df59f6831b7d73d1e3df7a9584070f45c117f55a9b81fa997c", + "value": "100", + "data": "" + } *) + "f86401830186a0825b049411d3c9168db9d12a3c591061d555870969b43dc96480820a95a030d35547c7d39738a85fd6e96d9c9308070b83f334d64f51a94404d20902f970a045ccee6d401d77df59f6831b7d73d1e3df7a9584070f45c117f55a9b81fa997c" + in + + let*@ _ = Rpc.send_raw_transaction ~raw_tx:tx1 evm_node in + let*@ _ = Rpc.send_raw_transaction ~raw_tx:tx2 evm_node in + let* txpool_pending, txpool_queued = Evm_node.txpool_content evm_node in + Check.((List.length txpool_pending = 1) int) + ~error_msg: + "Expected number of addresses with pending transaction to be %%R, got \ + %%L." ; + Check.((List.length txpool_queued = 1) int) + ~error_msg: + "Expected number of addresses with queued transaction to be %%R, got %%L." ; + let transaction_addr_pending = List.nth txpool_pending 0 in + let transaction_addr_queued = List.nth txpool_queued 0 in + Check.( + (transaction_addr_pending.address + = "0x6ce4d79d4e77402e1ef3417fdda433aa744c6e1c") + string) + ~error_msg:"Expected caller of transaction_1 to be %%R, got %%L." ; + Check.( + (transaction_addr_queued.address + = "0x6ce4d79d4e77402e1ef3417fdda433aa744c6e1c") + string) + ~error_msg:"Expected caller of transaction_2 to be %%R, got %%L." ; + let num_pending_transaction_addr_1 = + List.length transaction_addr_pending.transactions + in + let num_queued_transaction_addr_1 = + List.length transaction_addr_queued.transactions + in + Check.((num_pending_transaction_addr_1 = 1) int) + ~error_msg:"Expected number of pending transaction to be %%R, got %%L." ; + Check.((num_queued_transaction_addr_1 = 1) int) + ~error_msg:"Expected number of queued transaction to be %%R, got %%L." ; + let pending_transaction_addr_1_nonce, pending_transaction_addr_1_content = + List.nth transaction_addr_pending.transactions 0 + in + let queued_transaction_addr_1_nonce, queued_transaction_addr_1_content = + List.nth transaction_addr_queued.transactions 0 + in + Check.((pending_transaction_addr_1_nonce = 0L) int64) + ~error_msg:"Expected nonce pending transaction to be %%R, got %%L." ; + + Check.((queued_transaction_addr_1_nonce = 1L) int64) + ~error_msg:"Expected nonce queued transaction to be %%R, got %%L." ; + + let () = + check_transaction_content + ~transaction_content:pending_transaction_addr_1_content + ~blockHash:"null" + ~blockNumber:"null" + ~from:"0x6ce4d79d4e77402e1ef3417fdda433aa744c6e1c" + ~gas:"0x5b04" + ~gasPrice:"0x186a0" + ~hash:"0xed148f664807dfcb3c7095de22a6c63e72ae4f9d503549c525f5014327b51693" + ~input:"0x" + ~nonce:"0x0" + ~to_:"0x11d3c9168db9d12a3c591061d555870969b43dc9" + ~transactionIndex:"null" + ~value:"0x64" + (* TODO: https://gitlab.com/tezos/tezos/-/issues/7194 + v is currently incorrectly encoded as big-endian by the kernel, + causing the decoded value to be incorrect. Should be 0xa96 here *) + ~v:"0x960a" + ~r:"0x4217494c4c98d5f8015399c004e088d094fcee43bcb9a4a6b29bdff27d6f1079" + ~s:"0x23ca4eeac30b72e7582f2fcd9a151a855ae943ffb40f4a3ef616f5ae5483a592" + in + + let () = + check_transaction_content + ~transaction_content:queued_transaction_addr_1_content + ~blockHash:"null" + ~blockNumber:"null" + ~from:"0x6ce4d79d4e77402e1ef3417fdda433aa744c6e1c" + ~gas:"0x5b04" + ~gasPrice:"0x186a0" + ~hash:"0x38b2831803a0f9a82bcc68f79bf167311b0adfe9e3d111a7f9579cdfcbae0f0f" + ~input:"0x" + ~nonce:"0x1" + ~to_:"0x11d3c9168db9d12a3c591061d555870969b43dc9" + ~transactionIndex:"null" + ~value:"0x64" + (* TODO: https://gitlab.com/tezos/tezos/-/issues/7194 + v is currently incorrectly encoded as big-endian by the kernel, + causing the decoded value to be incorrect. Should be 0xa95 here *) + ~v:"0x950a" + ~r:"0x30d35547c7d39738a85fd6e96d9c9308070b83f334d64f51a94404d20902f970" + ~s:"0x45ccee6d401d77df59f6831b7d73d1e3df7a9584070f45c117f55a9b81fa997c" + in + let* _level = next_evm_level ~evm_node ~sc_rollup_node ~client in + let* txpool_pending, txpool_queued = Evm_node.txpool_content evm_node in + + Check.((List.length txpool_pending = 1) int) + ~error_msg: + "Expected number of addresses with pending transaction to be %%R, got \ + %%L." ; + Check.((List.length txpool_queued = 0) int) + ~error_msg: + "Expected number of addresses with queued transaction to be %%R, got %%L." ; + let transaction_addr_pending = List.nth txpool_pending 0 in + let num_pending_transaction_addr_1 = + List.length transaction_addr_pending.transactions + in + Check.((num_pending_transaction_addr_1 = 1) int) + ~error_msg:"Expected number of pending transaction to be %%R, got %%L." ; unit let test_rpc_web3_clientVersion = -- GitLab