From 88ad456d80b452a6cbee1b89e29bbd2d16ccb021 Mon Sep 17 00:00:00 2001 From: Rodi-Can Bozman Date: Wed, 11 Dec 2024 16:47:16 +0100 Subject: [PATCH 1/3] EVM/Node: add a watcher for newPendingTransactions events --- etherlink/bin_node/lib_dev/services.ml | 5 +---- etherlink/bin_node/lib_dev/tx_pool.ml | 7 +++++++ etherlink/bin_node/lib_dev/tx_pool.mli | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index 9be4bfc6cade..b97b7ca8b151 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -261,10 +261,7 @@ let eth_subscribe ~(kind : Ethereum_types.Subscription.kind) = | Logs _ -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/7640 *) Stdlib.failwith "The websocket event [logs] is not implemented yet." - | NewPendingTransactions -> - (* TODO: https://gitlab.com/tezos/tezos/-/issues/7641 *) - Stdlib.failwith - "The websocket event [newPendingTransactions] is not implemented yet." + | NewPendingTransactions -> Lwt_watcher.create_stream Tx_pool.txs_watcher | Syncing -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/7642 *) Stdlib.failwith "The websocket event [syncing] is not implemented yet." diff --git a/etherlink/bin_node/lib_dev/tx_pool.ml b/etherlink/bin_node/lib_dev/tx_pool.ml index f05dc50cbb55..663688385a6e 100644 --- a/etherlink/bin_node/lib_dev/tx_pool.ml +++ b/etherlink/bin_node/lib_dev/tx_pool.ml @@ -731,6 +731,9 @@ let find state tx_hash = in Option.either_f res (fun () -> Pool.find state.Types.pool tx_hash) +let txs_watcher : Ethereum_types.Subscription.output Lwt_watcher.input = + Lwt_watcher.create_input () + module Handlers = struct type self = worker @@ -755,6 +758,10 @@ module Handlers = struct match request with | Request.Add_transaction (transaction_object, txn) -> protect @@ fun () -> + Lwt_watcher.notify + txs_watcher + (Ethereum_types.Subscription.NewPendingTransactions + transaction_object.hash) ; let* res = match state.mode with | Forward {injector} -> injector txn diff --git a/etherlink/bin_node/lib_dev/tx_pool.mli b/etherlink/bin_node/lib_dev/tx_pool.mli index 0497baea5b1d..569d0fba2ce5 100644 --- a/etherlink/bin_node/lib_dev/tx_pool.mli +++ b/etherlink/bin_node/lib_dev/tx_pool.mli @@ -34,6 +34,10 @@ val start : parameters -> unit tzresult Lwt.t to be processed. *) val shutdown : unit -> unit tzresult Lwt.t +(** Watcher that gets notified each time a transaction is added to the + pending state. *) +val txs_watcher : Ethereum_types.Subscription.output Lwt_watcher.input + (** [add transaction_object raw_tx] adds a eth transaction and its raw contents to the tx-pool. -- GitLab From 233379b91beccfc6bd1507914a47b88d352182f7 Mon Sep 17 00:00:00 2001 From: Rodi-Can Bozman Date: Wed, 11 Dec 2024 17:00:56 +0100 Subject: [PATCH 2/3] EVM/Tezt: test websocket event newPendingTransactions --- etherlink/tezt/tests/evm_sequencer.ml | 106 ++++++++++++++++++++------ 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index 1465da507e8d..7d4d12f4f926 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -8367,6 +8367,31 @@ let test_websocket_subscription_rpcs_cant_be_called_via_http_requests = failwith "eth_unsubscribe shouldn't be callable via http requests") (fun _ -> unit) +let check_unsubscription ~websocket ~id ~sequencer evm_node = + let* sub_status = Rpc.unsubscribe ~websocket ~id evm_node in + Check.((sub_status = true) bool) + ~error_msg:"Unsubscription from newHeads should be successful" ; + (* After unsubbing to newHeads, we shouldn't receive data anymore. *) + let*@ _ = produce_block sequencer in + let* result = + Lwt.pick + [ + (let* never_return = Websocket.recv websocket in + return (Error never_return)); + (let* () = Lwt_unix.sleep 2. in + return (Ok ())); + ] + in + (match result with + | Ok () -> () + | Error _ -> failwith "The websocket shouldn't have received any new data.") ; + (* If we try to unsubscribe from this event again, it should return false as + we were already unsubbed. *) + let* sub_status = Rpc.unsubscribe ~websocket ~id evm_node in + Check.((sub_status = false) bool) + ~error_msg:"Unsubscribing from the same event twice should return false" ; + unit + let test_websocket_newHeads_event = register_all ~tags:["evm"; "rpc"; "websocket"; "new_heads"] @@ -8395,29 +8420,7 @@ let test_websocket_newHeads_event = in let* () = check_block_number ~level:lvl in let* () = check_block_number ~level:lvl' in - let* sub_status = Rpc.unsubscribe ~websocket ~id evm_node in - Check.((sub_status = true) bool) - ~error_msg:"Unsubscription from newHeads should be successful" ; - (* After unsubbing to newHeads, we shouldn't receive data anymore. *) - let*@ _ = produce_block sequencer in - let* result = - Lwt.pick - [ - (let* never_return = Websocket.recv websocket in - return (Error never_return)); - (let* () = Lwt_unix.sleep 2. in - return (Ok ())); - ] - in - (match result with - | Ok () -> () - | Error _ -> failwith "The websocket shouldn't have received any new data.") ; - (* If we try to unsubscribe from this event again, it should return false as - we were already unsubbed. *) - let* sub_status = Rpc.unsubscribe ~websocket ~id evm_node in - Check.((sub_status = false) bool) - ~error_msg:"Unsubscribing from the same event twice should return false" ; - unit + check_unsubscription ~websocket ~id ~sequencer evm_node in let* () = scenario sequencer (1l, 2l) in scenario observer (4l, 5l) @@ -8455,6 +8458,60 @@ let test_websocket_cleanup = ~error_msg:"All subscriptions should have been cleaned up from node" ; unit +let test_websocket_newPendingTransactions_event = + register_all + ~tags:["evm"; "rpc"; "websocket"; "new_pending_transactions"] + ~title: + "Check that websocket event `newPendingTransactions` is behaving \ + correctly" + ~time_between_blocks:Nothing + ~bootstrap_accounts: + ((Array.to_list Eth_account.bootstrap_accounts + |> List.map (fun a -> a.Eth_account.address)) + @ Eth_account.lots_of_address) + ~minimum_base_fee_per_gas:base_fee_for_hardcoded_tx + ~rpc_server:Dream (* Websockets only available in Dream *) + ~websockets:true + @@ fun {sequencer; sc_rollup_node; _} _protocol -> + let* observer = + run_new_observer_node + ~finalized_view:false + ~sc_rollup_node + ~rpc_server:Dream (* Websockets only available in Dream *) + ~websockets:true + sequencer + in + let sender = Eth_account.bootstrap_accounts.(0) in + let scenario evm_node transaction_hash = + let* websocket = Evm_node.open_websocket evm_node in + let* id = Rpc.subscribe ~websocket ~kind:NewPendingTransactions evm_node in + Lwt.async (fun () -> + (* In observer mode, the transaction will never be mined which will cause + the following line to be blocking. *) + let* _ = + Eth_cli.transaction_send + ~source_private_key:sender.private_key + ~to_public_key:sender.address + ~value:(Wei.of_eth_int 10) + ~endpoint:(Evm_node.endpoint evm_node) + () + in + unit) ; + let* tx = Websocket.recv websocket in + let tx_hash = JSON.(tx |-> "params" |-> "result" |> as_string) in + Check.((transaction_hash = tx_hash) string) + ~error_msg:"Received tx_hash was %R, expected %L" ; + check_unsubscription ~websocket ~id ~sequencer evm_node + in + let* () = + scenario + sequencer + "0x1b5678a27af55582f2bd6fa07223ff59ee93e16c228e40a948d09ee593560d36" + in + scenario + observer + "0xf5e6dcb59cbf260cfe04d89d07a0f270c11e489a6de4df319916c7ddb19f3a34" + let protocols = Protocol.all let () = @@ -8571,4 +8628,5 @@ let () = test_websocket_subscription_rpcs_cant_be_called_via_http_requests [Protocol.Alpha] ; test_websocket_newHeads_event [Protocol.Alpha] ; - test_websocket_cleanup [Protocol.Alpha] + test_websocket_cleanup [Protocol.Alpha] ; + test_websocket_newPendingTransactions_event [Protocol.Alpha] -- GitLab From 36bb41265a264f2107d0d370f9a444d22ffb706a Mon Sep 17 00:00:00 2001 From: Rodi-Can Bozman Date: Tue, 17 Dec 2024 10:31:39 +0100 Subject: [PATCH 3/3] EVM/Changes: add an entry in Experimental --- etherlink/CHANGES_NODE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etherlink/CHANGES_NODE.md b/etherlink/CHANGES_NODE.md index 4fa02945af44..2f6a7987386a 100644 --- a/etherlink/CHANGES_NODE.md +++ b/etherlink/CHANGES_NODE.md @@ -84,6 +84,8 @@ you start using them, you probably want to use `octez-evm-node check config `experimental_features.rpc_server = "dream"`. (!15560) - Added support for the WebSocket event `newHeads`, allowing clients to receive real-time notifications of new blocks. (!15899) +- Added support for the WebSocket event `newPendingTransactions`, enabling clients + to receive real-time notifications of incoming pending transactions. (!15991) ### Bug fixes -- GitLab