diff --git a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml index 0b4d3b8453b8c7000634484c147da08b29201b90..0646e5d3ca2353b71df7e4c437d0ca8e6bf9bd09 100644 --- a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml @@ -1121,3 +1121,131 @@ let state_override_empty = AddressMap.empty let state_override_encoding = AddressMap.associative_array_encoding state_account_override_encoding + +module Subscription = struct + exception Unknown_subscription + + type logs = {address : address; topics : hash list} + + let logs_encoding = + let open Data_encoding in + conv + (fun {address; topics} -> (address, topics)) + (fun (address, topics) -> {address; topics}) + (obj2 + (req "address" address_encoding) + (req "topics" (list hash_encoding))) + + type kind = NewHeads | Logs of logs | NewPendingTransactions | Syncing + + let kind_encoding = + let open Data_encoding in + union + [ + case + ~title:"params_size_two" + (Tag 0) + (tup2 string logs_encoding) + (function Logs logs -> Some ("logs", logs) | _ -> None) + (function + | "logs", logs -> Logs logs | _ -> raise Unknown_subscription); + case + ~title:"params_size_one" + (Tag 1) + (tup1 string) + (function + | NewHeads -> Some "newHeads" + | NewPendingTransactions -> Some "newPendingTransactions" + | Syncing -> Some "syncing" + | _ -> None) + (function + | "newHeads" -> NewHeads + | "newPendingTransactions" -> NewPendingTransactions + | "syncing" -> Syncing + | _ -> raise Unknown_subscription); + ] + + type id = Id of hex [@@ocaml.unboxed] + + let id_of_string s = Id (hex_of_string (String.lowercase_ascii s)) + + let id_to_string (Id a) = hex_to_string a + + let id_encoding = Data_encoding.(conv id_to_string id_of_string string) + + let id_input_encoding = + Data_encoding.(conv id_to_string id_of_string (tup1 string)) + + type sync_status = { + startingBlock : quantity; + currentBlock : quantity; + highestBlock : quantity; + pulledStates : quantity; + knownStates : quantity; + } + + let sync_status_encoding = + let open Data_encoding in + conv + (fun { + startingBlock; + currentBlock; + highestBlock; + pulledStates; + knownStates; + } -> + (startingBlock, currentBlock, highestBlock, pulledStates, knownStates)) + (fun (startingBlock, currentBlock, highestBlock, pulledStates, knownStates) -> + {startingBlock; currentBlock; highestBlock; pulledStates; knownStates}) + (obj5 + (req "startingBlock" quantity_encoding) + (req "currentBlock" quantity_encoding) + (req "highestBlock" quantity_encoding) + (req "pulledStates" quantity_encoding) + (req "knownStates" quantity_encoding)) + + type sync_output = {syncing : bool; status : sync_status} + + let sync_output_encoding = + let open Data_encoding in + conv + (fun {syncing; status} -> (syncing, status)) + (fun (syncing, status) -> {syncing; status}) + (obj2 (req "syncing" bool) (req "status" sync_status_encoding)) + + type output = + | NewHeads of block + | Logs of logs + | NewPendingTransactions of hash + | Syncing of sync_output + + let output_encoding = + let open Data_encoding in + union + [ + case + ~title:"newHeads" + (Tag 0) + block_encoding + (function NewHeads b -> Some b | _ -> None) + (fun b -> NewHeads b); + case + ~title:"logs" + (Tag 1) + logs_encoding + (function Logs l -> Some l | _ -> None) + (fun l -> Logs l); + case + ~title:"pendingTxs" + (Tag 2) + hash_encoding + (function NewPendingTransactions l -> Some l | _ -> None) + (fun l -> NewPendingTransactions l); + case + ~title:"sync" + (Tag 3) + sync_output_encoding + (function Syncing l -> Some l | _ -> None) + (fun l -> Syncing l); + ] +end diff --git a/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli b/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli index f40a9bf7585719fa2bdef9fc87bae83614bee2ed..c659ae805143bb0f7742488a8b0b0d0086d21fbe 100644 --- a/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli +++ b/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli @@ -309,3 +309,37 @@ module From_rlp : sig val decode_hex : Rlp.item -> hex tzresult end + +module Subscription : sig + exception Unknown_subscription + + type logs = {address : address; topics : hash list} + + type kind = NewHeads | Logs of logs | NewPendingTransactions | Syncing + + val kind_encoding : kind Data_encoding.t + + type id = Id of hex [@@ocaml.unboxed] + + val id_encoding : id Data_encoding.t + + val id_input_encoding : id Data_encoding.t + + type sync_status = { + startingBlock : quantity; + currentBlock : quantity; + highestBlock : quantity; + pulledStates : quantity; + knownStates : quantity; + } + + type sync_output = {syncing : bool; status : sync_status} + + type output = + | NewHeads of block + | Logs of logs + | NewPendingTransactions of hash + | Syncing of sync_output + + val output_encoding : output Data_encoding.t +end diff --git a/etherlink/bin_node/lib_dev/rpc_encodings.ml b/etherlink/bin_node/lib_dev/rpc_encodings.ml index c70e1fd4936995163fa6bc5fb2b34e939c73254b..1b95cd3216aa8fc49904853c5893af4cbedaa3bd 100644 --- a/etherlink/bin_node/lib_dev/rpc_encodings.ml +++ b/etherlink/bin_node/lib_dev/rpc_encodings.ml @@ -112,6 +112,38 @@ module JSONRPC = struct (req "id" (option id_repr_encoding)))) end +module Subscription = struct + let version = JSONRPC.version + + let method_ = "eth_subscription" + + type result = { + result : Data_encoding.json; + subscription : Ethereum_types.Subscription.id; + } + + let result_encoding = + Data_encoding.( + conv + (fun {result; subscription} -> (result, subscription)) + (fun (result, subscription) -> {result; subscription}) + (obj2 + (req "result" Data_encoding.json) + (req "subscription" Ethereum_types.Subscription.id_encoding))) + + type response = {params : result} + + let response_encoding = + Data_encoding.( + conv + (fun {params} -> ((), (), params)) + (fun ((), (), params) -> {params}) + (obj3 + (req "jsonrpc" (constant version)) + (req "method" (constant method_)) + (req "params" result_encoding))) +end + module Error = struct type t = unit @@ -858,6 +890,38 @@ module Coinbase = struct type ('input, 'output) method_ += Method : (input, output) method_ end +module Subscribe = struct + open Ethereum_types + + type input = Subscription.kind + + type output = Subscription.id + + let input_encoding = Subscription.kind_encoding + + let output_encoding = Subscription.id_encoding + + let method_ = "eth_subscribe" + + type ('input, 'output) method_ += Method : (input, output) method_ +end + +module Unsubscribe = struct + open Ethereum_types + + type input = Subscription.id + + type output = bool + + let input_encoding = Subscription.id_input_encoding + + let output_encoding = Data_encoding.bool + + let method_ = "eth_unsubscribe" + + type ('input, 'output) method_ += Method : (input, output) method_ +end + type map_result = | Method : ('input, 'output) method_ @@ -911,6 +975,8 @@ let supported_methods : (module METHOD) list = (module Eth_fee_history); (module Coinbase); (module Trace_call); + (module Subscribe); + (module Unsubscribe); ] let unsupported_methods : string list = diff --git a/etherlink/bin_node/lib_dev/rpc_encodings.mli b/etherlink/bin_node/lib_dev/rpc_encodings.mli index 8564844961e9874769b5eef5ab58dc65618edfd4..7c73dde06c25a541ddd7f2d0152f666461e1fafb 100644 --- a/etherlink/bin_node/lib_dev/rpc_encodings.mli +++ b/etherlink/bin_node/lib_dev/rpc_encodings.mli @@ -87,6 +87,26 @@ module JSONRPC : sig val response_encoding : response Data_encoding.t end +(* This isn't the JSON-RPC request that is sent to request websocket events but + the generic outputed data that is sent through the websocket periodically once + the appropriate websocket request was sent (see module [Subscribe]). *) +module Subscription : sig + val version : string + + val method_ : string + + type result = { + result : Data_encoding.json; + subscription : Ethereum_types.Subscription.id; + } + + val result_encoding : result Data_encoding.t + + type response = {params : result} + + val response_encoding : response Data_encoding.t +end + (* Errors returned by the RPC server, to be embedded as data to the JSON-RPC error object. *) module Error : sig @@ -314,6 +334,14 @@ module Eth_fee_history : module Coinbase : METHOD with type input = unit and type output = Ethereum_types.address +module Subscribe : + METHOD + with type input = Ethereum_types.Subscription.kind + and type output = Ethereum_types.Subscription.id + +module Unsubscribe : + METHOD with type input = Ethereum_types.Subscription.id and type output = bool + type map_result = | Method : ('input, 'output) method_