From 4a65710830c73d4520ec3c744a6caab6a3756c1b Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Fri, 30 Jun 2023 18:24:15 +0100 Subject: [PATCH] Sequencer: Sign sequencer kernel messages --- manifest/main.ml | 1 + opam/octez-smart-rollup-sequencer.opam | 1 + src/bin_sequencer_node/main_sequencer_node.ml | 6 +- src/kernel_sequencer/Cargo.toml | 3 + src/kernel_sequencer/src/delayed_inbox.rs | 9 +- src/lib_scoru_sequencer/dune | 4 +- src/lib_scoru_sequencer/kernel_message.ml | 67 +++++++++++---- src/lib_scoru_sequencer/seq_batcher.ml | 49 +++++++---- .../test/test_kernel_message.ml | 21 +++-- tezt/tests/sc_sequencer.ml | 84 ++++++++++--------- 10 files changed, 162 insertions(+), 83 deletions(-) diff --git a/manifest/main.ml b/manifest/main.ml index b4fbe4772a31..5a55fbc367d1 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -7885,6 +7885,7 @@ let octez_scoru_sequencer = octez_workers |> open_; octez_smart_rollup_node_lib |> open_; octez_smart_rollup_lib |> open_; + octez_client_base |> open_; octez_rpc; octez_rpc_http; octez_rpc_http_server; diff --git a/opam/octez-smart-rollup-sequencer.opam b/opam/octez-smart-rollup-sequencer.opam index cd3b3d5c4c3e..176ad82535e2 100644 --- a/opam/octez-smart-rollup-sequencer.opam +++ b/opam/octez-smart-rollup-sequencer.opam @@ -16,6 +16,7 @@ depends: [ "octez-smart-rollup-node-alpha" "octez-smart-rollup-node-lib" "octez-smart-rollup" + "tezos-client-base" ] build: [ ["rm" "-r" "vendors" "contrib"] diff --git a/src/bin_sequencer_node/main_sequencer_node.ml b/src/bin_sequencer_node/main_sequencer_node.ml index a054fe89dd49..27d4cfcd9882 100644 --- a/src/bin_sequencer_node/main_sequencer_node.ml +++ b/src/bin_sequencer_node/main_sequencer_node.ml @@ -44,9 +44,9 @@ let sc_operator_pkh next = ~desc:"Public key hash, or alias, of a sequencer node operator." ( Tezos_clic.parameter @@ fun cctxt s -> let parse_pkh s = - let from_alias s = Client_keys.Public_key_hash.find cctxt s in + let from_alias s = Client_keys.V0.Public_key_hash.find cctxt s in let from_key s = - match Signature.Public_key_hash.of_b58check_opt s with + match Signature.V0.Public_key_hash.of_b58check_opt s with | None -> failwith "Could not read public key hash for sequencer operator" | Some pkh -> return pkh @@ -58,7 +58,7 @@ let sc_operator_pkh next = match String.split ~limit:1 ':' s with | [_] -> let+ pkh = parse_pkh s in - `Default pkh + `Default (Signature.Of_V0.public_key_hash pkh) | [_purpose; _operator_s] -> failwith "Purposes are not supported for a sequencer operator" | _ -> diff --git a/src/kernel_sequencer/Cargo.toml b/src/kernel_sequencer/Cargo.toml index c1432364f2f6..b22f0130648e 100644 --- a/src/kernel_sequencer/Cargo.toml +++ b/src/kernel_sequencer/Cargo.toml @@ -46,6 +46,9 @@ version = "0.2.0" path = "../kernel_sdk/debug" version = "0.2.0" +[dependencies.hex] +version = "0.4" + [dev-dependencies.tezos_crypto_rs] version = "0.5.0" default-features = false diff --git a/src/kernel_sequencer/src/delayed_inbox.rs b/src/kernel_sequencer/src/delayed_inbox.rs index 0b2b979cf5f2..ce2d21218e06 100644 --- a/src/kernel_sequencer/src/delayed_inbox.rs +++ b/src/kernel_sequencer/src/delayed_inbox.rs @@ -61,8 +61,6 @@ pub fn read_input( Err(_) => {} Ok((_, message)) => match message { KernelMessage::Sequencer(sequencer_msg) => { - debug_msg!(host, "Received a sequence message {:?}", &sequencer_msg); - let Ok(payload) = extract_payload( sequencer_msg, &raw_rollup_address, @@ -71,7 +69,7 @@ pub fn read_input( match payload { SequencerMsg::Sequence(sequence) => { - handle_sequence_message(sequence) + handle_sequence_message(host, sequence) } SequencerMsg::SetSequencer(set_sequencer) => { handle_set_sequencer_message(set_sequencer) @@ -131,8 +129,9 @@ fn extract_payload( } /// Handle Sequence message -fn handle_sequence_message(_sequence: Sequence) { +fn handle_sequence_message(host: &H, sequence: Sequence) { // process the sequence + debug_msg!(host, "Received {:?} targeting our rollup", sequence); } fn handle_set_sequencer_message(_set_sequencer: SetSequencer) { @@ -153,7 +152,7 @@ fn handle_message( if filter_behavior.predicate(user_message.as_ref(), rollup_address) { debug_msg!( host, - "Received user message {:?} targeting our rollup, hence, will be added to the delayed inbox", + "Received a user message {:?} targeting our rollup, hence, will be added to the delayed inbox", user_message ); diff --git a/src/lib_scoru_sequencer/dune b/src/lib_scoru_sequencer/dune index 86a4bf7d2dec..05bbffc8a56c 100644 --- a/src/lib_scoru_sequencer/dune +++ b/src/lib_scoru_sequencer/dune @@ -13,6 +13,7 @@ octez-libs.tezos-workers octez-smart-rollup-node-lib octez-smart-rollup + tezos-client-base octez-libs.tezos-rpc octez-libs.tezos-rpc-http octez-libs.tezos-rpc-http-server) @@ -24,4 +25,5 @@ -open Tezos_protocol_alpha -open Tezos_workers -open Octez_smart_rollup_node - -open Octez_smart_rollup)) + -open Octez_smart_rollup + -open Tezos_client_base)) diff --git a/src/lib_scoru_sequencer/kernel_message.ml b/src/lib_scoru_sequencer/kernel_message.ml index d7ee9f7f000b..5f7685de77d8 100644 --- a/src/lib_scoru_sequencer/kernel_message.ml +++ b/src/lib_scoru_sequencer/kernel_message.ml @@ -144,15 +144,25 @@ module Framed_message = Framed (struct && List.equal String.equal x.l2_messages y.l2_messages end) -include Signed (Framed_message) +type unsigned = Framed_message.t -(* Signature consisting of zeroes *) -let dummy_signature = - Signature.V0.of_bytes_exn @@ Bytes.make Signature.V0.size @@ Char.chr 0 +let unsigned_encoding = Framed_message.encoding + +module Signed_raw = Signed (struct + type t = string + + let encoding = Data_encoding.Variable.string + + let equal = String.equal +end) + +type signed_raw = Signed_raw.t + +let signed_raw_encoding = Signed_raw.encoding let encode_sequence_message rollup_address ~prefix ~suffix - (l2_messages : Sc_rollup.Inbox_message.serialized list) : string = - (* Fix: actually sign a message *) + (l2_messages : Sc_rollup.Inbox_message.serialized list) : + [`Unsigned_encoded of string] = let unsigned_payload = Framed_message. { @@ -168,18 +178,45 @@ let encode_sequence_message rollup_address ~prefix ~suffix }; } in - let signed_framed_sequence = - {unsigned_payload; signature = dummy_signature} + `Unsigned_encoded + (Data_encoding.Binary.to_string_exn + Framed_message.encoding + unsigned_payload) + +let sign_sequence cctxt signer (`Unsigned_encoded unsigned_sequence) = + let open Lwt_result_syntax in + let+ signature = + Client_keys.V0.sign cctxt signer (String.to_bytes unsigned_sequence) + in + Data_encoding.Binary.to_string_exn + signed_raw_encoding + {unsigned_payload = unsigned_sequence; signature} + +let encode_and_sign_sequence (cctx, signer) rollup_address ~prefix ~suffix + serialized_msgs = + let encoded_sequence = + encode_sequence_message rollup_address ~prefix ~suffix serialized_msgs in - Data_encoding.Binary.to_string_exn encoding signed_framed_sequence + sign_sequence cctx signer encoded_sequence let single_l2_message_overhead = let dummy_address = Sc_rollup.Address.of_b58check_exn "sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb" in - String.length - @@ encode_sequence_message - dummy_address - ~prefix:1000l - ~suffix:1000l - [Sc_rollup.Inbox_message.unsafe_of_string ""] + (* Signature consisting of zeroes *) + let dummy_signature = + Signature.V0.of_bytes_exn @@ Bytes.make Signature.V0.size @@ Char.chr 0 + in + let (`Unsigned_encoded encoded) = + encode_sequence_message + dummy_address + ~prefix:1000l + ~suffix:1000l + [Sc_rollup.Inbox_message.unsafe_of_string ""] + in + let dummy_signed = + Data_encoding.Binary.to_string_exn + signed_raw_encoding + {unsigned_payload = encoded; signature = dummy_signature} + in + String.length dummy_signed diff --git a/src/lib_scoru_sequencer/seq_batcher.ml b/src/lib_scoru_sequencer/seq_batcher.ml index 54a98a3cc7f4..6f580c9f8fb9 100644 --- a/src/lib_scoru_sequencer/seq_batcher.ml +++ b/src/lib_scoru_sequencer/seq_batcher.ml @@ -45,7 +45,8 @@ module Batched_messages = Hash_queue.Make (L2_message.Hash) (L2_batched_message) type state = { node_ctxt : Node_context.ro; - signer : Signature.public_key_hash; + (* This one is V0 because sequencer kernel doesn't support BLS signatures yet *) + signer : Signature.V0.public_key_hash * Client_keys.sk_uri; messages : Message_queue.t; batched : Batched_messages.t; mutable simulation_ctxt : Simulation.t option; @@ -56,7 +57,9 @@ let inject_sequence state (sequencer_message, l2_messages) = let open Lwt_result_syntax in let operation = L1_operation.Add_messages {messages = [sequencer_message]} in let+ l1_hash = - Injector.add_pending_operation ~source:state.signer operation + Injector.add_pending_operation + ~source:(Signature.Of_V0.public_key_hash @@ fst state.signer) + operation in List.iter (fun msg -> @@ -125,16 +128,15 @@ let get_batch_sequences state head = (* That might happen only for the genesis + 1 block level *) return ([], 0) else - return - ( [ - ( Kernel_message.encode_sequence_message - state.node_ctxt.rollup_address - ~prefix:(Int32.of_int (delayed_inbox_size - 1)) - ~suffix:1l - l2_messages_serialized, - l2_messages ); - ], - delayed_inbox_size ) + let* signed_sequence = + Kernel_message.encode_and_sign_sequence + (state.node_ctxt.cctxt, snd state.signer) + state.node_ctxt.rollup_address + ~prefix:(Int32.of_int (delayed_inbox_size - 1)) + ~suffix:1l + l2_messages_serialized + in + return ([(signed_sequence, l2_messages)], delayed_inbox_size) let produce_batch_sequences state head = let open Lwt_result_syntax in @@ -225,10 +227,14 @@ let on_new_head state head = return_unit let init_batcher_state node_ctxt ~signer = - Lwt.return + let open Lwt_result_syntax in + let* _alias, _pk, sk = + Client_keys.V0.get_key node_ctxt.Node_context.cctxt signer + in + return { node_ctxt; - signer; + signer = (signer, sk); messages = Message_queue.create 100_000 (* ~ 400MB *); batched = Batched_messages.create 100_000 (* ~ 400MB *); simulation_ctxt = None; @@ -278,7 +284,20 @@ module Handlers = struct let on_launch _w () Types.{node_ctxt; signer} = let open Lwt_result_syntax in - let*! state = init_batcher_state node_ctxt ~signer in + let to_v0_exn pkh = + match + Signature.V0.Public_key_hash.of_bytes + @@ Signature.Public_key_hash.to_bytes pkh + with + | Error e -> + failwith + "Couldn't convert an operator key to V0 key: %a" + Error_monad.pp_print_trace + e + | Ok x -> return x + in + let* signer = to_v0_exn signer in + let* state = init_batcher_state node_ctxt ~signer in return state let on_error (type a b) _w st (r : (a, b) Request.t) (errs : b) : diff --git a/src/lib_scoru_sequencer/test/test_kernel_message.ml b/src/lib_scoru_sequencer/test/test_kernel_message.ml index 1d733a8818fa..60de0c11a614 100644 --- a/src/lib_scoru_sequencer/test/test_kernel_message.ml +++ b/src/lib_scoru_sequencer/test/test_kernel_message.ml @@ -35,9 +35,18 @@ open Alpha_context open Tztest module KM = Octez_smart_rollup_sequencer.Kernel_message -let test_signed ?loc expected_hex encoded_msg = +(* TODO fix it: actually sign it *) +let test_encoding ?loc expected_hex (`Unsigned_encoded encoded_msg) = let open Lwt_result_syntax in - let result_hex = Hex.show @@ Hex.of_string encoded_msg in + let dummy_signature = + Signature.V0.of_bytes_exn @@ Bytes.make Signature.V0.size @@ Char.chr 0 + in + let dummy_signed = + Data_encoding.Binary.to_string_exn + KM.signed_raw_encoding + {unsigned_payload = encoded_msg; signature = dummy_signature} + in + let result_hex = Hex.show @@ Hex.of_string dummy_signed in Assert.equal ?loc ~msg:"Encoded hex don't match" expected_hex result_hex ; return_unit @@ -52,7 +61,7 @@ let test_empty_suffix_n_prefix () = "006227a8721213bd7ddb9b56227e3acb01161b1e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in let* () = - test_signed + test_encoding ~loc:__LOC__ empty_l2_messages_hex (KM.encode_sequence_message rollup_address ~prefix:0l ~suffix:0l []) @@ -62,7 +71,7 @@ let test_empty_suffix_n_prefix () = "006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in let* () = - test_signed + test_encoding ~loc:__LOC__ single_empty_l2_message_hex (KM.encode_sequence_message @@ -76,7 +85,7 @@ let test_empty_suffix_n_prefix () = "006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000090000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in let* () = - test_signed + test_encoding ~loc:__LOC__ hello_l2_message_hex (KM.encode_sequence_message @@ -89,7 +98,7 @@ let test_empty_suffix_n_prefix () = let hello_world_l2_message_hex = "006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000120000000568656c6c6f00000005776f726c6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in - test_signed + test_encoding ~loc:__LOC__ hello_world_l2_message_hex (KM.encode_sequence_message diff --git a/tezt/tests/sc_sequencer.ml b/tezt/tests/sc_sequencer.ml index 218911aee935..9c2e9e70b7f0 100644 --- a/tezt/tests/sc_sequencer.ml +++ b/tezt/tests/sc_sequencer.ml @@ -29,7 +29,6 @@ Component: Smart Optimistic Rollups: Sequencer Invocation: dune exec tezt/tests/main.exe -- --file sc_sequencer.ml *) - open Sc_rollup_helpers open Tezos_protocol_alpha.Protocol @@ -55,39 +54,59 @@ let next_rollup_level {node; client; sc_sequencer_node; _} = let setup_sequencer_kernel ?(originator_key = Constant.bootstrap1.public_key_hash) ?(sequencer_key = Constant.bootstrap1.public_key_hash) protocol = - let* node, client = setup_l1 protocol in + (* Prepare sequencer kernel & originate it *) + let data_dir = Temp.dir "sequencer-kernel-data-dir" in + let* boot_sector = + prepare_installer_kernel + ~base_installee:"./" + ~config: + [ + Installer_kernel_config.Set + { + value = + (* encodings of State::Sequenced(edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav) *) + "00004798d2cc98473d7e250c898885718afd2e4efbcb1a1595ab9730761ed830de0f"; + to_ = "/__sequencer/state"; + }; + ] + ~preimages_dir:(Filename.concat data_dir "wasm_2_0_0") + "sequenced_kernel" + in + let boot_sector_file = Filename.temp_file "boot-sector" ".hex" in + let () = write_file boot_sector_file ~contents:boot_sector in + let* parameters_ty = + let client = Client.create_with_mode Client.Mockup in + Client.convert_data_to_json ~data:"unit" client + in + let sc_rollup_address = "sr1ExAn7W4MzgEoAWqhHwWivGCt3W8r1qH7g" in + let bootstrap_tx_kernel : Protocol.bootstrap_smart_rollup = + { + address = sc_rollup_address; + pvm_kind = "wasm_2_0_0"; + boot_sector; + parameters_ty; + } + in + + (* Run Tezos node & Sequencer node*) + let* node, client = + setup_l1 ~bootstrap_smart_rollups:[bootstrap_tx_kernel] protocol + in let sc_sequencer_node = Sc_rollup_node.create Custom node ~path:"./octez-smart-rollup-sequencer-node" + ~data_dir ~base_dir:(Client.base_dir client) ~default_operator:sequencer_key in - (* Prepare sequencer kernel & originate it *) - let* boot_sector = - prepare_installer_kernel - ~base_installee:"./" - ~preimages_dir: - (Filename.concat - (Sc_rollup_node.data_dir sc_sequencer_node) - "wasm_2_0_0") - "sequenced_kernel" - in - let* sc_rollup_address = - originate_sc_rollup - ~kind:pvm_kind - ~boot_sector - ~parameters_ty:"unit" - ~src:originator_key - client - in - (* Start a sequencer node *) + let* () = Client.bake_for_and_wait client in let* () = Sc_rollup_node.run_sequencer sc_sequencer_node sc_rollup_address - ["--log-kernel-debug"] + ["--log-kernel-debug"; "--boot-sector-file"; boot_sector_file] in let sc_rollup_client = Sc_rollup_client.create ~protocol sc_sequencer_node in return @@ -123,8 +142,7 @@ let send_message ~src client raw_msg = let wait_for_sequence_debug_message sc_node = Sc_rollup_node.wait_for sc_node "kernel_debug.v0" @@ fun json -> let message = JSON.as_string json in - if String.starts_with ~prefix:"Received a sequence message" message then - Some message + if String.starts_with ~prefix:"Received Sequence {" message then Some message else None let test_delayed_inbox_consumed = @@ -201,24 +219,14 @@ let test_delayed_inbox_consumed = let expected_sequences = List.map (fun (delayed_inbox_prefix, delayed_inbox_suffix) -> Format.sprintf - "Received a sequence message UnverifiedSigned { body: Framed { \ - destination: SmartRollupAddress { hash: SmartRollupHash(\"%s\") }, \ - payload: Sequence(Sequence { nonce: 0, delayed_messages_prefix: %d, \ - delayed_messages_suffix: %d, messages: [] }) }, signature: \ - Signature(\"edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q\") \ - }" - (Sc_rollup_repr.Address.to_b58check sc_rollup_address) + "Received Sequence { nonce: 0, delayed_messages_prefix: %d, \ + delayed_messages_suffix: %d, messages: [] } targeting our rollup" delayed_inbox_prefix delayed_inbox_suffix) @@ [(4, 1); (7, 1)] in - Check.( - ( = ) - expected_sequences - (List.rev !collected_sequences) - ~__LOC__ - (list string) - ~error_msg:"Unexpected debug messages emitted") ; + Check.((expected_sequences = List.rev !collected_sequences) (list string)) + ~error_msg:"Unexpected debug messages emitted:, should be %L, but got %R" ; Lwt.return_unit let register ~protocols = test_delayed_inbox_consumed protocols -- GitLab