From ae31aa84f1f2b488179795b4510f915c47a69e8b Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Wed, 18 Jan 2023 09:36:00 +0100 Subject: [PATCH 1/6] OPAM: relax version contraint for tezos-sapling The sapling part of tezos-rust-libs is strictly identical in versions 1.1, 1.2 and 1.3 but versions >= 1.2 requires modern versions of rustc not yet in stable distributions --- manifest/main.ml | 5 ++++- opam/tezos-sapling.opam | 2 +- opam/virtual/octez-deps.opam | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/manifest/main.ml b/manifest/main.ml index 6c972e7c037b..b5d38890dc0a 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -330,6 +330,9 @@ let tar_unix = external_lib "tar-unix" V.(at_least "2.0.1" && less_than "3.0.0") let tezos_rust_lib = opam_only ~can_vendor:false "tezos-rust-libs" V.(exactly "1.3") +let tezos_rust_lib_sapling = + opam_only ~can_vendor:false "tezos-rust-libs" V.(at_least "1.1") + let tezt_lib = external_lib ~js_compatible:false @@ -1824,7 +1827,7 @@ let octez_sapling = octez_stdlib |> open_; octez_crypto; octez_error_monad |> open_ |> open_ ~m:"TzLwtreslib"; - tezos_rust_lib; + tezos_rust_lib_sapling; octez_lwt_result_stdlib; ] ~js_of_ocaml:[[S "javascript_files"; S "runtime.js"]] diff --git a/opam/tezos-sapling.opam b/opam/tezos-sapling.opam index 2a318ea1d9bf..8e0de96773a5 100644 --- a/opam/tezos-sapling.opam +++ b/opam/tezos-sapling.opam @@ -18,7 +18,7 @@ depends: [ "tezos-stdlib" "tezos-crypto" "tezos-error-monad" - "tezos-rust-libs" { = "1.3" } + "tezos-rust-libs" { >= "1.1" } "tezos-lwt-result-stdlib" "tezos-base" {with-test} "tezos-stdlib-unix" {with-test} diff --git a/opam/virtual/octez-deps.opam b/opam/virtual/octez-deps.opam index ac0b322593ec..52c38d20c849 100644 --- a/opam/virtual/octez-deps.opam +++ b/opam/virtual/octez-deps.opam @@ -91,7 +91,7 @@ depends: [ "tezos-bls12-381-polynomial" { >= "1.0.1" & < "2.0.0" } "tezos-plompiler" { >= "1.0.1" & < "2.0.0" } "tezos-plonk" { >= "1.0.1" & < "2.0.0" } - "tezos-rust-libs" { = "1.3" } + "tezos-rust-libs" { = "1.3" & >= "1.1" } "tezt" { >= "3.0.0" } "tls" { >= "0.13.0" } "uri" { >= "3.1.0" } -- GitLab From 83957b8fb051e3ed8e62d6641853c1e59a559e5d Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Thu, 2 Feb 2023 17:50:19 +0100 Subject: [PATCH 2/6] Python: run make fmt --- tests_python/client/client.py | 4 ---- tests_python/client/client_output.py | 5 ----- tests_python/daemons/node.py | 1 - tests_python/pytest_plugins/job_selection.py | 6 +++--- tests_python/tools/utils.py | 3 +-- 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/tests_python/client/client.py b/tests_python/client/client.py index ac384f598fa6..7bce9812311b 100644 --- a/tests_python/client/client.py +++ b/tests_python/client/client.py @@ -1670,7 +1670,6 @@ class Client: def sapling_gen_key( self, key_name: str, force: bool = False, args: List[str] = None ) -> client_output.SaplingGenKeyResult: - cmd = ['sapling', 'gen', 'key', key_name, '--unencrypted'] args = args or [] if force: @@ -1714,7 +1713,6 @@ class Client: force: bool = False, args: List[str] = None, ) -> None: - mnemonic_str = " ".join(mnemonic) cmd = [ 'sapling', @@ -1739,7 +1737,6 @@ class Client: index: int, force: bool = False, ) -> client_output.SaplingDeriveKeyResult: - cmd = [ 'sapling', 'derive', @@ -1761,7 +1758,6 @@ class Client: def sapling_get_balance( self, key_name: str, contract_name: str, args: List[str] = None ) -> client_output.SaplingGetBalanceResult: - cmd = [ 'sapling', 'get', diff --git a/tests_python/client/client_output.py b/tests_python/client/client_output.py index 15a2d33f3e78..9310a9f53eb2 100644 --- a/tests_python/client/client_output.py +++ b/tests_python/client/client_output.py @@ -95,7 +95,6 @@ class GetAddressesResult: """Result of 'list known addresses' operation.""" def __init__(self, client_output: str): - pattern = re.compile(r"^(\w+):\s*(\w+).*$", re.MULTILINE) self.wallet = dict(re.findall(pattern, client_output)) @@ -236,7 +235,6 @@ class HashResult: """Result of a 'hash data' command.""" def __init__(self, client_output: str): - pattern = r'''Raw packed data: ?(0x[0-9a-f]*) Script-expression-ID-Hash: ?(\w*) Raw Script-expression-ID-Hash: ?(\w*) @@ -257,7 +255,6 @@ class SignBytesResult: """Result of a 'sign bytes ...' command.""" def __init__(self, client_output: str): - pattern = r'Signature: ?(\w*)\n' match = re.search(pattern, client_output) if match is None: @@ -269,7 +266,6 @@ class SignMessageResult: """Result of a 'sign message ...' command.""" def __init__(self, client_output: str): - pattern = r'Signature: ?(\w*)\n' match = re.search(pattern, client_output) if match is None: @@ -538,7 +534,6 @@ class CheckSignMessageResult: """Result of a 'check that message...' command.""" def __init__(self, client_output: str): - pattern = r'Signature check successful *\n' match = re.search(pattern, client_output) if match is None: diff --git a/tests_python/daemons/node.py b/tests_python/daemons/node.py index ced1ff8eb9a6..61b9d014da06 100644 --- a/tests_python/daemons/node.py +++ b/tests_python/daemons/node.py @@ -76,7 +76,6 @@ class Node: singleprocess: bool = False, env: Dict[str, str] = None, ): - """Creates a new Popen instance for a octez-node, and manages context. args: diff --git a/tests_python/pytest_plugins/job_selection.py b/tests_python/pytest_plugins/job_selection.py index 0e5ee07a2d74..3fd437a8267b 100644 --- a/tests_python/pytest_plugins/job_selection.py +++ b/tests_python/pytest_plugins/job_selection.py @@ -105,10 +105,10 @@ def knapsack(items: List[Tuple[Any, float]], bag_count: int) -> List[Bag]: for _ in range(0, bag_count): knapsack.append({'total_weight': 0.0, 'items': []}) - for (item, weight) in items: + for item, weight in items: min_index = 0 min_total_weight = knapsack[0]['total_weight'] - for (index, bag) in enumerate(knapsack[1:]): + for index, bag in enumerate(knapsack[1:]): if bag['total_weight'] < min_total_weight: min_total_weight = bag['total_weight'] min_index = index + 1 @@ -169,7 +169,7 @@ def tabulate( for row in [headers_s] + rows_s: width = cell_width[0] print(f'{{0: <{width}}}'.format(str(row[0])), end="") - for (col_idx, col) in enumerate(row[1:]): + for col_idx, col in enumerate(row[1:]): width = cell_width[col_idx + 1] print(f'{{0: >{width}}}'.format(str(col)), end="") print() diff --git a/tests_python/tools/utils.py b/tests_python/tools/utils.py index afafa8f117aa..d26390ce11f9 100644 --- a/tests_python/tools/utils.py +++ b/tests_python/tools/utils.py @@ -428,7 +428,6 @@ def assert_run_script_success( def assert_run_script_failwith( client: Client, contract: str, param: str, storage: str ) -> None: - pattern = 'script reached FAILWITH instruction' with assert_run_failure(pattern): client.run_script(contract, param, storage, trace_stack=True) @@ -454,7 +453,7 @@ def assert_typecheck_failure( def suball(replacements: List[Tuple[Pattern, str]], string: str) -> str: """Apply all the replacements in `replacements`, in order, to `string` using re.sub""" - for (pattern, replacement) in replacements: + for pattern, replacement in replacements: string = re.sub(pattern, replacement, string) return string -- GitLab From 1edd4ca594f18748f3da566b97c852f8073d90b1 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Thu, 2 Feb 2023 17:50:33 +0100 Subject: [PATCH 3/6] Sapling: update binding to match librustzcash.v5.0.0 --- .../bindings/rustzcash_ctypes_bindings.ml | 25 +++++++++---------- src/lib_sapling/rustzcash.ml | 25 +++++++------------ 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/lib_sapling/bindings/rustzcash_ctypes_bindings.ml b/src/lib_sapling/bindings/rustzcash_ctypes_bindings.ml index e62ddbd64d16..78b2943b19bf 100644 --- a/src/lib_sapling/bindings/rustzcash_ctypes_bindings.ml +++ b/src/lib_sapling/bindings/rustzcash_ctypes_bindings.ml @@ -25,9 +25,8 @@ module Bindings (F : Cstubs.FOREIGN) = struct let init_zksnark_params = foreign "librustzcash_init_zksnark_params" - (ocaml_bytes @-> size_t @-> ocaml_string @-> ocaml_bytes @-> size_t - @-> ocaml_string @-> ptr uchar @-> size_t @-> ocaml_string - @-> returning void) + (ocaml_bytes @-> size_t @-> ocaml_bytes @-> size_t @-> ptr uchar + @-> size_t @-> returning void) let nsk_to_nk = foreign @@ -63,14 +62,14 @@ module Bindings (F : Cstubs.FOREIGN) = struct let sapling_compute_cm = foreign - "librustzcash_sapling_compute_cm" - (ocaml_bytes @-> ocaml_bytes @-> uint64_t @-> ocaml_bytes @-> ocaml_bytes - @-> returning bool) + "librustzcash_sapling_compute_cmu" + (bool @-> ocaml_bytes @-> ocaml_bytes @-> uint64_t @-> ocaml_bytes + @-> ocaml_bytes @-> returning bool) let sapling_ka_agree = foreign "librustzcash_sapling_ka_agree" - (ocaml_bytes @-> ocaml_bytes @-> ocaml_bytes @-> returning bool) + (bool @-> ocaml_bytes @-> ocaml_bytes @-> ocaml_bytes @-> returning bool) let sapling_ka_derivepublic = foreign @@ -96,23 +95,23 @@ module Bindings (F : Cstubs.FOREIGN) = struct (* ZIP32 functions *) let zip32_xsk_master = foreign - "librustzcash_zip32_xsk_master" + "librustzcash_zip32_sapling_xsk_master" (ocaml_bytes @-> size_t @-> ocaml_bytes @-> returning void) let zip32_xfvk_address = foreign - "librustzcash_zip32_xfvk_address" + "librustzcash_zip32_find_sapling_address" (ocaml_bytes @-> ocaml_bytes @-> ocaml_bytes @-> ocaml_bytes - @-> returning bool) + @-> ocaml_bytes @-> returning bool) let zip32_xsk_derive = foreign - "librustzcash_zip32_xsk_derive" + "librustzcash_zip32_sapling_xsk_derive" (ocaml_bytes @-> uint32_t @-> ocaml_bytes @-> returning void) let zip32_xfvk_derive = foreign - "librustzcash_zip32_xfvk_derive" + "librustzcash_zip32_sapling_xfvk_derive" (ocaml_bytes @-> uint32_t @-> ocaml_bytes @-> returning bool) (* Prover *) @@ -146,7 +145,7 @@ module Bindings (F : Cstubs.FOREIGN) = struct let verification_ctx_init = foreign "librustzcash_sapling_verification_ctx_init" - (void @-> returning (ptr void)) + (bool @-> returning (ptr void)) let verification_ctx_free = foreign diff --git a/src/lib_sapling/rustzcash.ml b/src/lib_sapling/rustzcash.ml index 84945c332d73..fe23e428b8c1 100644 --- a/src/lib_sapling/rustzcash.ml +++ b/src/lib_sapling/rustzcash.ml @@ -394,7 +394,7 @@ let valid_position pos = Parameters of type Rust `usize` are converted to OCaml `int` because they are only file paths. NULL is a void pointer. *) -let init_zksnark_params ~spend_path ~spend_hash ~output_path ~output_hash = +let init_zksnark_params ~spend_path ~output_path = let spend_path = Bytes.of_string spend_path in let output_path = Bytes.of_string output_path in let spend_path_len = Bytes.length spend_path in @@ -402,17 +402,12 @@ let init_zksnark_params ~spend_path ~spend_hash ~output_path ~output_hash = RS.init_zksnark_params (Ctypes.ocaml_bytes_start spend_path) (Unsigned.Size_t.of_int spend_path_len) - (Ctypes.ocaml_string_start spend_hash) (Ctypes.ocaml_bytes_start output_path) (Unsigned.Size_t.of_int output_path_len) - (Ctypes.ocaml_string_start output_hash) (* Getting a NULL pointer of type uchar. Causing the warning saying we convert a void * to unsigned char* *) Ctypes.(from_voidp uchar null) Unsigned.Size_t.zero - (* Any value can be passed. The Rust code does check if the path is a NULL - pointer (i.e. see previous comment) *) - (Ctypes.ocaml_string_start "Undefined") let nsk_to_nk nsk = let nk = Bytes.create 32 in @@ -480,6 +475,7 @@ let compute_cm diversifier pk_d ~amount rcm = let cm = Bytes.create 32 in let res = RS.sapling_compute_cm + false (Ctypes.ocaml_bytes_start (of_diversifier diversifier)) (Ctypes.ocaml_bytes_start (of_pkd pk_d)) (Unsigned.UInt64.of_int64 amount) @@ -493,6 +489,7 @@ let ka_agree (p : Bytes.t) (sk : Bytes.t) = let ka = Bytes.create 32 in let res = RS.sapling_ka_agree + false (Ctypes.ocaml_bytes_start p) (Ctypes.ocaml_bytes_start sk) (Ctypes.ocaml_bytes_start ka) @@ -535,7 +532,7 @@ let proving_ctx_free ctx = RS.proving_ctx_free ctx type verification_ctx = unit Ctypes_static.ptr -let verification_ctx_init () = RS.verification_ctx_init () +let verification_ctx_init () = RS.verification_ctx_init false let verification_ctx_free ctx = RS.verification_ctx_free ctx @@ -654,10 +651,12 @@ let zip32_xsk_master seed = let zip32_xfvk_address xfvk j = let j_ret = Bytes.create 11 in let addr = Bytes.create 43 in - let bytes_xfvk = of_zip32_full_viewing_key xfvk in + let fvk = of_full_viewing_key xfvk.fvk in + let dk = xfvk.dk in let res = RS.zip32_xfvk_address - (Ctypes.ocaml_bytes_start bytes_xfvk) + (Ctypes.ocaml_bytes_start fvk) + (Ctypes.ocaml_bytes_start dk) (Ctypes.ocaml_bytes_start (of_diversifier_index j)) (Ctypes.ocaml_bytes_start j_ret) (Ctypes.ocaml_bytes_start addr) @@ -779,13 +778,7 @@ let find_params ?(getenv_opt = Sys.getenv_opt) ?(getcwd = Sys.getcwd) let init_params () = let {spend_path; output_path} = find_params () in - let spend_hash = - "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c\000" - in - let output_hash = - "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028\000" - in - init_zksnark_params ~spend_path ~spend_hash ~output_path ~output_hash + init_zksnark_params ~spend_path ~output_path let init_params_lazy = Lazy.from_fun init_params -- GitLab From 474588165919c4eaa938a9175a6434cf2ce7e8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 10 Feb 2023 11:08:00 +0100 Subject: [PATCH 4/6] Build: update tezos-rust-libs opam dependency --- .gitlab-ci.yml | 2 +- manifest/main.ml | 4 ++-- opam/tezos-sapling.opam | 2 +- opam/tezos-wasmer.opam | 2 +- opam/virtual/octez-deps.opam | 2 +- scripts/version.sh | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 457479cff029..295a41c733a8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,7 +33,7 @@ variables: # /!\ CI_REGISTRY is overriden to use a private Docker registry mirror in AWS ECR # in GitLab namespaces `nomadic-labs` and `tezos` ## This value MUST be the same as `opam_repository_tag` in `scripts/version.sh` - build_deps_image_version: e86fb42578f762ced5052c92ef484833686d6158 + build_deps_image_version: 25dac742bf06804f9fe91a8b38f98e12c9a4e80e build_deps_image_name: "${CI_REGISTRY}/tezos/opam-repository" GIT_STRATEGY: fetch GIT_DEPTH: "1" diff --git a/manifest/main.ml b/manifest/main.ml index b5d38890dc0a..d37add903098 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -328,10 +328,10 @@ let tar = external_lib "tar" V.True let tar_unix = external_lib "tar-unix" V.(at_least "2.0.1" && less_than "3.0.0") let tezos_rust_lib = - opam_only ~can_vendor:false "tezos-rust-libs" V.(exactly "1.3") + opam_only ~can_vendor:false "tezos-rust-libs" V.(exactly "1.4") let tezos_rust_lib_sapling = - opam_only ~can_vendor:false "tezos-rust-libs" V.(at_least "1.1") + opam_only ~can_vendor:false "tezos-rust-libs" V.(exactly "1.4") let tezt_lib = external_lib diff --git a/opam/tezos-sapling.opam b/opam/tezos-sapling.opam index 8e0de96773a5..e328d3273275 100644 --- a/opam/tezos-sapling.opam +++ b/opam/tezos-sapling.opam @@ -18,7 +18,7 @@ depends: [ "tezos-stdlib" "tezos-crypto" "tezos-error-monad" - "tezos-rust-libs" { >= "1.1" } + "tezos-rust-libs" { = "1.4" } "tezos-lwt-result-stdlib" "tezos-base" {with-test} "tezos-stdlib-unix" {with-test} diff --git a/opam/tezos-wasmer.opam b/opam/tezos-wasmer.opam index 0dabe6b089e0..988983f45a28 100644 --- a/opam/tezos-wasmer.opam +++ b/opam/tezos-wasmer.opam @@ -13,7 +13,7 @@ depends: [ "ctypes" { >= "0.18.0" } "ctypes-foreign" { >= "0.18.0" } "lwt" { >= "5.6.0" } - "tezos-rust-libs" { = "1.3" } + "tezos-rust-libs" { = "1.4" } ] x-opam-monorepo-opam-provided: [ "tezos-rust-libs" diff --git a/opam/virtual/octez-deps.opam b/opam/virtual/octez-deps.opam index 52c38d20c849..179fffa3a0e4 100644 --- a/opam/virtual/octez-deps.opam +++ b/opam/virtual/octez-deps.opam @@ -91,7 +91,7 @@ depends: [ "tezos-bls12-381-polynomial" { >= "1.0.1" & < "2.0.0" } "tezos-plompiler" { >= "1.0.1" & < "2.0.0" } "tezos-plonk" { >= "1.0.1" & < "2.0.0" } - "tezos-rust-libs" { = "1.3" & >= "1.1" } + "tezos-rust-libs" { = "1.4" } "tezt" { >= "3.0.0" } "tls" { >= "0.13.0" } "uri" { >= "3.1.0" } diff --git a/scripts/version.sh b/scripts/version.sh index de58c4757029..c337d6abbfad 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -20,12 +20,12 @@ export recommended_node_version=16.18.1 ## full_opam_repository is a commit hash of the public OPAM repository, i.e. ## https://github.com/ocaml/opam-repository -export full_opam_repository_tag=6698d7801c195f2f1e5ef81997bb09a75d71f6c0 +export full_opam_repository_tag=4d8e7321c83290c76d178bd18b7bd73b332168e3 ## opam_repository is an additional, tezos-specific opam repository. ## This value MUST be the same as `build_deps_image_version` in `.gitlab/ci/templates.yml export opam_repository_url=https://gitlab.com/tezos/opam-repository -export opam_repository_tag="${OPAM_REPOSITORY_TAG:-e86fb42578f762ced5052c92ef484833686d6158}" +export opam_repository_tag="${OPAM_REPOSITORY_TAG:-25dac742bf06804f9fe91a8b38f98e12c9a4e80e}" export opam_repository_git="$opam_repository_url.git" export opam_repository="$opam_repository_git"\#"$opam_repository_tag" -- GitLab From 076f47bd743891ff1f50c2808f52b03ed5590259 Mon Sep 17 00:00:00 2001 From: Killian Delarue Date: Thu, 16 Feb 2023 11:03:51 +0100 Subject: [PATCH 5/6] CI: add BISECT_FILE variable in .tezt_template --- .gitlab/ci/jobs/test/tezt.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab/ci/jobs/test/tezt.yml b/.gitlab/ci/jobs/test/tezt.yml index 1afd9f021fcd..a424ba8a7bc4 100644 --- a/.gitlab/ci/jobs/test/tezt.yml +++ b/.gitlab/ci/jobs/test/tezt.yml @@ -9,6 +9,7 @@ include: .gitlab/ci/jobs/test/common.yml .tezt_template: variables: JUNIT: "tezt-junit.xml" + BISECT_FILE: "$CI_PROJECT_DIR/_coverage_output/" artifacts: reports: junit: $JUNIT -- GitLab From efaeef430e761598a920f6f26c3a9275596c9cb1 Mon Sep 17 00:00:00 2001 From: Arvid Jakobsson Date: Wed, 11 Jan 2023 11:07:03 +0100 Subject: [PATCH 6/6] Tezt: translate [test_nonce_seed_revelation.py] --- .../tests_015/test_nonce_seed_revelation.py | 134 ---------- .../tests_016/test_nonce_seed_revelation.py | 134 ---------- .../tests_alpha/test_nonce_seed_revelation.py | 134 ---------- tezt/tests/main.ml | 1 + tezt/tests/nonce_seed_revelation.ml | 229 ++++++++++++++++++ 5 files changed, 230 insertions(+), 402 deletions(-) delete mode 100644 tests_python/tests_015/test_nonce_seed_revelation.py delete mode 100644 tests_python/tests_016/test_nonce_seed_revelation.py delete mode 100644 tests_python/tests_alpha/test_nonce_seed_revelation.py create mode 100644 tezt/tests/nonce_seed_revelation.ml diff --git a/tests_python/tests_015/test_nonce_seed_revelation.py b/tests_python/tests_015/test_nonce_seed_revelation.py deleted file mode 100644 index b089e5361e11..000000000000 --- a/tests_python/tests_015/test_nonce_seed_revelation.py +++ /dev/null @@ -1,134 +0,0 @@ -import time -import pytest -from tools import constants -from launchers.sandbox import Sandbox -from . import protocol - - -BLOCKS_PER_COMMITMENT = protocol.PARAMETERS['blocks_per_commitment'] -BLOCKS_PER_CYCLE = protocol.PARAMETERS['blocks_per_cycle'] -FIRST_PROTOCOL_BLOCK = 1 -TIMEOUT = 60 - -MINIMAL_BLOCK_DELAY = 1 -DELAY_INCREMENT_PER_ROUND = 1 -TEST_DURATION = ( - FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE -) * MINIMAL_BLOCK_DELAY -NUM_NODES = 5 - - -@pytest.mark.incremental -@pytest.mark.slow -@pytest.mark.baker -class TestNonceSeedRevelation: - """Test baker injection of nonce revelations. - - See http://tezos.gitlab.io/015_lima/proof_of_stake.html - - Runs a node and a baker. The baker bakes two full cycles. - We collect nonce hashes from the first cycle. And check - that they are revealed in the second cycle""" - - def test_init(self, sandbox: Sandbox): - """Run a node and a baker. - - The node runs in archive mode to get metadata in `client.get_block()`. - The protocol is activated in the past so the baker can submit blocks - immediately without waiting for current time.""" - - node_params = constants.NODE_PARAMS + ['--history-mode', 'archive'] - for i in range(NUM_NODES): - sandbox.add_node(i, params=node_params) - - # client setup - parameters = protocol.get_parameters() - parameters['minimal_block_delay'] = str(MINIMAL_BLOCK_DELAY) - parameters['delay_increment_per_round'] = str(DELAY_INCREMENT_PER_ROUND) - protocol.activate(sandbox.client(0), parameters=parameters) - - # baker setup - # delegated_accounts = [f'bootstrap{i}' for i in range(1, 6)] - for i in range(NUM_NODES): - sandbox.add_baker( - i, - [f"bootstrap{i + 1}"], - proto=protocol.DAEMON, - log_levels=constants.TENDERBAKE_BAKER_LOG_LEVELS, - run_params=['--liquidity-baking-toggle-vote', 'pass'], - ) - - @pytest.mark.timeout(2 * TEST_DURATION) - def test_wait_for_two_cycles(self, sandbox: Sandbox): - """Poll the node until target level is reached""" - target = FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE - level = target - 1 - while level < target: - time.sleep( - 4 * MINIMAL_BLOCK_DELAY - ) # sleep first to avoid useless first query - if sandbox.client(0).get_level() >= target: - break - # No need to bake more - for i in range(NUM_NODES): - sandbox.rm_baker(i, proto=protocol.DAEMON) - - def test_get_all_blocks(self, sandbox: Sandbox, session: dict): - """Retrieve all blocks for two full cycles.""" - blocks = [ - sandbox.client(0).get_block(FIRST_PROTOCOL_BLOCK + i) - for i in range(2 * BLOCKS_PER_CYCLE) - ] - session['blocks'] = blocks - - def test_cycle_alignment(self, session): - """Test cycles start where they are supposed to start. - - Not really needed but helps clarifying cycles positions.""" - - blocks = session['blocks'] - # blocks[0] is considered cycle = 0, cycle_position = 0 for the new - # protocol, but because it is a protocol transition block, it - # doesn't have the "cycle" and "cycle_position" metadata (unlike - # the remaining blocks) - initial_block_level = blocks[1]['metadata']['level_info'] - assert initial_block_level['cycle'] == 0 - assert initial_block_level['cycle_position'] == 1 - final_block_level = blocks[BLOCKS_PER_CYCLE]['metadata']['level_info'] - assert final_block_level['cycle'] == 1 - assert final_block_level['cycle_position'] == 0 - - def test_collect_seed_nonce_hashes(self, session): - """Collect nonce hashes in the block headers in the first cycle""" - seed_nonce_hashes = {} - blocks = session['blocks'] - for i in range(BLOCKS_PER_CYCLE // BLOCKS_PER_COMMITMENT): - level = (i + 1) * BLOCKS_PER_COMMITMENT - 1 - seed_nonce_hash = blocks[level]['header']['seed_nonce_hash'] - seed_nonce_hashes[level] = seed_nonce_hash - session['seed_nonce_hashes'] = seed_nonce_hashes - - def test_check_revelations(self, session): - """Collect reveal ops in second cycle and check they match - the nonce hashes from first cycle.""" - blocks = session['blocks'] - seed_nonce_hashes = session['seed_nonce_hashes'] - ops = [] - # collect all operations - for i in range(BLOCKS_PER_CYCLE, 2 * BLOCKS_PER_CYCLE): - ops.extend(blocks[i]['operations'][2]) - reveal_ops = {} - for operation in ops: - content = operation['contents'][0] - # there should be only revelations there - assert content['kind'] == "seed_nonce_revelation" - level = content['level'] - FIRST_PROTOCOL_BLOCK - # Can't submit twice the same reveal op - assert level not in reveal_ops - # level should match a seed - assert level in seed_nonce_hashes - reveal_ops[level] = content['nonce'] - - # check all nonce hashes have been revealed - assert len(reveal_ops) == len(seed_nonce_hashes) - # we could go a step further and check that revelations are correct diff --git a/tests_python/tests_016/test_nonce_seed_revelation.py b/tests_python/tests_016/test_nonce_seed_revelation.py deleted file mode 100644 index 06b51ca5c6c3..000000000000 --- a/tests_python/tests_016/test_nonce_seed_revelation.py +++ /dev/null @@ -1,134 +0,0 @@ -import time -import pytest -from tools import constants -from launchers.sandbox import Sandbox -from . import protocol - - -BLOCKS_PER_COMMITMENT = protocol.PARAMETERS['blocks_per_commitment'] -BLOCKS_PER_CYCLE = protocol.PARAMETERS['blocks_per_cycle'] -FIRST_PROTOCOL_BLOCK = 1 -TIMEOUT = 60 - -MINIMAL_BLOCK_DELAY = 1 -DELAY_INCREMENT_PER_ROUND = 1 -TEST_DURATION = ( - FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE -) * MINIMAL_BLOCK_DELAY -NUM_NODES = 5 - - -@pytest.mark.incremental -@pytest.mark.slow -@pytest.mark.baker -class TestNonceSeedRevelation: - """Test baker injection of nonce revelations. - - See http://tezos.gitlab.io/016_mumbai/proof_of_stake.html - - Runs a node and a baker. The baker bakes two full cycles. - We collect nonce hashes from the first cycle. And check - that they are revealed in the second cycle""" - - def test_init(self, sandbox: Sandbox): - """Run a node and a baker. - - The node runs in archive mode to get metadata in `client.get_block()`. - The protocol is activated in the past so the baker can submit blocks - immediately without waiting for current time.""" - - node_params = constants.NODE_PARAMS + ['--history-mode', 'archive'] - for i in range(NUM_NODES): - sandbox.add_node(i, params=node_params) - - # client setup - parameters = protocol.get_parameters() - parameters['minimal_block_delay'] = str(MINIMAL_BLOCK_DELAY) - parameters['delay_increment_per_round'] = str(DELAY_INCREMENT_PER_ROUND) - protocol.activate(sandbox.client(0), parameters=parameters) - - # baker setup - # delegated_accounts = [f'bootstrap{i}' for i in range(1, 6)] - for i in range(NUM_NODES): - sandbox.add_baker( - i, - [f"bootstrap{i + 1}"], - proto=protocol.DAEMON, - log_levels=constants.TENDERBAKE_BAKER_LOG_LEVELS, - run_params=['--liquidity-baking-toggle-vote', 'pass'], - ) - - @pytest.mark.timeout(2 * TEST_DURATION) - def test_wait_for_two_cycles(self, sandbox: Sandbox): - """Poll the node until target level is reached""" - target = FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE - level = target - 1 - while level < target: - time.sleep( - 4 * MINIMAL_BLOCK_DELAY - ) # sleep first to avoid useless first query - if sandbox.client(0).get_level() >= target: - break - # No need to bake more - for i in range(NUM_NODES): - sandbox.rm_baker(i, proto=protocol.DAEMON) - - def test_get_all_blocks(self, sandbox: Sandbox, session: dict): - """Retrieve all blocks for two full cycles.""" - blocks = [ - sandbox.client(0).get_block(FIRST_PROTOCOL_BLOCK + i) - for i in range(2 * BLOCKS_PER_CYCLE) - ] - session['blocks'] = blocks - - def test_cycle_alignment(self, session): - """Test cycles start where they are supposed to start. - - Not really needed but helps clarifying cycles positions.""" - - blocks = session['blocks'] - # blocks[0] is considered cycle = 0, cycle_position = 0 for the new - # protocol, but because it is a protocol transition block, it - # doesn't have the "cycle" and "cycle_position" metadata (unlike - # the remaining blocks) - initial_block_level = blocks[1]['metadata']['level_info'] - assert initial_block_level['cycle'] == 0 - assert initial_block_level['cycle_position'] == 1 - final_block_level = blocks[BLOCKS_PER_CYCLE]['metadata']['level_info'] - assert final_block_level['cycle'] == 1 - assert final_block_level['cycle_position'] == 0 - - def test_collect_seed_nonce_hashes(self, session): - """Collect nonce hashes in the block headers in the first cycle""" - seed_nonce_hashes = {} - blocks = session['blocks'] - for i in range(BLOCKS_PER_CYCLE // BLOCKS_PER_COMMITMENT): - level = (i + 1) * BLOCKS_PER_COMMITMENT - 1 - seed_nonce_hash = blocks[level]['header']['seed_nonce_hash'] - seed_nonce_hashes[level] = seed_nonce_hash - session['seed_nonce_hashes'] = seed_nonce_hashes - - def test_check_revelations(self, session): - """Collect reveal ops in second cycle and check they match - the nonce hashes from first cycle.""" - blocks = session['blocks'] - seed_nonce_hashes = session['seed_nonce_hashes'] - ops = [] - # collect all operations - for i in range(BLOCKS_PER_CYCLE, 2 * BLOCKS_PER_CYCLE): - ops.extend(blocks[i]['operations'][2]) - reveal_ops = {} - for operation in ops: - content = operation['contents'][0] - # there should be only revelations there - assert content['kind'] == "seed_nonce_revelation" - level = content['level'] - FIRST_PROTOCOL_BLOCK - # Can't submit twice the same reveal op - assert level not in reveal_ops - # level should match a seed - assert level in seed_nonce_hashes - reveal_ops[level] = content['nonce'] - - # check all nonce hashes have been revealed - assert len(reveal_ops) == len(seed_nonce_hashes) - # we could go a step further and check that revelations are correct diff --git a/tests_python/tests_alpha/test_nonce_seed_revelation.py b/tests_python/tests_alpha/test_nonce_seed_revelation.py deleted file mode 100644 index 44ac88f405cc..000000000000 --- a/tests_python/tests_alpha/test_nonce_seed_revelation.py +++ /dev/null @@ -1,134 +0,0 @@ -import time -import pytest -from tools import constants -from launchers.sandbox import Sandbox -from . import protocol - - -BLOCKS_PER_COMMITMENT = protocol.PARAMETERS['blocks_per_commitment'] -BLOCKS_PER_CYCLE = protocol.PARAMETERS['blocks_per_cycle'] -FIRST_PROTOCOL_BLOCK = 1 -TIMEOUT = 60 - -MINIMAL_BLOCK_DELAY = 1 -DELAY_INCREMENT_PER_ROUND = 1 -TEST_DURATION = ( - FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE -) * MINIMAL_BLOCK_DELAY -NUM_NODES = 5 - - -@pytest.mark.incremental -@pytest.mark.slow -@pytest.mark.baker -class TestNonceSeedRevelation: - """Test baker injection of nonce revelations. - - See http://tezos.gitlab.io/alpha/proof_of_stake.html - - Runs a node and a baker. The baker bakes two full cycles. - We collect nonce hashes from the first cycle. And check - that they are revealed in the second cycle""" - - def test_init(self, sandbox: Sandbox): - """Run a node and a baker. - - The node runs in archive mode to get metadata in `client.get_block()`. - The protocol is activated in the past so the baker can submit blocks - immediately without waiting for current time.""" - - node_params = constants.NODE_PARAMS + ['--history-mode', 'archive'] - for i in range(NUM_NODES): - sandbox.add_node(i, params=node_params) - - # client setup - parameters = protocol.get_parameters() - parameters['minimal_block_delay'] = str(MINIMAL_BLOCK_DELAY) - parameters['delay_increment_per_round'] = str(DELAY_INCREMENT_PER_ROUND) - protocol.activate(sandbox.client(0), parameters=parameters) - - # baker setup - # delegated_accounts = [f'bootstrap{i}' for i in range(1, 6)] - for i in range(NUM_NODES): - sandbox.add_baker( - i, - [f"bootstrap{i + 1}"], - proto=protocol.DAEMON, - log_levels=constants.TENDERBAKE_BAKER_LOG_LEVELS, - run_params=['--liquidity-baking-toggle-vote', 'pass'], - ) - - @pytest.mark.timeout(2 * TEST_DURATION) - def test_wait_for_two_cycles(self, sandbox: Sandbox): - """Poll the node until target level is reached""" - target = FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE - level = target - 1 - while level < target: - time.sleep( - 4 * MINIMAL_BLOCK_DELAY - ) # sleep first to avoid useless first query - if sandbox.client(0).get_level() >= target: - break - # No need to bake more - for i in range(NUM_NODES): - sandbox.rm_baker(i, proto=protocol.DAEMON) - - def test_get_all_blocks(self, sandbox: Sandbox, session: dict): - """Retrieve all blocks for two full cycles.""" - blocks = [ - sandbox.client(0).get_block(FIRST_PROTOCOL_BLOCK + i) - for i in range(2 * BLOCKS_PER_CYCLE) - ] - session['blocks'] = blocks - - def test_cycle_alignment(self, session): - """Test cycles start where they are supposed to start. - - Not really needed but helps clarifying cycles positions.""" - - blocks = session['blocks'] - # blocks[0] is considered cycle = 0, cycle_position = 0 for the new - # protocol, but because it is a protocol transition block, it - # doesn't have the "cycle" and "cycle_position" metadata (unlike - # the remaining blocks) - initial_block_level = blocks[1]['metadata']['level_info'] - assert initial_block_level['cycle'] == 0 - assert initial_block_level['cycle_position'] == 1 - final_block_level = blocks[BLOCKS_PER_CYCLE]['metadata']['level_info'] - assert final_block_level['cycle'] == 1 - assert final_block_level['cycle_position'] == 0 - - def test_collect_seed_nonce_hashes(self, session): - """Collect nonce hashes in the block headers in the first cycle""" - seed_nonce_hashes = {} - blocks = session['blocks'] - for i in range(BLOCKS_PER_CYCLE // BLOCKS_PER_COMMITMENT): - level = (i + 1) * BLOCKS_PER_COMMITMENT - 1 - seed_nonce_hash = blocks[level]['header']['seed_nonce_hash'] - seed_nonce_hashes[level] = seed_nonce_hash - session['seed_nonce_hashes'] = seed_nonce_hashes - - def test_check_revelations(self, session): - """Collect reveal ops in second cycle and check they match - the nonce hashes from first cycle.""" - blocks = session['blocks'] - seed_nonce_hashes = session['seed_nonce_hashes'] - ops = [] - # collect all operations - for i in range(BLOCKS_PER_CYCLE, 2 * BLOCKS_PER_CYCLE): - ops.extend(blocks[i]['operations'][2]) - reveal_ops = {} - for operation in ops: - content = operation['contents'][0] - # there should be only revelations there - assert content['kind'] == "seed_nonce_revelation" - level = content['level'] - FIRST_PROTOCOL_BLOCK - # Can't submit twice the same reveal op - assert level not in reveal_ops - # level should match a seed - assert level in seed_nonce_hashes - reveal_ops[level] = content['nonce'] - - # check all nonce hashes have been revealed - assert len(reveal_ops) == len(seed_nonce_hashes) - # we could go a step further and check that revelations are correct diff --git a/tezt/tests/main.ml b/tezt/tests/main.ml index 39ae8e043003..89c0de997385 100644 --- a/tezt/tests/main.ml +++ b/tezt/tests/main.ml @@ -141,6 +141,7 @@ let register_protocol_tests_that_use_supports_correctly () = Multinode_snapshot.register ~protocols ; Node_cors.register ~protocols ; Node_event_level.register ~protocols ; + Nonce_seed_revelation.register ~protocols ; Normalize.register ~protocols ; Operation_validation.register ~protocols ; Operation_size.register ~protocols ; diff --git a/tezt/tests/nonce_seed_revelation.ml b/tezt/tests/nonce_seed_revelation.ml new file mode 100644 index 000000000000..73915431290d --- /dev/null +++ b/tezt/tests/nonce_seed_revelation.ml @@ -0,0 +1,229 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2021 Nomadic Labs *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(* Testing + ------- + Component: Consensus + Invocation: dune exec tezt/tests/main.exe -- --file nonce_seed_revelation.ml + Subject: Tests injection of nonce revelations +*) + +let first_protocol_block = 1 + +let minimal_block_delay = 1 + +let delay_increment_per_round = 1 + +let num_nodes = 5 + +type block = { + cycle : int option; + cycle_position : int option; + seed_nonce_hash : string option; + operations : JSON.t list; +} + +(* Test baker injection of nonce revelations. + See http://tezos.gitlab.io/alpha/proof_of_stake.html + Runs a node and a baker. The baker bakes two full cycles. + We collect nonce hashes from the first cycle. And check + that they are revealed in the second cycle *) +let test_nonce_seed_revelation = + Protocol.register_test + ~__FILE__ + ~title:"Nonce seed revelation" + ~tags:["nonce"; "seed"; "revelation"] + @@ fun protocol -> + (* Run a node and a baker. + The node runs in archive mode to obtain metadata with [RPC.get_chain_block]. *) + let* parameter_file = + Protocol.write_parameter_file + ~base:(Right (protocol, None)) + [ + (["minimal_block_delay"], `String_of_int minimal_block_delay); + (["delay_increment_per_round"], `String_of_int delay_increment_per_round); + ] + in + (* Get protocol parameters *) + let blocks_per_cycle, blocks_per_commitment = + let parameters = JSON.parse_file parameter_file in + JSON. + ( parameters |-> "blocks_per_cycle" |> as_int, + parameters |-> "blocks_per_commitment" |> as_int ) + in + let target_level = first_protocol_block + (2 * blocks_per_cycle) in + let nodes = + Cluster.create num_nodes [Synchronisation_threshold 0; History_mode Archive] + in + let head_node = List.hd nodes in + Cluster.clique nodes ; + let* () = Cluster.start nodes in + let* client = Client.init ~endpoint:(Node head_node) () in + (* Set up promise to wait for level, before starting bakers *) + let cycle_two_promise = + Lwt_list.iter_p + (fun node -> + let* (_ : int) = Node.wait_for_level node target_level in + unit) + nodes + in + (* Start bakers before activating alpha *) + let bakers_promise = + Lwt_list.mapi_p + (fun i node -> + let* client = Client.init ~endpoint:(Node node) () in + let delegates = [Account.Bootstrap.keys.(i).alias] in + Baker.init ~protocol ~delegates node client) + nodes + in + let* () = + Client.activate_protocol_and_wait + ~timestamp:Now + ~parameter_file + ~protocol + client + in + (* Wait for the bakers to start *) + let* bakers = bakers_promise in + Log.info "Wait for two cycles" ; + (* Wait until target level is reached *) + let* () = cycle_two_promise in + (* No need to bake further *) + let* () = Lwt_list.iter_p Baker.terminate bakers in + Log.info "Get all blocks" ; + (* Retrieve all blocks for two full cycles. *) + let* blocks = + Lwt_list.map_p + (fun level -> + let* block = + RPC.call ~log_request:false head_node + @@ RPC.get_chain_block ~block:(string_of_int level) () + in + let level_info = JSON.(block |-> "metadata" |-> "level_info") in + return + JSON. + { + cycle = level_info |-> "cycle" |> as_int_opt; + cycle_position = level_info |-> "cycle_position" |> as_int_opt; + seed_nonce_hash = + block |-> "header" |-> "seed_nonce_hash" |> as_string_opt; + operations = block |-> "operations" |=> 2 |> as_list; + }) + (range first_protocol_block (2 * blocks_per_cycle)) + in + let blocks = Array.of_list blocks in + Log.info "Cycle alignment" ; + (* Test that cycles start where they are supposed to start. + Not really needed but helps clarifying cycles positions. *) + (* blocks[0] is considered cycle = 0, cycle_position = 0 for the new + protocol, but because it is a protocol transition block, it + doesn't have the "cycle" and "cycle_position" metadata (unlike + the remaining blocks) *) + let initial_block = blocks.(1) in + let final_block = blocks.(blocks_per_cycle) in + Check.( + (initial_block.cycle = Some 0) + (option int) + ~__LOC__ + ~error_msg:"Expected %R, got %L" ; + (initial_block.cycle_position = Some 1) + (option int) + ~__LOC__ + ~error_msg:"Expected %R, got %L" ; + (final_block.cycle = Some 1) + (option int) + ~__LOC__ + ~error_msg:"Expected %R, got %L" ; + (final_block.cycle_position = Some 0) + (option int) + ~__LOC__ + ~error_msg:"Expected %R, got %L") ; + let start_collect = 0 in + let stop_collect = (blocks_per_cycle / blocks_per_commitment) - 1 in + Log.info "Collect seed nonce hashes in first cycle" ; + (* Collect nonce hashes in the block headers in the first cycle *) + let seed_nonce_hashes : (int, string) Hashtbl.t = Hashtbl.create 5 in + let () = + List.iter + (fun i -> + let level = ((i + 1) * blocks_per_commitment) - first_protocol_block in + Log.info "Collecting seed nonce in block %d" level ; + match blocks.(level).seed_nonce_hash with + | Some seed_nonce_hash -> + Hashtbl.add seed_nonce_hashes level seed_nonce_hash + | None -> + Test.fail "Expected to find a seed nonce hash at level %d" level) + (range start_collect stop_collect) + in + let start_collect_revelations = blocks_per_cycle in + let len_collect_revelations = blocks_per_cycle in + Log.info + "Check revelations in blocks [%d..%d]" + start_collect_revelations + (start_collect_revelations + len_collect_revelations) ; + (* Collect reveal ops in second cycle and check that their level match + those of the nonce hashes from first cycle. *) + let ops = + List.concat_map + (fun block -> block.operations) + (Array.sub blocks start_collect_revelations len_collect_revelations + |> Array.to_list) + in + let reveal_ops : (int, string) Hashtbl.t = Hashtbl.create 5 in + let () = + List.iter + (fun op -> + let contents = JSON.(op |-> "contents" |=> 0) in + let kind = JSON.(contents |-> "kind" |> as_string) in + let level = + JSON.(contents |-> "level" |> as_int) - first_protocol_block + in + Check.( + (kind = "seed_nonce_revelation") + string + ~__LOC__ + ~error_msg:"Expected %R, got %L" ; + is_false + (Hashtbl.mem reveal_ops level) + ~__LOC__ + ~error_msg:"Cannot submit twice the same reveal operation" ; + is_true + (Hashtbl.mem seed_nonce_hashes level) + ~__LOC__ + ~error_msg:"Level should match a seed") ; + Log.info "Checked revelation for level %d" level ; + Hashtbl.add reveal_ops level JSON.(contents |-> "nonce" |> as_string)) + ops + in + Check.( + Hashtbl.(length reveal_ops = length seed_nonce_hashes) + int + ~__LOC__ + ~error_msg: + "Expected the same number of reveal operations (%L) as seed nonce \ + hashes (%R)") ; + unit + +let register ~protocols = test_nonce_seed_revelation protocols -- GitLab