diff --git a/devtools/yes_wallet/test/bench_signature_perf.ml b/devtools/yes_wallet/test/bench_signature_perf.ml index eaa5c48e18f96b2a0e47c9b381181525aad9436d..d1d354de60eefe35d61e728208471ef655de9551 100644 --- a/devtools/yes_wallet/test/bench_signature_perf.ml +++ b/devtools/yes_wallet/test/bench_signature_perf.ml @@ -32,19 +32,23 @@ let keys = let keys_p = Tezos_crypto.Signature.generate_key ~algo:P256 () in let keys_e = Tezos_crypto.Signature.generate_key ~algo:Ed25519 () in let keys_s = Tezos_crypto.Signature.generate_key ~algo:Secp256k1 () in + let keys_b = Tezos_crypto.Signature.generate_key ~algo:Bls () in function | Tezos_crypto.Signature.P256 -> keys_p | Ed25519 -> keys_e | Secp256k1 -> keys_s + | Bls -> keys_b let wrong_keys = let keys_p = Tezos_crypto.Signature.generate_key ~algo:P256 () in let keys_e = Tezos_crypto.Signature.generate_key ~algo:Ed25519 () in let keys_s = Tezos_crypto.Signature.generate_key ~algo:Secp256k1 () in + let keys_b = Tezos_crypto.Signature.generate_key ~algo:Bls () in function | Tezos_crypto.Signature.P256 -> keys_p | Ed25519 -> keys_e | Secp256k1 -> keys_s + | Bls -> keys_b let wrong_pk algo = let _, pk, _ = wrong_keys algo in @@ -102,6 +106,7 @@ let str_of_algo = function | Tezos_crypto.Signature.Ed25519 -> "Ed25519" | Tezos_crypto.Signature.Secp256k1 -> "Secp256k1" | Tezos_crypto.Signature.P256 -> "P256" + | Tezos_crypto.Signature.Bls -> "Bls" let time ~yes_crypto ~algo size datas = Format.eprintf "generating signatures...@?" ; @@ -159,5 +164,5 @@ let () = let _ = repeat 5 (time ~algo 100_000) (generate_data 1_000 100_000) in let _ = repeat 5 (time ~algo 1_000_000) (generate_data 1_000 1_000_000) in ()) - [Ed25519; Secp256k1; P256] + [Ed25519; Secp256k1; P256; Bls] (* let _ = time (generate_data 1_000 10_000_000) *) diff --git a/docs/alpha/blocks_ops.rst b/docs/alpha/blocks_ops.rst index f4c25e2416b7e98bbc746df0e607181833dd4a1b..7f94d9b5d313ea70f195b07a195365a0aca13b7b 100644 --- a/docs/alpha/blocks_ops.rst +++ b/docs/alpha/blocks_ops.rst @@ -181,7 +181,7 @@ manager operations are the only fee-paying and *baker*), or to register themselves as delegates. - The ``Update_consensus_key`` operation allows users to delegate the responsibility of signing blocks and consensus-related operations to - another account. + another account. Note that consensus keys cannot be BLS public keys. - The ``Origination`` operation is used to :ref:`originate`, that is to deploy, smart contracts in the Tezos blockchain. diff --git a/docs/alpha/glossary.rst b/docs/alpha/glossary.rst index 7d50b69d4479db1bd7fef66d17b33859c925f029..4b8c1b926efe43e6a3f9d08aa7f7def56c8a3757 100644 --- a/docs/alpha/glossary.rst +++ b/docs/alpha/glossary.rst @@ -87,7 +87,7 @@ _`Delegate` rights to participate in consensus (aka baking_ rights) and in governance. The delegate's rights are calculated based on its own tokens plus the sum of tokens - delegated to it. + delegated to it. Note that since ``tz4`` accounts cannot be delegates. _`Delegation` An operation_ in which an account_ balance is lent to a @@ -140,7 +140,7 @@ _`Implicit account` If *registered*, an `implicit account`_ can act as a delegate_. The address of an `implicit account`_ always starts with the - letters `tz` followed by `1`, `2` or `3` (depending on the + letters `tz` followed by `1`, `2`, `3` or `4` (depending on the signature scheme) and finally the hash of the public key. _`Layer 1` diff --git a/docs/alpha/michelson.rst b/docs/alpha/michelson.rst index c0da21f399d1c74302429e0a761805cbaa260d4b..53d581dbf42ab1da084af6db06bf06932dcc8e79 100644 --- a/docs/alpha/michelson.rst +++ b/docs/alpha/michelson.rst @@ -39,8 +39,8 @@ tokens (and be the destinations of transactions). - An implicit account is a non programmable account, whose tokens are spendable and delegatable by a public key. Its address is - directly the public key hash, and starts with ``tz1``, ``tz2`` or - ``tz3``. + directly the public key hash, and starts with ``tz1``, ``tz2``, + ``tz3`` or ``tz4``. - A smart contract is a programmable account. A transaction to such an address can provide data, and can fail for reasons decided by its Michelson code. Its address is a unique hash that depends on @@ -50,8 +50,8 @@ From Michelson, they are indistinguishable. A safe way to think about this is to consider that implicit accounts are smart contracts that always succeed to receive tokens, and does nothing else. -Another kind of addresses, prefixed by ``txr1`` and ``tz4``, are -related to :doc:`transaction rollups <./transaction_rollups>`. +Another kind of addresses, prefixed by ``txr1``, are related to +:doc:`transaction rollups <./transaction_rollups>`. Finally, addresses prefixed with ``scr1`` identify smart rollups. @@ -1893,7 +1893,9 @@ optional delegate, the initial amount taken from the current contract, and the initial storage of the originated contract. The contract is returned as a first class value (to be dropped, passed as parameter or stored). The ``CONTRACT 'p`` instruction will fail -until it is actually originated. +until it is actually originated. Note that since ``tz4`` addresses +cannot be registered as delegates, the origination operation will fail +if the delegate is a ``tz4``. - ``TRANSFER_TOKENS``: Forge a transaction. @@ -1919,6 +1921,7 @@ key hash of a registered delegate that is not the current delegate of the contract, then this operation sets the delegate of the contract to this registered delegate. The operation fails if ``kh`` is the current delegate of the contract or if ``kh`` is not a registered delegate. +Note that ``tz4`` addresses cannot be registered as delegates. - ``BALANCE``: Push the current amount of mutez held by the executing contract, including any mutez added by the calling transaction. diff --git a/docs/alpha/proof_of_stake.rst b/docs/alpha/proof_of_stake.rst index 688f6b4bfba10aa54835510fba729436611d419c..450f9e9d2696b94a43bc4c0cde5820ffe185b1d5 100644 --- a/docs/alpha/proof_of_stake.rst +++ b/docs/alpha/proof_of_stake.rst @@ -28,7 +28,8 @@ Delegation ---------- A *delegate* is any :ref:`implicit account ` registered as -such by emitting a delegate registration operation. +such by emitting a delegate registration operation. Note that ``tz4`` accounts +cannot be registered as delegate. Any :ref:`account ` (implicit or originated) can specify a delegate through a delegation operation. diff --git a/docs/introduction/howtouse.rst b/docs/introduction/howtouse.rst index b26caebfab4c4872a745a21a2e9afb339eaf935a..794cb6c234d45b7c7f0dcaa4fa7a692e72874f36 100644 --- a/docs/introduction/howtouse.rst +++ b/docs/introduction/howtouse.rst @@ -297,9 +297,10 @@ In more realistic scenarios, you should supply the option ``--encrypted`` when g $ octez-client gen keys bob --encrypted -Tezos support three different ECC (`Elliptic-Curve Cryptography `_) schemes: *Ed25519*, *secp256k1* (the -one used in Bitcoin), and *P-256* (also called *secp256r1*). The two -latter curves have been added for interoperability with Bitcoin and +Tezos supports four different ECC (`Elliptic-Curve Cryptography `_) schemes: *Ed25519*, *secp256k1* (the +one used in Bitcoin), *P-256* (also called *secp256r1*), and *BLS* (variant +*MinPk*, for aggregated signatures). The secp256k1 and P256 +curves have been added for interoperability with Bitcoin and Hardware Security Modules (*HSMs*) mostly. Unless your use case requires those, you should probably use *Ed25519*. We use a verified library for Ed25519, and it is generally recommended over other curves diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index 814a1c84191bfbb40f83ab220a3bda990d0994c1..776f36c96d5a754a4ebe35bcf99d02630e0c4881 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -60,6 +60,14 @@ RPC Changes Operation receipts ------------------ +Cryptography +------------ + +- Support for BLS signatures and introduction of a new account type whose + address has the prefix ``tz4`` (whose keys are BLS-MinPk key pairs). The + ``CHECK_SIGNATURE`` instruction of Michelson can also check BLS + signatures. ``tz4`` accounts are forbidden to be delegates. (MR :gl:`!5444`) + Bug Fixes --------- diff --git a/manifest/main.ml b/manifest/main.ml index 7c0f62242a1c0ce722a209a879025e111aabcec4..9dbe9e466d1ffb1159e5b50f6a508e259d400489 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -3739,6 +3739,7 @@ end = struct (3, "test_zk_rollup_encoding", N.(number >= 015)); (3, "test_dal_slot_proof", N.(number >= 016)); (3, "test_compare_operations", N.(number >= 015)); + (3, "test_operation_encoding", N.(number >= 016)); ] |> List.filter_map (fun (i, n, b) -> if b then Some (i, n) else None) in diff --git a/src/bin_signer/handler.ml b/src/bin_signer/handler.ml index 4870fc3814b94aea3d329cb8eb2931bc85063b62..30cb791bb114758f65a3d6f79f01bb3df7c73376 100644 --- a/src/bin_signer/handler.ml +++ b/src/bin_signer/handler.ml @@ -46,7 +46,7 @@ module High_watermark = struct (req "level" int32) (opt "round" int32) (req "hash" raw_hash) - (opt "signature" Tezos_crypto.Signature.encoding) + (opt "signature" (dynamic_size Tezos_crypto.Signature.encoding)) let get_level_and_round_for_tenderbake_block bytes = (* ... *) diff --git a/src/lib_client_base/client_keys.ml b/src/lib_client_base/client_keys.ml index 8728c888694bc97d8ccd07706946c4386376093f..4be72df4c2ef6106d21dae123420e345269f6015 100644 --- a/src/lib_client_base/client_keys.ml +++ b/src/lib_client_base/client_keys.ml @@ -1175,6 +1175,8 @@ module V0 = Make (struct Tezos_crypto.Signature.Public_key_hash.t -> Public_key_hash.t tzresult = let open Result_syntax in function + | Bls _ -> + tzfail (Exn (Failure "BLS public key hash not supported by V0")) | Ed25519 k -> return (Ed25519 k : Public_key_hash.t) | Secp256k1 k -> return (Secp256k1 k : Public_key_hash.t) | P256 k -> return (P256 k : Public_key_hash.t) @@ -1183,6 +1185,7 @@ module V0 = Make (struct Tezos_crypto.Signature.Public_key.t -> Public_key.t tzresult = let open Result_syntax in function + | Bls _ -> tzfail (Exn (Failure "BLS public key not supported by V0")) | Ed25519 k -> return (Ed25519 k : Public_key.t) | Secp256k1 k -> return (Secp256k1 k : Public_key.t) | P256 k -> return (P256 k : Public_key.t) @@ -1190,6 +1193,7 @@ module V0 = Make (struct let signature : Tezos_crypto.Signature.t -> t tzresult = let open Result_syntax in function + | Bls _ -> tzfail (Exn (Failure "BLS signature not supported by V0")) | Ed25519 k -> return (Ed25519 k : t) | Secp256k1 k -> return (Secp256k1 k : t) | P256 k -> return (P256 k : t) diff --git a/src/lib_client_commands/client_keys_commands.ml b/src/lib_client_commands/client_keys_commands.ml index 9b8c1eb899dabbe2df18b9e68899326e659caf77..653d7ebcc2b89931f607bcac2c2f3b020af00d8e 100644 --- a/src/lib_client_commands/client_keys_commands.ml +++ b/src/lib_client_commands/client_keys_commands.ml @@ -34,16 +34,17 @@ let group = let algo_param () = let open Lwt_result_syntax in Tezos_clic.parameter - ~autocomplete:(fun _ -> return ["ed25519"; "secp256k1"; "p256"]) + ~autocomplete:(fun _ -> return ["ed25519"; "secp256k1"; "p256"; "bls"]) (fun _ name -> match name with | "ed25519" -> return Tezos_crypto.Signature.Ed25519 | "secp256k1" -> return Tezos_crypto.Signature.Secp256k1 | "p256" -> return Tezos_crypto.Signature.P256 + | "bls" -> return Tezos_crypto.Signature.Bls | name -> failwith "Unknown signature algorithm (%s). Available: 'ed25519', \ - 'secp256k1' or 'p256'" + 'secp256k1','p256' or 'bls'" name) let sig_algo_arg = @@ -51,7 +52,7 @@ let sig_algo_arg = ~doc:"use custom signature algorithm" ~long:"sig" ~short:'s' - ~placeholder:"ed25519|secp256k1|p256" + ~placeholder:"ed25519|secp256k1|p256|bls" ~default:"ed25519" (algo_param ()) diff --git a/src/lib_crypto/aggregate_signature.mli b/src/lib_crypto/aggregate_signature.mli index 0196e5035c64e48c2ef9655fec623b0e7c035857..cd1baeef5d5f2afa114893fcfe14e4ca1e289c75 100644 --- a/src/lib_crypto/aggregate_signature.mli +++ b/src/lib_crypto/aggregate_signature.mli @@ -22,6 +22,11 @@ (* DEALINGS IN THE SOFTWARE. *) (* *) (*****************************************************************************) + +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3811 + Remove this module and use `Signature` module instead. +*) + type public_key_hash = Bls12_381 of Bls.Public_key_hash.t type public_key = Bls12_381 of Bls.Public_key.t diff --git a/src/lib_crypto/s.ml b/src/lib_crypto/s.ml index 36b80744f0fe6b946fe71d4d902e5ef1d702eb8c..8b1ee571d632153d72b41d8a29cd06c58a0fe5e2 100644 --- a/src/lib_crypto/s.ml +++ b/src/lib_crypto/s.ml @@ -452,6 +452,29 @@ module type AGGREGATE_SIGNATURE = sig val aggregate_signature_opt : t list -> t option end +module type SPLIT_SIGNATURE = sig + include SIGNATURE + + (** A signature prefix potentially carries data. *) + type prefix + + (** A splitted signature is a binary representation of a signature with a + fixed 64 bytes suffix and a possible prefix. *) + type splitted = {prefix : prefix option; suffix : Bytes.t} + + (** [split_signature s] splits the signature [s] into [{prefix; suffix}] where + suffix is the fixed 64 bytes suffix of [s] and prefix are the remaining + preceding bytes if any. *) + val split_signature : t -> splitted + + (** [of_splitted s] reconstructs a signature from a splitted one, if + possible. *) + val of_splitted : splitted -> t option + + (** Encoding for signature prefixes. *) + val prefix_encoding : prefix Data_encoding.t +end + module type FIELD = sig exception Not_in_field of Bytes.t diff --git a/src/lib_crypto/signature.ml b/src/lib_crypto/signature.ml index 3833bf5c21ca5a0bc924ce87423d4fffc45ec4a8..03b05a8c4663c08cf7d59ed6dd78d6a92541d93c 100644 --- a/src/lib_crypto/signature.ml +++ b/src/lib_crypto/signature.ml @@ -67,22 +67,26 @@ module V0 = struct | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) + | V_latest.Bls _ -> None let public_key : V_latest.Public_key.t -> Public_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) + | V_latest.Bls _ -> None let secret_key : V_latest.Secret_key.t -> Secret_key.t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) + | V_latest.Bls _ -> None let signature : V_latest.t -> t option = function | V_latest.Ed25519 k -> Some (Ed25519 k) | V_latest.Secp256k1 k -> Some (Secp256k1 k) | V_latest.P256 k -> Some (P256 k) | V_latest.Unknown k -> Some (Unknown k) + | V_latest.Bls _ -> None end end diff --git a/src/lib_crypto/signature.mli b/src/lib_crypto/signature.mli index 555b24672859e5a7e7aad6475e9d456c532026ff..01a26a7546c25e5a03f2f9116cdf087077a7b49a 100644 --- a/src/lib_crypto/signature.mli +++ b/src/lib_crypto/signature.mli @@ -72,8 +72,7 @@ module V0 : sig CONV_OPT with module V_from := V_latest and module V_to := Signature_v0 end -(** [V1] supports Ed25519, Secp256k1, P256. It is a copy of {!V0} without type - equalities. *) +(** [V1] supports Ed25519, Secp256k1, P256, and BLS. *) module V1 : sig include module type of Signature_v1 diff --git a/src/lib_crypto/signature_v1.ml b/src/lib_crypto/signature_v1.ml index 91184800cc9adad27d9cdf5168ad6d35e59d2eff..6f17d6c602023f0081e0767a8fd9fdad6e77aded 100644 --- a/src/lib_crypto/signature_v1.ml +++ b/src/lib_crypto/signature_v1.ml @@ -31,16 +31,19 @@ type public_key_hash = | Ed25519 of Ed25519.Public_key_hash.t | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t + | Bls of Bls.Public_key_hash.t type public_key = | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t + | Bls of Bls.Public_key.t type secret_key = | Ed25519 of Ed25519.Secret_key.t | Secp256k1 of Secp256k1.Secret_key.t | P256 of P256.Secret_key.t + | Bls of Bls.Secret_key.t type watermark = Signature_v0.watermark = | Block_header of Chain_id.t @@ -53,10 +56,11 @@ module Public_key_hash = struct | Ed25519 of Ed25519.Public_key_hash.t | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t + | Bls of Bls.Public_key_hash.t let name = "Signature.Public_key_hash" - let title = "A Ed25519, Secp256k1, or P256 public key hash" + let title = "A Ed25519, Secp256k1, P256, or BLS public key hash" type Base58.data += Data of t (* unused *) @@ -92,6 +96,12 @@ module Public_key_hash = struct P256.Public_key_hash.encoding (function P256 x -> Some x | _ -> None) (function x -> P256 x); + case + (Tag 3) + ~title:"Bls" + Bls.Public_key_hash.encoding + (function Bls x -> Some x | _ -> None) + (function x -> Bls x); ] let to_bytes s = Data_encoding.Binary.to_bytes_exn raw_encoding s @@ -123,6 +133,7 @@ module Public_key_hash = struct | Some (Ed25519.Public_key_hash.Data pkh) -> Some (Ed25519 pkh) | Some (Secp256k1.Public_key_hash.Data pkh) -> Some (Secp256k1 pkh) | Some (P256.Public_key_hash.Data pkh) -> Some (P256 pkh) + | Some (Bls.Public_key_hash.Data pkh) -> Some (Bls pkh) | _ -> None let of_b58check_exn s = @@ -140,17 +151,20 @@ module Public_key_hash = struct | Ed25519 pkh -> Ed25519.Public_key_hash.to_b58check pkh | Secp256k1 pkh -> Secp256k1.Public_key_hash.to_b58check pkh | P256 pkh -> P256.Public_key_hash.to_b58check pkh + | Bls pkh -> Bls.Public_key_hash.to_b58check pkh let to_short_b58check = function | Ed25519 pkh -> Ed25519.Public_key_hash.to_short_b58check pkh | Secp256k1 pkh -> Secp256k1.Public_key_hash.to_short_b58check pkh | P256 pkh -> P256.Public_key_hash.to_short_b58check pkh + | Bls pkh -> Bls.Public_key_hash.to_short_b58check pkh let to_path key l = match key with | Ed25519 h -> "ed25519" :: Ed25519.Public_key_hash.to_path h l | Secp256k1 h -> "secp256k1" :: Secp256k1.Public_key_hash.to_path h l | P256 h -> "p256" :: P256.Public_key_hash.to_path h l + | Bls h -> "bls" :: Bls.Public_key_hash.to_path h l let of_path = function | "ed25519" :: q -> ( @@ -165,6 +179,10 @@ module Public_key_hash = struct match P256.Public_key_hash.of_path q with | Some pkh -> Some (P256 pkh) | None -> None) + | "bls" :: q -> ( + match Bls.Public_key_hash.of_path q with + | Some pkh -> Some (Bls pkh) + | None -> None) | _ -> assert false (* FIXME classification des erreurs *) @@ -173,6 +191,7 @@ module Public_key_hash = struct | "ed25519" :: q -> Ed25519 (Ed25519.Public_key_hash.of_path_exn q) | "secp256k1" :: q -> Secp256k1 (Secp256k1.Public_key_hash.of_path_exn q) | "p256" :: q -> P256 (P256.Public_key_hash.of_path_exn q) + | "bls" :: q -> Bls (Bls.Public_key_hash.of_path_exn q) | _ -> assert false (* FIXME classification des erreurs *) @@ -180,9 +199,11 @@ module Public_key_hash = struct let path_length = let l1 = Ed25519.Public_key_hash.path_length and l2 = Secp256k1.Public_key_hash.path_length - and l3 = P256.Public_key_hash.path_length in + and l3 = P256.Public_key_hash.path_length + and l4 = Bls.Public_key_hash.path_length in assert (Compare.Int.(l1 = l2)) ; assert (Compare.Int.(l1 = l3)) ; + assert (Compare.Int.(l1 = l4)) ; 1 + l1 let prefix_path _ = assert false (* unused *) @@ -199,6 +220,7 @@ module Public_key_hash = struct | Ed25519 x, Ed25519 y -> Ed25519.Public_key_hash.compare x y | Secp256k1 x, Secp256k1 y -> Secp256k1.Public_key_hash.compare x y | P256 x, P256 y -> P256.Public_key_hash.compare x y + | Bls x, Bls y -> Bls.Public_key_hash.compare x y | _ -> Stdlib.compare a b end) @@ -252,6 +274,7 @@ module Public_key = struct | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t + | Bls of Bls.Public_key.t let name = "Signature.Public_key" @@ -262,6 +285,7 @@ module Public_key = struct | Ed25519 pk -> Public_key_hash.Ed25519 (Ed25519.Public_key.hash pk) | Secp256k1 pk -> Public_key_hash.Secp256k1 (Secp256k1.Public_key.hash pk) | P256 pk -> Public_key_hash.P256 (P256.Public_key.hash pk) + | Bls pk -> Public_key_hash.Bls (Bls.Public_key.hash pk) include Compare.Make (struct type nonrec t = t @@ -271,8 +295,11 @@ module Public_key = struct | Ed25519 x, Ed25519 y -> Ed25519.Public_key.compare x y | Secp256k1 x, Secp256k1 y -> Secp256k1.Public_key.compare x y | P256 x, P256 y -> P256.Public_key.compare x y - | Ed25519 _, (Secp256k1 _ | P256 _) -> -1 - | Secp256k1 _, P256 _ -> -1 + | Bls x, Bls y -> Bls.Public_key.compare x y + | Ed25519 _, (Secp256k1 _ | P256 _ | Bls _) -> -1 + | Secp256k1 _, (P256 _ | Bls _) -> -1 + | P256 _, Bls _ -> -1 + | Bls _, (P256 _ | Secp256k1 _ | Ed25519 _) -> 1 | P256 _, (Secp256k1 _ | Ed25519 _) -> 1 | Secp256k1 _, Ed25519 _ -> 1 end) @@ -293,6 +320,7 @@ module Public_key = struct | Some (Ed25519.Public_key.Data public_key) -> Some (Ed25519 public_key) | Some (Secp256k1.Public_key.Data public_key) -> Some (Secp256k1 public_key) | Some (P256.Public_key.Data public_key) -> Some (P256 public_key) + | Some (Bls.Public_key.Data public_key) -> Some (Bls public_key) | _ -> None let of_b58check_exn s = @@ -310,11 +338,13 @@ module Public_key = struct | Ed25519 pk -> Ed25519.Public_key.to_b58check pk | Secp256k1 pk -> Secp256k1.Public_key.to_b58check pk | P256 pk -> P256.Public_key.to_b58check pk + | Bls pk -> Bls.Public_key.to_b58check pk let to_short_b58check = function | Ed25519 pk -> Ed25519.Public_key.to_short_b58check pk | Secp256k1 pk -> Secp256k1.Public_key.to_short_b58check pk | P256 pk -> P256.Public_key.to_short_b58check pk + | Bls pk -> Bls.Public_key.to_short_b58check pk let of_bytes_without_validation b = let tag = Bytes.(get_int8 b 0) in @@ -331,6 +361,9 @@ module Public_key = struct | 2 -> Option.bind (P256.Public_key.of_bytes_without_validation b) (fun pk -> Some (P256 pk)) + | 3 -> + Option.bind (Bls.Public_key.of_bytes_without_validation b) (fun pk -> + Some (Bls pk)) | _ -> None include Helpers.MakeEncoder (struct @@ -363,6 +396,12 @@ module Public_key = struct P256.Public_key.encoding (function P256 x -> Some x | _ -> None) (function x -> P256 x); + case + ~title:"Bls" + (Tag 3) + Bls.Public_key.encoding + (function Bls x -> Some x | _ -> None) + (function x -> Bls x); ] let of_b58check = of_b58check @@ -386,6 +425,7 @@ module Secret_key = struct | Ed25519 of Ed25519.Secret_key.t | Secp256k1 of Secp256k1.Secret_key.t | P256 of P256.Secret_key.t + | Bls of Bls.Secret_key.t let name = "Signature.Secret_key" @@ -396,6 +436,7 @@ module Secret_key = struct | Secp256k1 sk -> Public_key.Secp256k1 (Secp256k1.Secret_key.to_public_key sk) | P256 sk -> Public_key.P256 (P256.Secret_key.to_public_key sk) + | Bls sk -> Public_key.Bls (Bls.Secret_key.to_public_key sk) include Compare.Make (struct type nonrec t = t @@ -405,6 +446,7 @@ module Secret_key = struct | Ed25519 x, Ed25519 y -> Ed25519.Secret_key.compare x y | Secp256k1 x, Secp256k1 y -> Secp256k1.Secret_key.compare x y | P256 x, P256 y -> P256.Secret_key.compare x y + | Bls x, Bls y -> Bls.Secret_key.compare x y | _ -> Stdlib.compare a b end) @@ -424,6 +466,7 @@ module Secret_key = struct | Some (Ed25519.Secret_key.Data sk) -> Some (Ed25519 sk) | Some (Secp256k1.Secret_key.Data sk) -> Some (Secp256k1 sk) | Some (P256.Secret_key.Data sk) -> Some (P256 sk) + | Some (Bls.Secret_key.Data sk) -> Some (Bls sk) | _ -> None let of_b58check_exn s = @@ -441,11 +484,13 @@ module Secret_key = struct | Ed25519 sk -> Ed25519.Secret_key.to_b58check sk | Secp256k1 sk -> Secp256k1.Secret_key.to_b58check sk | P256 sk -> P256.Secret_key.to_b58check sk + | Bls sk -> Bls.Secret_key.to_b58check sk let to_short_b58check = function | Ed25519 sk -> Ed25519.Secret_key.to_short_b58check sk | Secp256k1 sk -> Secp256k1.Secret_key.to_short_b58check sk | P256 sk -> P256.Secret_key.to_short_b58check sk + | Bls sk -> Bls.Secret_key.to_short_b58check sk include Helpers.MakeEncoder (struct type nonrec t = t @@ -477,6 +522,12 @@ module Secret_key = struct P256.Secret_key.encoding (function P256 x -> Some x | _ -> None) (function x -> P256 x); + case + (Tag 3) + ~title:"Bls" + Bls.Secret_key.encoding + (function Bls x -> Some x | _ -> None) + (function x -> Bls x); ] let of_b58check = of_b58check @@ -497,53 +548,60 @@ type signature = | Ed25519 of Ed25519.t | Secp256k1 of Secp256k1.t | P256 of P256.t + | Bls of Bls.t | Unknown of Bytes.t +type prefix = Bls_prefix of Bytes.t + +type splitted = {prefix : prefix option; suffix : Bytes.t} + type t = signature let name = "Signature.V1" -let title = "A Ed25519, Secp256k1 or P256 signature" - -let size = - assert (Ed25519.size = Secp256k1.size && Secp256k1.size = P256.size) ; - Ed25519.size +let title = "A Ed25519, Secp256k1, P256 or BLS signature" let to_bytes = function | Ed25519 b -> Ed25519.to_bytes b | Secp256k1 b -> Secp256k1.to_bytes b | P256 b -> P256.to_bytes b + | Bls b -> Bls.to_bytes b | Unknown b -> b -let of_bytes_opt s = if Bytes.length s = size then Some (Unknown s) else None +let of_bytes_opt s = + let len = Bytes.length s in + if len = Bls.size then Option.map (fun b -> Bls b) (Bls.of_bytes_opt s) + else if len = Ed25519.size then Some (Unknown s) + else None -let to_string s = Bytes.to_string (to_bytes s) - -let of_string_opt s = of_bytes_opt (Bytes.of_string s) +let () = + assert (Ed25519.size = 64) ; + assert (Secp256k1.size = 64) ; + assert (P256.size = 64) ; + assert (Bls.size = 96) -type Base58.data += Data of t +type Base58.data += Data_unknown of Bytes.t -let b58check_encoding = +let unknown_b58check_encoding = Base58.register_encoding ~prefix:Base58.Prefix.generic_signature ~length:Ed25519.size - ~to_raw:to_string - ~of_raw:of_string_opt - ~wrap:(fun x -> Data x) + ~to_raw:Bytes.to_string + ~of_raw:(fun s -> Some (Bytes.of_string s)) + ~wrap:(fun x -> Data_unknown x) -let () = Base58.check_encoded_prefix b58check_encoding "sig" 96 +let () = Base58.check_encoded_prefix unknown_b58check_encoding "sig" 96 -include Helpers.MakeRaw (struct - type nonrec t = t - - let name = name - - let of_bytes_opt = of_bytes_opt - - let of_string_opt = of_string_opt +type Base58.data += Data of t (* unused *) - let to_string = to_string -end) +let b58check_encoding = + (* unused *) + Base58.register_encoding + ~prefix:"\255\255" + ~length:2 + ~to_raw:(fun _ -> assert false) + ~of_raw:(fun _ -> assert false) + ~wrap:(fun x -> Data x) include Compare.Make (struct type nonrec t = t @@ -561,7 +619,12 @@ let of_b58check_opt s = then Option.map (fun x -> Secp256k1 x) (Secp256k1.of_b58check_opt s) else if TzString.has_prefix ~prefix:P256.b58check_encoding.encoded_prefix s then Option.map (fun x -> P256 x) (P256.of_b58check_opt s) - else Base58.simple_decode b58check_encoding s + else if TzString.has_prefix ~prefix:Bls.b58check_encoding.encoded_prefix s + then Option.map (fun x -> Bls x) (Bls.of_b58check_opt s) + else + Option.map + (fun x -> Unknown x) + (Base58.simple_decode unknown_b58check_encoding s) let of_b58check_exn s = match of_b58check_opt s with @@ -577,13 +640,29 @@ let to_b58check = function | Ed25519 b -> Ed25519.to_b58check b | Secp256k1 b -> Secp256k1.to_b58check b | P256 b -> P256.to_b58check b - | Unknown b -> Base58.simple_encode b58check_encoding (Unknown b) + | Bls b -> Bls.to_b58check b + | Unknown b -> Base58.simple_encode unknown_b58check_encoding b let to_short_b58check = function | Ed25519 b -> Ed25519.to_short_b58check b | Secp256k1 b -> Secp256k1.to_short_b58check b | P256 b -> P256.to_short_b58check b - | Unknown b -> Base58.simple_encode b58check_encoding (Unknown b) + | Bls b -> Bls.to_short_b58check b + | Unknown b -> Base58.simple_encode unknown_b58check_encoding b + +let raw_encoding = + conv + to_bytes + (fun b -> + match of_bytes_opt b with + | None -> + Format.kasprintf + Stdlib.failwith + "Not a valid signature: %a" + Hex.pp + (Hex.of_bytes b) + | Some s -> s) + Variable.bytes include Helpers.MakeEncoder (struct type nonrec t = t @@ -592,8 +671,7 @@ include Helpers.MakeEncoder (struct let title = title - let raw_encoding = - Data_encoding.conv to_bytes of_bytes_exn (Data_encoding.Fixed.bytes size) + let raw_encoding = raw_encoding let of_b58check = of_b58check @@ -606,6 +684,28 @@ include Helpers.MakeEncoder (struct let to_short_b58check = to_short_b58check end) +let to_bytes s = Data_encoding.Binary.to_bytes_exn raw_encoding s + +let of_bytes_opt s = Data_encoding.Binary.of_bytes_opt raw_encoding s + +let to_string s = Bytes.to_string (to_bytes s) + +let of_string_opt s = of_bytes_opt (Bytes.of_string s) + +include Helpers.MakeRaw (struct + type nonrec t = t + + let name = name + + let of_bytes_opt = of_bytes_opt + + let of_string_opt = of_string_opt + + let to_string = to_string +end) + +let size t = Data_encoding.Binary.length encoding t + let pp ppf t = Format.fprintf ppf "%s" (to_b58check t) let of_ed25519 s = Ed25519 s @@ -614,8 +714,51 @@ let of_secp256k1 s = Secp256k1 s let of_p256 s = P256 s +let of_bls s = Bls s + let zero = of_ed25519 Ed25519.zero +(* NOTE: At the moment, only BLS signatures can be encoded with a tag. We impose + this restriction so that there is only one valid binary representation for a + same signature (modulo malleability). + + We reserve the tags 0, 1, 2 and 255 for tags of the other signatures if we + decide to unify signature representation one day.*) +let prefix_encoding = + let open Data_encoding in + def + "bls_signature_prefix" + ~description:"The prefix of a BLS signature, i.e. the first 32 bytes." + @@ union + [ + case + (Tag 3) + ~title:"Bls_prefix" + (Fixed.bytes (Bls.size - Ed25519.size)) + (function Bls_prefix x -> Some x) + (function x -> Bls_prefix x); + ] + +let split_signature = function + | (Ed25519 _ | Secp256k1 _ | P256 _) as s -> + {prefix = None; suffix = to_bytes s} + | Bls s -> + let s = Bls.to_bytes s in + let prefix = Bytes.sub s 0 32 in + let suffix = Bytes.sub s 32 64 in + {prefix = Some (Bls_prefix prefix); suffix} + | Unknown s -> + assert (Compare.Int.(Bytes.length s = 64)) ; + {prefix = None; suffix = s} + +let of_splitted {prefix; suffix} = + let open Option_syntax in + match prefix with + | None -> of_bytes_opt suffix + | Some (Bls_prefix prefix) -> + let+ s = Bls.of_bytes_opt (Bytes.cat prefix suffix) in + Bls s + let bytes_of_watermark = function | Block_header chain_id -> Bytes.cat (Bytes.of_string "\x01") (Chain_id.to_bytes chain_id) @@ -643,6 +786,7 @@ let sign ?watermark secret_key message = | Secret_key.Ed25519 sk -> of_ed25519 (Ed25519.sign ?watermark sk message) | Secp256k1 sk -> of_secp256k1 (Secp256k1.sign ?watermark sk message) | P256 sk -> of_p256 (P256.sign ?watermark sk message) + | Bls sk -> of_bls (Bls.sign ?watermark sk message) let check ?watermark public_key signature message = let watermark = Option.map bytes_of_watermark watermark in @@ -659,12 +803,18 @@ let check ?watermark public_key signature message = match P256.of_bytes_opt signature with | Some s -> P256.check ?watermark pk s message | None -> false) + | Public_key.Bls pk, Unknown signature -> ( + match Bls.of_bytes_opt signature with + | Some s -> Bls.check ?watermark pk s message + | None -> false) | Public_key.Ed25519 pk, Ed25519 signature -> Ed25519.check ?watermark pk signature message | Public_key.Secp256k1 pk, Secp256k1 signature -> Secp256k1.check ?watermark pk signature message | Public_key.P256 pk, P256 signature -> P256.check ?watermark pk signature message + | Public_key.Bls pk, Bls signature -> + Bls.check ?watermark pk signature message | _ -> false (* The following cache is a hack to work around a quadratic algorithm @@ -713,9 +863,9 @@ let append ?watermark sk msg = Bytes.cat msg (to_bytes (sign ?watermark sk msg)) let concat msg signature = Bytes.cat msg (to_bytes signature) -type algo = Ed25519 | Secp256k1 | P256 +type algo = Ed25519 | Secp256k1 | P256 | Bls -let algos = [Ed25519; Secp256k1; P256] +let algos = [Ed25519; Secp256k1; P256; Bls] let generate_key ?(algo = Ed25519) ?seed () = match algo with @@ -730,18 +880,23 @@ let generate_key ?(algo = Ed25519) ?seed () = | P256 -> let pkh, pk, sk = P256.generate_key ?seed () in (Public_key_hash.P256 pkh, Public_key.P256 pk, Secret_key.P256 sk) + | Bls -> + let pkh, pk, sk = Bls.generate_key ?seed () in + (Public_key_hash.Bls pkh, Public_key.Bls pk, Secret_key.Bls sk) let deterministic_nonce sk msg = match sk with | Secret_key.Ed25519 sk -> Ed25519.deterministic_nonce sk msg | Secret_key.Secp256k1 sk -> Secp256k1.deterministic_nonce sk msg | Secret_key.P256 sk -> P256.deterministic_nonce sk msg + | Secret_key.Bls sk -> Bls.deterministic_nonce sk msg let deterministic_nonce_hash sk msg = match sk with | Secret_key.Ed25519 sk -> Ed25519.deterministic_nonce_hash sk msg | Secret_key.Secp256k1 sk -> Secp256k1.deterministic_nonce_hash sk msg | Secret_key.P256 sk -> P256.deterministic_nonce_hash sk msg + | Secret_key.Bls sk -> Bls.deterministic_nonce_hash sk msg module Of_V0 = struct let public_key_hash : Signature_v0.Public_key_hash.t -> Public_key_hash.t = diff --git a/src/lib_crypto/signature_v1.mli b/src/lib_crypto/signature_v1.mli index dd16298c50508bc06843c882b6ad3b78e826b71f..22f1856bc0bc57cd3d54a4063477d5c2247f0d47 100644 --- a/src/lib_crypto/signature_v1.mli +++ b/src/lib_crypto/signature_v1.mli @@ -28,16 +28,19 @@ type public_key_hash = | Ed25519 of Ed25519.Public_key_hash.t | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t + | Bls of Bls.Public_key_hash.t type public_key = | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t + | Bls of Bls.Public_key.t type secret_key = | Ed25519 of Ed25519.Secret_key.t | Secp256k1 of Secp256k1.Secret_key.t | P256 of P256.Secret_key.t + | Bls of Bls.Secret_key.t type watermark = Signature_v0.watermark = | Block_header of Chain_id.t @@ -53,14 +56,20 @@ type signature = | Ed25519 of Ed25519.t | Secp256k1 of Secp256k1.t | P256 of P256.t + | Bls of Bls.t | Unknown of Bytes.t +(** A signature prefix holds data only for signature that are more than 64 bytes + long. *) +type prefix = Bls_prefix of Bytes.t + include - S.SIGNATURE + S.SPLIT_SIGNATURE with type Public_key_hash.t = public_key_hash and type Public_key.t = public_key and type Secret_key.t = secret_key and type watermark := watermark + and type prefix := prefix and type t = signature (** [append sk buf] is the concatenation of [buf] and the @@ -73,18 +82,33 @@ val concat : Bytes.t -> t -> Bytes.t include S.RAW_DATA with type t := t +(** The size of the signature in bytes. Can be [64] for Ed25519, Secp256k1 and + P256 signatures or [96] for BLS signatures. *) +val size : t -> int + +(** [of_secp256k1 s] returns a wrapped version of the Secp256k1 signature [s] in + {!t}. *) val of_secp256k1 : Secp256k1.t -> t +(** [of_ed25519 s] returns a wrapped version of the Ed25519 signature [s] in + {!t}. *) val of_ed25519 : Ed25519.t -> t +(** [of_p256 s] returns a wrapped version of the P256 signature [s] in {!t}. *) val of_p256 : P256.t -> t -type algo = Ed25519 | Secp256k1 | P256 +(** [of_bls s] returns a wrapped version of the BLS signature [s] in {!t}. *) +val of_bls : Bls.t -> t + +(** The type of signing algorithms. *) +type algo = Ed25519 | Secp256k1 | P256 | Bls (** The list of signing algorithm supported, i.e. all constructors of type {!algo}. *) val algos : algo list +(** [generate_key ~algo ~seed ()] generates a key pair for the signing algorithm + [algo] from the random seed [seed]. *) val generate_key : ?algo:algo -> ?seed:Bytes.t -> diff --git a/src/lib_crypto/test/test_prop_signature.ml b/src/lib_crypto/test/test_prop_signature.ml index dde66c8d81d164a0f941e1594c8ec8d51bb53165..dc0f99c143322f9de67af1c72ccfde6db4e1a73e 100644 --- a/src/lib_crypto/test/test_prop_signature.ml +++ b/src/lib_crypto/test/test_prop_signature.ml @@ -151,7 +151,7 @@ end (** Test: instantiate Signature_Properties over Signature with algo in generate key respectively set to - Ed25519, Secp256k1, P256. *) + Ed25519, Secp256k1, P256, Bls. *) let () = let module Bls_Props = Aggregate_Signature_Properties @@ -183,5 +183,12 @@ let () = in [("bls12_381", qcheck_wrap Bls_Props.tests)] - @ List.map f [(Ed25519, "Ed25519"); (Secp256k1, "Secp256k1"); (P256, "P256")] + @ List.map + f + [ + (Ed25519, "Ed25519"); + (Secp256k1, "Secp256k1"); + (P256, "P256"); + (Bls, "Bls"); + ] |> Alcotest.run "tezos-crypto-prop-signature" diff --git a/src/lib_crypto/test/test_signature.ml b/src/lib_crypto/test/test_signature.ml index 0928a77296bdab7639da2de75c592628eb9df300..934a0bb3716d3b5a6d5ba467fc1c45d6d932f151 100644 --- a/src/lib_crypto/test/test_signature.ml +++ b/src/lib_crypto/test/test_signature.ml @@ -57,6 +57,17 @@ let test_size () = @@ Data_encoding.Binary.fixed_length Secp256k1.Public_key.encoding) + 1 in + assert (Compare.Int.(expected = length)) ; + let length = + let _pkh, pk, _sk = generate_key ~algo:Bls () in + Public_key.size pk + in + let expected = + (* add 1 for the tag of union encoding *) + (WithExceptions.Option.get ~loc:__LOC__ + @@ Data_encoding.Binary.fixed_length Bls.Public_key.encoding) + + 1 + in assert (Compare.Int.(expected = length)) let test_of_bytes_without_validation () = @@ -68,7 +79,7 @@ let test_of_bytes_without_validation () = in let pk2 = Signature.Public_key.of_bytes_without_validation bytes in assert (Some pk = pk2)) - [Ed25519; Secp256k1; P256] + [Ed25519; Secp256k1; P256; Bls] let tests = [ diff --git a/src/lib_protocol_environment/environment_V8.ml b/src/lib_protocol_environment/environment_V8.ml index f7be6c3705b02cf1777b4eafa568238b2a61e9b6..5e2e0b5e40ad4476e3191e2057fb8178e614fd62 100644 --- a/src/lib_protocol_environment/environment_V8.ml +++ b/src/lib_protocol_environment/environment_V8.ml @@ -80,6 +80,7 @@ module type T = sig and type Signature.public_key_hash = Tezos_crypto.Signature.V1.public_key_hash and type Signature.public_key = Tezos_crypto.Signature.V1.public_key + and type Signature.signature = Tezos_crypto.Signature.V1.signature and type Signature.t = Tezos_crypto.Signature.V1.t and type Signature.watermark = Tezos_crypto.Signature.V1.watermark and type Micheline.canonical_location = Micheline.canonical_location @@ -274,6 +275,26 @@ struct let def name ?title ?description encoding = def (Param.name ^ "." ^ name) ?title ?description encoding + + (* TODO: https://gitlab.com/nomadic-labs/data-encoding/-/issues/58 + Remove when fix is integrated in data-encoding. *) + let splitted ~json ~binary = + let open Data_encoding__.Encoding in + let e = splitted ~json ~binary in + { + e with + encoding = + (match e.encoding with + | Splitted {encoding; json_encoding; _} -> + Splitted + { + encoding; + json_encoding; + is_obj = is_obj json && is_obj binary; + is_tup = is_tup json && is_tup binary; + } + | desc -> desc); + } end module Time = Time.Protocol @@ -455,6 +476,20 @@ struct val aggregate_signature_opt : t list -> t option end + module type SPLIT_SIGNATURE = sig + include SIGNATURE + + type prefix + + type splitted = {prefix : prefix option; suffix : Bytes.t} + + val split_signature : t -> splitted + + val of_splitted : splitted -> t option + + val prefix_encoding : prefix Data_encoding.t + end + module type FIELD = sig type t diff --git a/src/lib_protocol_environment/environment_V8.mli b/src/lib_protocol_environment/environment_V8.mli index c3c15f69fd7dc344a00869d9ef88ba3e72c44dd3..2408ab441d26dd9c83c8a07b3953bea677b77475 100644 --- a/src/lib_protocol_environment/environment_V8.mli +++ b/src/lib_protocol_environment/environment_V8.mli @@ -80,6 +80,7 @@ module type T = sig and type Signature.public_key_hash = Tezos_crypto.Signature.V1.public_key_hash and type Signature.public_key = Tezos_crypto.Signature.V1.public_key + and type Signature.signature = Tezos_crypto.Signature.V1.signature and type Signature.t = Tezos_crypto.Signature.V1.t and type Signature.watermark = Tezos_crypto.Signature.V1.watermark and type Micheline.canonical_location = Micheline.canonical_location diff --git a/src/lib_protocol_environment/sigs/v8.ml b/src/lib_protocol_environment/sigs/v8.ml index 3c6ecc5f8178139a9415b9d6bcc08c50088428b5..930ed0b8d25607234d1480fde06674b04983a2b2 100644 --- a/src/lib_protocol_environment/sigs/v8.ml +++ b/src/lib_protocol_environment/sigs/v8.ml @@ -9194,6 +9194,7 @@ end (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* Copyright (c) 2020 Metastate AG *) +(* Copyright (c) 2022 Nomadic Labs. *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -9433,6 +9434,20 @@ module type AGGREGATE_SIGNATURE = sig val aggregate_signature_opt : t list -> t option end +module type SPLIT_SIGNATURE = sig + include SIGNATURE + + type prefix + + type splitted = {prefix : prefix option; suffix : Bytes.t} + + val split_signature : t -> splitted + + val of_splitted : splitted -> t option + + val prefix_encoding : prefix Data_encoding.t +end + module type FIELD = sig type t @@ -9800,6 +9815,7 @@ end (* *) (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2022 Nomadic Labs. *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -9825,11 +9841,13 @@ type public_key_hash = | Ed25519 of Ed25519.Public_key_hash.t | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t + | Bls of Bls.Public_key_hash.t type public_key = | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t + | Bls of Bls.Public_key.t type watermark = | Block_header of Chain_id.t @@ -9837,11 +9855,24 @@ type watermark = | Generic_operation | Custom of bytes +type signature = + | Ed25519 of Ed25519.t + | Secp256k1 of Secp256k1.t + | P256 of P256.t + | Bls of Bls.t + | Unknown of Bytes.t + +type prefix = Bls_prefix of Bytes.t + include - S.SIGNATURE + S.SPLIT_SIGNATURE with type Public_key_hash.t = public_key_hash and type Public_key.t = public_key and type watermark := watermark + and type prefix := prefix + and type t = signature + +val size : t -> int end # 96 "v8.in.ml" diff --git a/src/lib_protocol_environment/sigs/v8/s.mli b/src/lib_protocol_environment/sigs/v8/s.mli index d00c6d4bdcfd9927cde172359f119b5db888875b..e350f6adc6e175dbe825d0872bcb338a539c0181 100644 --- a/src/lib_protocol_environment/sigs/v8/s.mli +++ b/src/lib_protocol_environment/sigs/v8/s.mli @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* Copyright (c) 2020 Metastate AG *) +(* Copyright (c) 2022 Nomadic Labs. *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -242,6 +243,20 @@ module type AGGREGATE_SIGNATURE = sig val aggregate_signature_opt : t list -> t option end +module type SPLIT_SIGNATURE = sig + include SIGNATURE + + type prefix + + type splitted = {prefix : prefix option; suffix : Bytes.t} + + val split_signature : t -> splitted + + val of_splitted : splitted -> t option + + val prefix_encoding : prefix Data_encoding.t +end + module type FIELD = sig type t diff --git a/src/lib_protocol_environment/sigs/v8/signature.mli b/src/lib_protocol_environment/sigs/v8/signature.mli index 1a1d295d60256c308962d6f9a6a6cfee24308d8f..67b9dd7aca160201b8c7a64c0e9d9bd849247c1e 100644 --- a/src/lib_protocol_environment/sigs/v8/signature.mli +++ b/src/lib_protocol_environment/sigs/v8/signature.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2022 Nomadic Labs. *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -27,11 +28,13 @@ type public_key_hash = | Ed25519 of Ed25519.Public_key_hash.t | Secp256k1 of Secp256k1.Public_key_hash.t | P256 of P256.Public_key_hash.t + | Bls of Bls.Public_key_hash.t type public_key = | Ed25519 of Ed25519.Public_key.t | Secp256k1 of Secp256k1.Public_key.t | P256 of P256.Public_key.t + | Bls of Bls.Public_key.t type watermark = | Block_header of Chain_id.t @@ -39,8 +42,21 @@ type watermark = | Generic_operation | Custom of bytes +type signature = + | Ed25519 of Ed25519.t + | Secp256k1 of Secp256k1.t + | P256 of P256.t + | Bls of Bls.t + | Unknown of Bytes.t + +type prefix = Bls_prefix of Bytes.t + include - S.SIGNATURE + S.SPLIT_SIGNATURE with type Public_key_hash.t = public_key_hash and type Public_key.t = public_key and type watermark := watermark + and type prefix := prefix + and type t = signature + +val size : t -> int diff --git a/src/lib_shell/injection_directory.ml b/src/lib_shell/injection_directory.ml index 4f8e936247776d776c19982b8f9d4c90009095e6..2b8e49f49de3e059157de87a79d48c763d1e3896 100644 --- a/src/lib_shell/injection_directory.ml +++ b/src/lib_shell/injection_directory.ml @@ -48,18 +48,17 @@ let inject_block validator ?force ?chain bytes operations = let inject_operation validator ~force ?chain bytes = let open Lwt_result_syntax in let*! chain_id = read_chain_id validator chain in - let t = - match Data_encoding.Binary.of_bytes_opt Operation.encoding bytes with - | None -> failwith "Can't parse the operation" - | Some op -> Validator.inject_operation validator ~force ?chain_id op - in - let hash = Tezos_crypto.Operation_hash.hash_bytes [bytes] in - Lwt.return (hash, t) + match Data_encoding.Binary.of_bytes_opt Operation.encoding bytes with + | None -> failwith "Can't parse the operation" + | Some op -> + let t = Validator.inject_operation validator ~force ?chain_id op in + let hash = Operation.hash op in + return (hash, t) let inject_operations validator ~force ?chain bytes_list = - let open Lwt_syntax in + let open Lwt_result_syntax in let* rev_hashes, rev_promises = - List.fold_left_s + List.fold_left_es (fun (hashes, promises) bytes -> let* hash, promise = inject_operation validator ~force ?chain bytes in return (hash :: hashes, promise :: promises)) @@ -131,14 +130,14 @@ let build_rpc_directory validator = dir := Tezos_rpc.Directory.register !dir s (fun () p q -> f p q) in let inject_operation ~force q contents = - let*! hash, wait = + let* hash, wait = inject_operation validator ~force ?chain:q#chain contents in let* () = if q#async then return_unit else wait in return hash in let inject_operations q contents = - let*! hashes, wait = + let* hashes, wait = inject_operations validator ~force:q#force ?chain:q#chain contents in let* () = if q#async then return_unit else wait in diff --git a/src/lib_shell_benchmarks/encoding_benchmarks.ml b/src/lib_shell_benchmarks/encoding_benchmarks.ml index 5e824f53047b11ca1b58d0f1bd9521d3dd2de150..4888a24d89e750394beb4cb0c2401d31a19c7a84 100644 --- a/src/lib_shell_benchmarks/encoding_benchmarks.ml +++ b/src/lib_shell_benchmarks/encoding_benchmarks.ml @@ -36,6 +36,7 @@ struct | Tezos_crypto.Signature.Ed25519 -> "ed25519" | Tezos_crypto.Signature.Secp256k1 -> "secp256k1" | Tezos_crypto.Signature.P256 -> "p256" + | Tezos_crypto.Signature.Bls -> "bls" module Sampler = Crypto_samplers.Make_finite_key_pool (struct let size = 256 @@ -208,6 +209,10 @@ module P256 = Make_elliptic_curve_encoding_benchmarks (struct let algo = Tezos_crypto.Signature.P256 end) +module Bls = Make_elliptic_curve_encoding_benchmarks (struct + let algo = Tezos_crypto.Signature.Bls +end) + let chain_id_encoding = make_encode_fixed_size ~name:"ENCODING_CHAIN_ID" diff --git a/src/lib_signer_backends/encrypted.ml b/src/lib_signer_backends/encrypted.ml index 700a7b187bc6b5a18ad963f366d565fba34ae7c8..efbcd0e7f6f0e652c431a94beb725f328417f879 100644 --- a/src/lib_signer_backends/encrypted.ml +++ b/src/lib_signer_backends/encrypted.ml @@ -81,7 +81,7 @@ module Raw = struct Data_encoding.Binary.to_bytes_exn Tezos_crypto.P256.Secret_key.encoding sk - | Decrypted_aggregate_sk (Bls12_381 sk) -> + | Decrypted_sk (Bls sk) | Decrypted_aggregate_sk (Bls12_381 sk) -> Data_encoding.Binary.to_bytes_exn Tezos_crypto.Bls.Secret_key.encoding sk @@ -139,7 +139,9 @@ module Raw = struct failwith "Corrupted wallet, deciphered key is not a valid \ Tezos_crypto.P256 secret key") - | Some bytes, Encrypted_aggregate_sk -> ( + | ( Some bytes, + (Encrypted_aggregate_sk | Encrypted_sk Tezos_crypto.Signature.Bls) ) + -> ( match Data_encoding.Binary.of_bytes_opt Tezos_crypto.Bls.Secret_key.encoding @@ -398,7 +400,8 @@ let common_encrypt sk password = | Decrypted_sk (Ed25519 _) -> Encodings.ed25519 | Decrypted_sk (Secp256k1 _) -> Encodings.secp256k1 | Decrypted_sk (P256 _) -> Encodings.p256 - | Decrypted_aggregate_sk (Bls12_381 _) -> Encodings.bls12_381 + | Decrypted_sk (Bls _) | Decrypted_aggregate_sk (Bls12_381 _) -> + Encodings.bls12_381 in Tezos_crypto.Base58.simple_encode encoding payload diff --git a/src/lib_test/qcheck2_helpers.ml b/src/lib_test/qcheck2_helpers.ml index 7622f54343e53fe288320ddc3ed49de131fa2d49..e0896e621f59fa4a0707f4c76c4bf7dd5aeea613 100644 --- a/src/lib_test/qcheck2_helpers.ml +++ b/src/lib_test/qcheck2_helpers.ml @@ -304,7 +304,17 @@ let test_roundtrip ~count ~title ~gen ~eq encoding = |> Data_encoding.Json.to_string |> Format.pp_print_string fmt in let test rdt input = - let output = Roundtrip.make encoding rdt input in + let output = + try Roundtrip.make encoding rdt input + with exn -> + QCheck2.Test.fail_reportf + "%s %s roundtrip error: error %s on %a" + title + (Roundtrip.target rdt) + (Printexc.to_string exn) + pp + input + in let success = eq input output in if not success then QCheck2.Test.fail_reportf diff --git a/src/proto_alpha/lib_benchmark/michelson_samplers_base.ml b/src/proto_alpha/lib_benchmark/michelson_samplers_base.ml index 3f8f301fd1ab7718fe34b741ce9d397679166dcb..c9a9d36e0612a490fd85b527b7b57a077acd082e 100644 --- a/src/proto_alpha/lib_benchmark/michelson_samplers_base.ml +++ b/src/proto_alpha/lib_benchmark/michelson_samplers_base.ml @@ -78,7 +78,7 @@ end) : S = struct Script_int.abs (Script_int.of_zint i) let signature rng_state = - let i = Random.State.int rng_state 4 in + let i = Random.State.int rng_state 5 in match i with | 0 -> ( let open Tezos_crypto.Ed25519 in @@ -98,10 +98,22 @@ end) : S = struct match of_bytes_opt bytes with | None -> assert false | Some s -> Tezos_crypto.Signature.of_p256 s) - | _ -> ( + | 3 -> + (* BLS checks that signatures are on the curve so we need to generate real + ones by signing a message. *) + let open Tezos_crypto.Bls in + let msg = Base_samplers.uniform_bytes ~nbytes:32 rng_state in + let seed = Base_samplers.uniform_bytes ~nbytes:32 rng_state in + let _, _, sk = generate_key ~seed () in + Tezos_crypto.Signature.of_bls (sign sk msg) + | _ -> let open Tezos_crypto.Signature in - let bytes = Base_samplers.uniform_bytes ~nbytes:size rng_state in - match of_bytes_opt bytes with None -> assert false | Some s -> s) + let bytes = + Base_samplers.uniform_bytes + ~nbytes:Tezos_crypto.Ed25519.size + rng_state + in + Unknown bytes let string rng_state = let s = diff --git a/src/proto_alpha/lib_benchmarks_proto/interpreter_benchmarks.ml b/src/proto_alpha/lib_benchmarks_proto/interpreter_benchmarks.ml index 5bf24e5a52cb2d63bce311e6ae59361e3bdb2f9a..e652792c927a638ce229259054df7ab8ab4cfe71 100644 --- a/src/proto_alpha/lib_benchmarks_proto/interpreter_benchmarks.ml +++ b/src/proto_alpha/lib_benchmarks_proto/interpreter_benchmarks.ml @@ -2406,6 +2406,8 @@ module Registration_section = struct Interpreter_workload.N_ICheck_signature_secp256k1 | Tezos_crypto.Signature.P256 -> Interpreter_workload.N_ICheck_signature_p256 + | Tezos_crypto.Signature.Bls -> + Interpreter_workload.N_ICheck_signature_bls in benchmark_with_stack_sampler ~intercept:for_intercept @@ -2439,6 +2441,8 @@ module Registration_section = struct let () = check_signature Tezos_crypto.Signature.P256 + let () = check_signature Tezos_crypto.Signature.Bls + let () = simple_benchmark ~name:Interpreter_workload.N_IHash_key diff --git a/src/proto_alpha/lib_benchmarks_proto/interpreter_model.ml b/src/proto_alpha/lib_benchmarks_proto/interpreter_model.ml index 2f7a35f7f8afa61d058e123f683ce5807bf2de4c..57cf14fbf5094947ddc6676ac5473050e07644b2 100644 --- a/src/proto_alpha/lib_benchmarks_proto/interpreter_model.ml +++ b/src/proto_alpha/lib_benchmarks_proto/interpreter_model.ml @@ -441,7 +441,7 @@ let ir_model ?specialization instr_or_cont = | N_IBlake2b | N_ISha256 | N_ISha512 | N_IKeccak | N_ISha3 -> model_1 instr_or_cont (affine_model name) | N_ICheck_signature_ed25519 | N_ICheck_signature_secp256k1 - | N_ICheck_signature_p256 -> + | N_ICheck_signature_p256 | N_ICheck_signature_bls -> model_1 instr_or_cont (affine_model name) | N_IContract | N_ITransfer_tokens | N_IImplicit_account -> model_0 instr_or_cont (const1_model name) diff --git a/src/proto_alpha/lib_benchmarks_proto/interpreter_workload.ml b/src/proto_alpha/lib_benchmarks_proto/interpreter_workload.ml index fe88e14d2fea357278d858c7594f9ca50142f302..e6d61fb593bb5da786e7c8cae3a5e5f8cb3f68f3 100644 --- a/src/proto_alpha/lib_benchmarks_proto/interpreter_workload.ml +++ b/src/proto_alpha/lib_benchmarks_proto/interpreter_workload.ml @@ -176,6 +176,7 @@ type instruction_name = | N_ICheck_signature_ed25519 | N_ICheck_signature_secp256k1 | N_ICheck_signature_p256 + | N_ICheck_signature_bls | N_IHash_key | N_IPack | N_IUnpack @@ -369,6 +370,7 @@ let string_of_instruction_name : instruction_name -> string = | N_ICheck_signature_ed25519 -> "N_ICheck_signature_ed25519" | N_ICheck_signature_secp256k1 -> "N_ICheck_signature_secp256k1" | N_ICheck_signature_p256 -> "N_ICheck_signature_p256" + | N_ICheck_signature_bls -> "N_ICheck_signature_bls" | N_IHash_key -> "N_IHash_key" | N_IPack -> "N_IPack" | N_IUnpack -> "N_IUnpack" @@ -590,6 +592,7 @@ let all_instructions = N_ICheck_signature_ed25519; N_ICheck_signature_secp256k1; N_ICheck_signature_p256; + N_ICheck_signature_bls; N_IHash_key; N_IPack; N_IUnpack; @@ -999,6 +1002,9 @@ module Instructions = struct let check_signature_p256 _pk _signature message = ir_sized_step N_ICheck_signature_p256 (unary "message" message) + let check_signature_bls _pk _signature message = + ir_sized_step N_ICheck_signature_bls (unary "message" message) + let hash_key = ir_sized_step N_IHash_key nullary let pack (micheline_size : Size.micheline_size) = @@ -1376,21 +1382,26 @@ let extract_ir_sized_step : | ILevel (_, _), _ -> Instructions.level | ICheck_signature (_, _), (public_key, (_signature, (message, _))) -> ( match public_key with - | Tezos_crypto.Signature.Ed25519 _pk -> - let pk = Size.of_int Tezos_crypto.Ed25519.size in - let signature = Size.of_int Tezos_crypto.Signature.size in + | Tezos_crypto.Signature.Ed25519 pk -> + let pk = Size.of_int (Tezos_crypto.Ed25519.Public_key.size pk) in + let signature = Size.of_int Tezos_crypto.Ed25519.size in let message = Size.bytes message in Instructions.check_signature_ed25519 pk signature message - | Tezos_crypto.Signature.Secp256k1 _pk -> - let pk = Size.of_int Tezos_crypto.Secp256k1.size in - let signature = Size.of_int Tezos_crypto.Signature.size in + | Tezos_crypto.Signature.Secp256k1 pk -> + let pk = Size.of_int (Tezos_crypto.Secp256k1.Public_key.size pk) in + let signature = Size.of_int Tezos_crypto.Secp256k1.size in let message = Size.bytes message in Instructions.check_signature_secp256k1 pk signature message - | Tezos_crypto.Signature.P256 _pk -> - let pk = Size.of_int Tezos_crypto.P256.size in - let signature = Size.of_int Tezos_crypto.Signature.size in + | Tezos_crypto.Signature.P256 pk -> + let pk = Size.of_int (Tezos_crypto.P256.Public_key.size pk) in + let signature = Size.of_int Tezos_crypto.P256.size in + let message = Size.bytes message in + Instructions.check_signature_p256 pk signature message + | Tezos_crypto.Signature.Bls pk -> + let pk = Size.of_int (Tezos_crypto.Bls.Public_key.size pk) in + let signature = Size.of_int Tezos_crypto.Bls.size in let message = Size.bytes message in - Instructions.check_signature_p256 pk signature message) + Instructions.check_signature_bls pk signature message) | IHash_key (_, _), _ -> Instructions.hash_key | IPack (_, ty, _), (v, _) -> ( let script_res = diff --git a/src/proto_alpha/lib_client/injection.ml b/src/proto_alpha/lib_client/injection.ml index 6c18cf9d9d47cde36fc9a09ab69f0bbdf9226674..307aea3af98e6a788ba912750d3742b2954701e2 100644 --- a/src/proto_alpha/lib_client/injection.ml +++ b/src/proto_alpha/lib_client/injection.ml @@ -259,7 +259,10 @@ let preapply (type t) (cctxt : #Protocol_client_context.full) ~chain ~block {shell = {branch}; protocol_data = {contents; signature}} in let oph = Operation.hash op in - let size = Bytes.length bytes + Tezos_crypto.Signature.size in + let packed_op = + {shell = {branch}; protocol_data = Operation_data {contents; signature}} + in + let size = Data_encoding.Binary.length Operation.encoding packed_op in (match fee_parameter with | Some fee_parameter -> check_fees cctxt fee_parameter contents size | None -> Lwt.return_unit) @@ -631,6 +634,15 @@ let detect_script_failure : type kind. kind operation_metadata -> _ = in fun {contents} -> detect_script_failure contents +let signature_size_of_algo : Tezos_crypto.Signature.algo -> int = function + | Ed25519 -> Tezos_crypto.Ed25519.size + | Secp256k1 -> Tezos_crypto.Secp256k1.size + | P256 -> Tezos_crypto.P256.size + | Bls -> + (* BLS signatures in operations are encoded with 2 extra bytes: a [ff] + prefix and a tag [03]. *) + Tezos_crypto.Bls.size + 2 + (* This value is used as a safety guard for gas limit. *) let safety_guard = Gas.Arith.(integral_of_int_exn 100) @@ -658,8 +670,8 @@ let safety_guard = Gas.Arith.(integral_of_int_exn 100) *) let may_patch_limits (type kind) (cctxt : #Protocol_client_context.full) - ~fee_parameter ~chain ~block ?successor_level ?branch ?(force = false) - ?(simulation = false) + ~fee_parameter ~signature_algo ~chain ~block ?successor_level ?branch + ?(force = false) ?(simulation = false) (annotated_contents : kind Annotated_manager_operation.annotated_list) : kind Kind.manager contents_list tzresult Lwt.t = Tezos_client_base.Client_confirmations.wait_for_bootstrapped cctxt @@ -793,7 +805,7 @@ let may_patch_limits (type kind) (cctxt : #Protocol_client_context.full) + Data_encoding.Binary.length Operation.contents_encoding (Contents op) - + Tezos_crypto.Signature.size + + signature_size_of_algo signature_algo else Data_encoding.Binary.length Operation.contents_encoding @@ -1369,9 +1381,9 @@ let may_replace_operation (type kind) (cctxt : #full) chain from Lwt.return_ok contents let inject_manager_operation cctxt ~chain ~block ?successor_level ?branch - ?confirmations ?dry_run ?verbose_signing ?simulation ?force ~source ~src_pk - ~src_sk ~fee ~gas_limit ~storage_limit ?counter ?(replace_by_fees = false) - ~fee_parameter (type kind) + ?confirmations ?dry_run ?verbose_signing ?simulation ?force ~source + ~(src_pk : public_key) ~src_sk ~fee ~gas_limit ~storage_limit ?counter + ?(replace_by_fees = false) ~fee_parameter (type kind) (operations : kind Annotated_manager_operation.annotated_list) : (Tezos_crypto.Operation_hash.t * packed_operation @@ -1397,6 +1409,13 @@ let inject_manager_operation cctxt ~chain ~block ?successor_level ?branch | Cons_manager (Manager_info {operation = Reveal _; _}, _) -> true | _ -> false in + let signature_algo = + match src_pk with + | Ed25519 _ -> Tezos_crypto.Signature.Ed25519 + | Secp256k1 _ -> Secp256k1 + | P256 _ -> P256 + | Bls _ -> Bls + in let apply_specified_options counter op = Annotated_manager_operation.set_source source op >>? fun op -> Annotated_manager_operation.set_counter counter op >>? fun op -> @@ -1438,6 +1457,7 @@ let inject_manager_operation cctxt ~chain ~block ?successor_level ?branch may_patch_limits cctxt ~fee_parameter + ~signature_algo ~chain ~block ?force @@ -1479,6 +1499,7 @@ let inject_manager_operation cctxt ~chain ~block ?successor_level ?branch may_patch_limits cctxt ~fee_parameter + ~signature_algo ~chain ~block ?force diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 647d189f236bb6c13181781add002704d0ffe6d2..64179fc18249892eb0f4d18f830036b4d82fd962 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -513,6 +513,8 @@ module Delegate = struct let prepare_stake_distribution = Stake_storage.prepare_stake_distribution + let check_not_tz4 = Contract_delegate_storage.check_not_tz4 + let delegated_contracts = Contract_delegate_storage.delegated_contracts let deactivated = Delegate_activation_storage.is_inactive diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 19791cd0d24c265eb121bc2c0d766aad9e59b256..3ce5eb1aee43a379864fea4e16bbc3fccf2eb7e0 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2623,6 +2623,8 @@ end {!Delegate_consensus_key}, {!Delegate_missed_endorsements_storage}, {!Delegate_slashed_deposits_storage}, {!Delegate_cycles}. *) module Delegate : sig + val check_not_tz4 : Signature.public_key_hash -> unit tzresult + val frozen_deposits_limit : context -> public_key_hash -> Tez.t option tzresult Lwt.t @@ -2718,6 +2720,8 @@ module Delegate : sig context -> public_key_hash -> Cycle.t tzresult Lwt.t module Consensus_key : sig + val check_not_tz4 : Signature.public_key -> unit tzresult + val active_pubkey : context -> public_key_hash -> Consensus_key.pk tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml index 75efd48243daa84662d3c0079af54998d5758763..2050e7bcacdc1cf8af716d2805b5a09580a62375 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml @@ -23,9 +23,32 @@ (* *) (*****************************************************************************) +type error += (* `Permanent *) Forbidden_tz4_delegate of Bls.Public_key_hash.t + +let () = + register_error_kind + `Branch + ~id:"delegate.forbidden_tz4" + ~title:"Forbidden delegate" + ~description:"Delegates are forbidden to be tz4 (BLS) accounts." + ~pp:(fun ppf implicit -> + Format.fprintf + ppf + "The delegate %a is forbidden as it is a BLS public key hash." + Bls.Public_key_hash.pp + implicit) + Data_encoding.(obj1 (req "delegate" Bls.Public_key_hash.encoding)) + (function Forbidden_tz4_delegate d -> Some d | _ -> None) + (fun d -> Forbidden_tz4_delegate d) + +let check_not_tz4 : Signature.Public_key_hash.t -> unit tzresult = function + | Bls tz4 -> error (Forbidden_tz4_delegate tz4) + | Ed25519 _ | Secp256k1 _ | P256 _ -> Ok () + let find = Storage.Contract.Delegate.find let init ctxt contract delegate = + check_not_tz4 delegate >>?= fun () -> Storage.Contract.Delegate.init ctxt contract delegate >>=? fun ctxt -> let delegate_contract = Contract_repr.Implicit delegate in Storage.Contract.Delegated.add (ctxt, delegate_contract) contract >|= ok @@ -43,6 +66,7 @@ let delete ctxt contract = Storage.Contract.Delegate.remove ctxt contract >|= ok let set ctxt contract delegate = + check_not_tz4 delegate >>?= fun () -> unlink ctxt contract >>=? fun ctxt -> Storage.Contract.Delegate.add ctxt contract delegate >>= fun ctxt -> let delegate_contract = Contract_repr.Implicit delegate in diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli index 189a03df8fdf1ea3d5f861ba8b749297952dac6e..59131fb09b0cb3f6f34f1807e53e58fc5d405493 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli @@ -27,6 +27,15 @@ responsible for maintaining the tables {!Storage.Contract.Delegate} and {!Storage.Contract.Delegated}. *) +type error += + | (* `Permanent *) + Forbidden_tz4_delegate of Bls.Public_key_hash.t + (** Delegates cannot be tz4 accounts (i.e. BLS public key hashes). This + error is returned when we try to register such a delegate. *) + +(** [check_not_tz4 pkh] checks that [pkh] is not a BLS address. *) +val check_not_tz4 : Signature.public_key_hash -> unit tzresult + (** [find ctxt contract] returns the delegate associated to [contract], or [None] if [contract] has no delegate. *) val find : diff --git a/src/proto_alpha/lib_protocol/contract_repr.ml b/src/proto_alpha/lib_protocol/contract_repr.ml index 13b3c490230f859be44e2e8c3e264653782c1579..43624de9dc71d68f45cba9d6de69533846e025f0 100644 --- a/src/proto_alpha/lib_protocol/contract_repr.ml +++ b/src/proto_alpha/lib_protocol/contract_repr.ml @@ -56,6 +56,7 @@ let implicit_of_b58data : Base58.data -> Signature.public_key_hash option = | Ed25519.Public_key_hash.Data h -> Some (Signature.Ed25519 h) | Secp256k1.Public_key_hash.Data h -> Some (Signature.Secp256k1 h) | P256.Public_key_hash.Data h -> Some (Signature.P256 h) + | Bls.Public_key_hash.Data h -> Some (Signature.Bls h) | _ -> None let originated_of_b58data = function diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml index 5b0ded930a8b05b39752fc88241b16cde1dbcd01..f981f386e287b146c39135b220469ff31a07bde1 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -26,6 +26,7 @@ type error += | Invalid_consensus_key_update_noop of Cycle_repr.t | Invalid_consensus_key_update_active + | Invalid_consensus_key_update_tz4 of Bls.Public_key.t let () = register_error_kind @@ -54,7 +55,21 @@ let () = "The delegate consensus key is already used by another delegate") Data_encoding.empty (function Invalid_consensus_key_update_active -> Some () | _ -> None) - (fun () -> Invalid_consensus_key_update_active) + (fun () -> Invalid_consensus_key_update_active) ; + register_error_kind + `Permanent + ~id:"delegate.consensus_key.tz4" + ~title:"Consensus key cannot be a tz4" + ~description:"Consensus key cannot be a tz4 (BLS public key)." + ~pp:(fun ppf pk -> + Format.fprintf + ppf + "The consensus key %a is forbidden as it is a BLS public key." + Bls.Public_key_hash.pp + (Bls.Public_key.hash pk)) + Data_encoding.(obj1 (req "delegate_pk" Bls.Public_key.encoding)) + (function Invalid_consensus_key_update_tz4 pk -> Some pk | _ -> None) + (fun pk -> Invalid_consensus_key_update_tz4 pk) type pk = Raw_context.consensus_pk = { delegate : Signature.Public_key_hash.t; @@ -97,12 +112,17 @@ let check_unused ctxt pkh = let*! is_active = Storage.Consensus_keys.mem ctxt pkh in fail_when is_active Invalid_consensus_key_update_active +let check_not_tz4 : Signature.Public_key.t -> unit tzresult = function + | Bls pk -> error (Invalid_consensus_key_update_tz4 pk) + | Ed25519 _ | Secp256k1 _ | P256 _ -> Ok () + let set_unused = Storage.Consensus_keys.remove let set_used = Storage.Consensus_keys.add let init ctxt delegate pk = let open Lwt_result_syntax in + let*? () = check_not_tz4 pk in let pkh = Signature.Public_key.hash pk in let* () = check_unused ctxt pkh in let*! ctxt = set_used ctxt pkh in @@ -176,6 +196,7 @@ let register_update ctxt delegate pk = Signature.Public_key.(pk = active_pubkey) (Invalid_consensus_key_update_noop first_active_cycle) in + let*? () = check_not_tz4 pk in let pkh = Signature.Public_key.hash pk in let* () = check_unused ctxt pkh in let*! ctxt = set_used ctxt pkh in diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli index edbf190c80390761b9eb5bdd69d94d8e8244c7fa..914f02ed7f86e6b455495442f58ad5057156b2be 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -32,6 +32,7 @@ type error += | Invalid_consensus_key_update_noop of Cycle_repr.t | Invalid_consensus_key_update_active + | Invalid_consensus_key_update_tz4 of Bls.Public_key.t (** The public key of a consensus key and the associated delegate. *) type pk = Raw_context.consensus_pk = { @@ -52,6 +53,9 @@ val pp : Format.formatter -> t -> unit val pkh : pk -> t +(** [check_not_tz4 pk] checks that [pk] is not a BLS address. *) +val check_not_tz4 : Signature.public_key -> unit tzresult + (** Initialize the consensus key when registering a delegate. *) val init : Raw_context.t -> diff --git a/src/proto_alpha/lib_protocol/gas_comparable_input_size.ml b/src/proto_alpha/lib_protocol/gas_comparable_input_size.ml index 62831ed1d33b1910b988b612184208391fdf523a..4a5cb467e4dc807f5855cb582ba7bc7a6c8d8672 100644 --- a/src/proto_alpha/lib_protocol/gas_comparable_input_size.ml +++ b/src/proto_alpha/lib_protocol/gas_comparable_input_size.ml @@ -82,8 +82,8 @@ let mutez (_tez : Alpha_context.Tez.tez) : t = let bool (_ : bool) : t = 1 -let signature (_signature : Script_typed_ir.Script_signature.t) : t = - Script_typed_ir.Script_signature.size +let signature (signature : Script_typed_ir.Script_signature.t) : t = + Script_typed_ir.Script_signature.size signature let key_hash (_keyhash : Signature.public_key_hash) : t = Signature.Public_key_hash.size diff --git a/src/proto_alpha/lib_protocol/michelson_v1_gas.ml b/src/proto_alpha/lib_protocol/michelson_v1_gas.ml index 23b1d88841883ec62f56512d756c3a9cf9afe123..721b420b5ecb0cd4ff6d68ac2e9f449227f1296d 100644 --- a/src/proto_alpha/lib_protocol/michelson_v1_gas.ml +++ b/src/proto_alpha/lib_protocol/michelson_v1_gas.ml @@ -279,6 +279,7 @@ module Cost_of = struct | Ed25519 _ -> cost_N_ICheck_signature_ed25519 (Bytes.length b) | Secp256k1 _ -> cost_N_ICheck_signature_secp256k1 (Bytes.length b) | P256 _ -> cost_N_ICheck_signature_p256 (Bytes.length b) + | Bls _ -> cost_N_ICheck_signature_bls (Bytes.length b) in atomic_step_cost cost @@ -698,7 +699,7 @@ module Cost_of = struct cost_DECODING_PUBLIC_KEY_ed25519 (max cost_DECODING_PUBLIC_KEY_secp256k1 - cost_DECODING_PUBLIC_KEY_p256)) + (max cost_DECODING_PUBLIC_KEY_p256 cost_DECODING_PUBLIC_KEY_bls))) let public_key_readable = atomic_step_cost @@ -707,7 +708,9 @@ module Cost_of = struct cost_B58CHECK_DECODING_PUBLIC_KEY_ed25519 (max cost_B58CHECK_DECODING_PUBLIC_KEY_secp256k1 - cost_B58CHECK_DECODING_PUBLIC_KEY_p256)) + (max + cost_B58CHECK_DECODING_PUBLIC_KEY_p256 + cost_B58CHECK_DECODING_PUBLIC_KEY_bls))) let key_hash_optimized = atomic_step_cost @@ -716,7 +719,9 @@ module Cost_of = struct cost_DECODING_PUBLIC_KEY_HASH_ed25519 (max cost_DECODING_PUBLIC_KEY_HASH_secp256k1 - cost_DECODING_PUBLIC_KEY_HASH_p256)) + (max + cost_DECODING_PUBLIC_KEY_HASH_p256 + cost_DECODING_PUBLIC_KEY_HASH_bls))) let key_hash_readable = atomic_step_cost @@ -725,7 +730,9 @@ module Cost_of = struct cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_ed25519 (max cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_secp256k1 - cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_p256)) + (max + cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_p256 + cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_bls))) let signature_optimized = atomic_step_cost @@ -734,7 +741,7 @@ module Cost_of = struct cost_DECODING_SIGNATURE_ed25519 (max cost_DECODING_SIGNATURE_secp256k1 - cost_DECODING_SIGNATURE_p256)) + (max cost_DECODING_SIGNATURE_p256 cost_DECODING_SIGNATURE_bls))) let signature_readable = atomic_step_cost @@ -743,7 +750,9 @@ module Cost_of = struct cost_B58CHECK_DECODING_SIGNATURE_ed25519 (max cost_B58CHECK_DECODING_SIGNATURE_secp256k1 - cost_B58CHECK_DECODING_SIGNATURE_p256)) + (max + cost_B58CHECK_DECODING_SIGNATURE_p256 + cost_B58CHECK_DECODING_SIGNATURE_bls))) let chain_id_optimized = atomic_step_cost cost_DECODING_CHAIN_ID @@ -816,7 +825,7 @@ module Cost_of = struct cost_ENCODING_PUBLIC_KEY_ed25519 (max cost_ENCODING_PUBLIC_KEY_secp256k1 - cost_ENCODING_PUBLIC_KEY_p256)) + (max cost_ENCODING_PUBLIC_KEY_p256 cost_ENCODING_PUBLIC_KEY_bls))) let public_key_readable = atomic_step_cost @@ -825,7 +834,9 @@ module Cost_of = struct cost_B58CHECK_ENCODING_PUBLIC_KEY_ed25519 (max cost_B58CHECK_ENCODING_PUBLIC_KEY_secp256k1 - cost_B58CHECK_ENCODING_PUBLIC_KEY_p256)) + (max + cost_B58CHECK_ENCODING_PUBLIC_KEY_p256 + cost_B58CHECK_ENCODING_PUBLIC_KEY_bls))) let key_hash_optimized = atomic_step_cost @@ -834,7 +845,9 @@ module Cost_of = struct cost_ENCODING_PUBLIC_KEY_HASH_ed25519 (max cost_ENCODING_PUBLIC_KEY_HASH_secp256k1 - cost_ENCODING_PUBLIC_KEY_HASH_p256)) + (max + cost_ENCODING_PUBLIC_KEY_HASH_p256 + cost_ENCODING_PUBLIC_KEY_HASH_bls))) let key_hash_readable = atomic_step_cost @@ -843,7 +856,9 @@ module Cost_of = struct cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_ed25519 (max cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_secp256k1 - cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_p256)) + (max + cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_p256 + cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_bls))) let signature_optimized = atomic_step_cost @@ -852,7 +867,7 @@ module Cost_of = struct cost_ENCODING_SIGNATURE_ed25519 (max cost_ENCODING_SIGNATURE_secp256k1 - cost_ENCODING_SIGNATURE_p256)) + (max cost_ENCODING_SIGNATURE_p256 cost_ENCODING_SIGNATURE_bls))) let signature_readable = atomic_step_cost @@ -861,7 +876,9 @@ module Cost_of = struct cost_B58CHECK_ENCODING_SIGNATURE_ed25519 (max cost_B58CHECK_ENCODING_SIGNATURE_secp256k1 - cost_B58CHECK_ENCODING_SIGNATURE_p256)) + (max + cost_B58CHECK_ENCODING_SIGNATURE_p256 + cost_B58CHECK_ENCODING_SIGNATURE_bls))) let chain_id_optimized = atomic_step_cost cost_ENCODING_CHAIN_ID diff --git a/src/proto_alpha/lib_protocol/michelson_v1_gas_costs_generated.ml b/src/proto_alpha/lib_protocol/michelson_v1_gas_costs_generated.ml index dc15839f70404e230b39cb80f3cc7ce8e20c4a07..da06d014c762863baabe79b8b697357bd33018dc 100644 --- a/src/proto_alpha/lib_protocol/michelson_v1_gas_costs_generated.ml +++ b/src/proto_alpha/lib_protocol/michelson_v1_gas_costs_generated.ml @@ -146,6 +146,14 @@ let cost_N_ICheck_signature_secp256k1 size = let v0 = S.safe_int size in S.safe_int 51_600 + (v0 + (v0 lsr 3)) +(* model N_ICheck_signature_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_N_ICheck_signature_bls size = + let open S_syntax in + let v0 = S.safe_int size in + S.safe_int 7_567_000 + (v0 + (v0 lsr 3)) + (* model N_IComb *) (* Approximating 3.531001 x term *) (* Note: size >= 2, so the cost is never 0 *) @@ -758,6 +766,11 @@ let cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_p256 = S.safe_int 3_300 (* model B58CHECK_DECODING_PUBLIC_KEY_HASH_secp256k1 *) let cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_secp256k1 = S.safe_int 3_300 +(* model B58CHECK_DECODING_PUBLIC_KEY_HASH_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_B58CHECK_DECODING_PUBLIC_KEY_HASH_bls = S.safe_int 3_300 + (* model B58CHECK_DECODING_PUBLIC_KEY_ed25519 *) let cost_B58CHECK_DECODING_PUBLIC_KEY_ed25519 = S.safe_int 4_200 @@ -767,6 +780,11 @@ let cost_B58CHECK_DECODING_PUBLIC_KEY_p256 = S.safe_int 325_000 (* model B58CHECK_DECODING_PUBLIC_KEY_secp256k1 *) let cost_B58CHECK_DECODING_PUBLIC_KEY_secp256k1 = S.safe_int 9_000 +(* model B58CHECK_DECODING_PUBLIC_KEY_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_B58CHECK_DECODING_PUBLIC_KEY_bls = S.safe_int 9_000 + (* model B58CHECK_DECODING_SIGNATURE_ed25519 *) let cost_B58CHECK_DECODING_SIGNATURE_ed25519 = S.safe_int 6_400 @@ -776,6 +794,11 @@ let cost_B58CHECK_DECODING_SIGNATURE_p256 = S.safe_int 6_400 (* model B58CHECK_DECODING_SIGNATURE_secp256k1 *) let cost_B58CHECK_DECODING_SIGNATURE_secp256k1 = S.safe_int 6_400 +(* model B58CHECK_DECODING_SIGNATURE_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_B58CHECK_DECODING_SIGNATURE_bls = S.safe_int 6_400 + (* model ENCODING_BLS_FR *) let cost_ENCODING_BLS_FR = S.safe_int 80 @@ -797,6 +820,11 @@ let cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_p256 = S.safe_int 3_200 (* model B58CHECK_ENCODING_PUBLIC_KEY_HASH_secp256k1 *) let cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_secp256k1 = S.safe_int 3_200 +(* model B58CHECK_ENCODING_PUBLIC_KEY_HASH_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_B58CHECK_ENCODING_PUBLIC_KEY_HASH_bls = S.safe_int 3_200 + (* model B58CHECK_ENCODING_PUBLIC_KEY_ed25519 *) let cost_B58CHECK_ENCODING_PUBLIC_KEY_ed25519 = S.safe_int 4_500 @@ -806,6 +834,11 @@ let cost_B58CHECK_ENCODING_PUBLIC_KEY_p256 = S.safe_int 4_550 (* model B58CHECK_ENCODING_PUBLIC_KEY_secp256k1 *) let cost_B58CHECK_ENCODING_PUBLIC_KEY_secp256k1 = S.safe_int 4_950 +(* model B58CHECK_ENCODING_PUBLIC_KEY_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_B58CHECK_ENCODING_PUBLIC_KEY_bls = S.safe_int 4_500 + (* model B58CHECK_ENCODING_SIGNATURE_ed25519 *) let cost_B58CHECK_ENCODING_SIGNATURE_ed25519 = S.safe_int 8_300 @@ -815,6 +848,11 @@ let cost_B58CHECK_ENCODING_SIGNATURE_p256 = S.safe_int 8_300 (* model B58CHECK_ENCODING_SIGNATURE_secp256k1 *) let cost_B58CHECK_ENCODING_SIGNATURE_secp256k1 = S.safe_int 8_300 +(* model B58CHECK_ENCODING_SIGNATURE_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_B58CHECK_ENCODING_SIGNATURE_bls = S.safe_int 8_300 + (* model DECODING_CHAIN_ID *) let cost_DECODING_CHAIN_ID = S.safe_int 50 @@ -827,6 +865,11 @@ let cost_DECODING_PUBLIC_KEY_HASH_p256 = S.safe_int 50 (* model DECODING_PUBLIC_KEY_HASH_secp256k1 *) let cost_DECODING_PUBLIC_KEY_HASH_secp256k1 = S.safe_int 50 +(* model DECODING_PUBLIC_KEY_HASH_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_DECODING_PUBLIC_KEY_HASH_bls = S.safe_int 50 + (* model DECODING_PUBLIC_KEY_ed25519 *) let cost_DECODING_PUBLIC_KEY_ed25519 = S.safe_int 60 @@ -836,6 +879,11 @@ let cost_DECODING_PUBLIC_KEY_p256 = S.safe_int 320_000 (* model DECODING_PUBLIC_KEY_secp256k1 *) let cost_DECODING_PUBLIC_KEY_secp256k1 = S.safe_int 4_900 +(* model DECODING_PUBLIC_KEY_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_DECODING_PUBLIC_KEY_bls = S.safe_int 60 + (* model DECODING_SIGNATURE_ed25519 *) let cost_DECODING_SIGNATURE_ed25519 = S.safe_int 35 @@ -845,6 +893,11 @@ let cost_DECODING_SIGNATURE_p256 = S.safe_int 35 (* model DECODING_SIGNATURE_secp256k1 *) let cost_DECODING_SIGNATURE_secp256k1 = S.safe_int 35 +(* model DECODING_SIGNATURE_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_DECODING_SIGNATURE_bls = S.safe_int 35 + (* model DECODING_Chest_key *) let cost_DECODING_Chest_key = S.safe_int 5900 @@ -867,6 +920,11 @@ let cost_ENCODING_PUBLIC_KEY_HASH_p256 = S.safe_int 70 (* model ENCODING_PUBLIC_KEY_HASH_secp256k1 *) let cost_ENCODING_PUBLIC_KEY_HASH_secp256k1 = S.safe_int 70 +(* model ENCODING_PUBLIC_KEY_HASH_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_ENCODING_PUBLIC_KEY_HASH_bls = S.safe_int 70 + (* model ENCODING_PUBLIC_KEY_ed25519 *) let cost_ENCODING_PUBLIC_KEY_ed25519 = S.safe_int 80 @@ -876,6 +934,11 @@ let cost_ENCODING_PUBLIC_KEY_p256 = S.safe_int 90 (* model ENCODING_PUBLIC_KEY_secp256k1 *) let cost_ENCODING_PUBLIC_KEY_secp256k1 = S.safe_int 455 +(* model ENCODING_PUBLIC_KEY_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_ENCODING_PUBLIC_KEY_bls = S.safe_int 80 + (* model ENCODING_SIGNATURE_ed25519 *) let cost_ENCODING_SIGNATURE_ed25519 = S.safe_int 45 @@ -885,6 +948,11 @@ let cost_ENCODING_SIGNATURE_p256 = S.safe_int 45 (* model ENCODING_SIGNATURE_secp256k1 *) let cost_ENCODING_SIGNATURE_secp256k1 = S.safe_int 45 +(* model ENCODING_SIGNATURE_bls *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/3183 + Run benchmarks to update costs. *) +let cost_ENCODING_SIGNATURE_bls = S.safe_int 45 + (* model ENCODING_Chest_key *) let cost_ENCODING_Chest_key = S.safe_int 10_000 diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index c0d35adfd418504dd5d12c9491b0ba7f14c9cc86..4420693aca6770dd5624e2a08a8e87fe8278c4e0 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -551,19 +551,24 @@ let to_list = function Contents_list l -> contents_list_to_list l (* This first version of of_list has the type (_, string) result expected by the conv_with_guard combinator of Data_encoding. For a more conventional return type see [of_list] below. *) -let rec of_list_internal = function +let of_list_internal contents = + let rec of_list_internal acc = function + | [] -> Ok acc + | Contents o :: os -> ( + match (o, acc) with + | ( Manager_operation _, + Contents_list (Single (Manager_operation _) as rest) ) -> + (of_list_internal [@tailcall]) (Contents_list (Cons (o, rest))) os + | Manager_operation _, Contents_list (Cons _ as rest) -> + (of_list_internal [@tailcall]) (Contents_list (Cons (o, rest))) os + | _ -> + Error + "Operation list of length > 1 should only contain manager \ + operations.") + in + match List.rev contents with | [] -> Error "Operation lists should not be empty." - | [Contents o] -> Ok (Contents_list (Single o)) - | Contents o :: os -> ( - of_list_internal os >>? fun (Contents_list os) -> - match (o, os) with - | Manager_operation _, Single (Manager_operation _) -> - Ok (Contents_list (Cons (o, os))) - | Manager_operation _, Cons _ -> Ok (Contents_list (Cons (o, os))) - | _ -> - Error - "Operation list of length > 1 should only contains manager \ - operations.") + | Contents o :: os -> of_list_internal (Contents_list (Single o)) os type error += Contents_list_error of string (* `Permanent *) @@ -628,6 +633,13 @@ let zk_rollup_operation_update_tag = zk_rollup_operation_tag_offset + 2 module Encoding = struct open Data_encoding + (** These tags are reserved for future extensions: [fd] - [ff]. *) + let reserved_tag t = Compare.Int.(t >= 0xfd) + + let signature_prefix_tag = 0xff + + let () = assert (reserved_tag signature_prefix_tag) + let case tag name args proj inj = case tag @@ -1752,8 +1764,57 @@ module Encoding = struct zk_rollup_operation_update_tag Manager_operations.zk_rollup_update_case + type packed_case = PCase : 'b case -> packed_case + + let contents_cases = + [ + PCase endorsement_case; + PCase preendorsement_case; + PCase dal_attestation_case; + PCase seed_nonce_revelation_case; + PCase vdf_revelation_case; + PCase double_endorsement_evidence_case; + PCase double_preendorsement_evidence_case; + PCase double_baking_evidence_case; + PCase activate_account_case; + PCase proposals_case; + PCase ballot_case; + PCase reveal_case; + PCase transaction_case; + PCase origination_case; + PCase delegation_case; + PCase set_deposits_limit_case; + PCase increase_paid_storage_case; + PCase update_consensus_key_case; + PCase drain_delegate_case; + PCase failing_noop_case; + PCase register_global_constant_case; + PCase tx_rollup_origination_case; + PCase tx_rollup_submit_batch_case; + PCase tx_rollup_commit_case; + PCase tx_rollup_return_bond_case; + PCase tx_rollup_finalize_commitment_case; + PCase tx_rollup_remove_commitment_case; + PCase tx_rollup_rejection_case; + PCase tx_rollup_dispatch_tickets_case; + PCase transfer_ticket_case; + PCase dal_publish_slot_header_case; + PCase sc_rollup_originate_case; + PCase sc_rollup_add_messages_case; + PCase sc_rollup_cement_case; + PCase sc_rollup_publish_case; + PCase sc_rollup_refute_case; + PCase sc_rollup_timeout_case; + PCase sc_rollup_execute_outbox_message_case; + PCase sc_rollup_recover_bond_case; + PCase zk_rollup_origination_case; + PCase zk_rollup_publish_case; + PCase zk_rollup_update_case; + ] + let contents_encoding = - let make (Case {tag; name; encoding; select; proj; inj}) = + let make (PCase (Case {tag; name; encoding; select; proj; inj})) = + assert (not @@ reserved_tag tag) ; case (Tag tag) name @@ -1761,72 +1822,167 @@ module Encoding = struct (fun o -> match select o with None -> None | Some o -> Some (proj o)) (fun x -> Contents (inj x)) in - def "operation.alpha.contents" - @@ union - [ - make endorsement_case; - make preendorsement_case; - make dal_attestation_case; - make seed_nonce_revelation_case; - make vdf_revelation_case; - make double_endorsement_evidence_case; - make double_preendorsement_evidence_case; - make double_baking_evidence_case; - make activate_account_case; - make proposals_case; - make ballot_case; - make reveal_case; - make transaction_case; - make origination_case; - make delegation_case; - make set_deposits_limit_case; - make increase_paid_storage_case; - make update_consensus_key_case; - make drain_delegate_case; - make failing_noop_case; - make register_global_constant_case; - make tx_rollup_origination_case; - make tx_rollup_submit_batch_case; - make tx_rollup_commit_case; - make tx_rollup_return_bond_case; - make tx_rollup_finalize_commitment_case; - make tx_rollup_remove_commitment_case; - make tx_rollup_rejection_case; - make tx_rollup_dispatch_tickets_case; - make transfer_ticket_case; - make dal_publish_slot_header_case; - make sc_rollup_originate_case; - make sc_rollup_add_messages_case; - make sc_rollup_cement_case; - make sc_rollup_publish_case; - make sc_rollup_refute_case; - make sc_rollup_timeout_case; - make sc_rollup_execute_outbox_message_case; - make sc_rollup_recover_bond_case; - make zk_rollup_origination_case; - make zk_rollup_publish_case; - make zk_rollup_update_case; - ] + def "operation.alpha.contents" @@ union (List.map make contents_cases) let contents_list_encoding = conv_with_guard to_list of_list_internal (Variable.list contents_encoding) - let optional_signature_encoding = + let protocol_data_json_encoding = conv - (function Some s -> s | None -> Signature.zero) - (fun s -> if Signature.equal s Signature.zero then None else Some s) - Signature.encoding - + (fun (Operation_data {contents; signature}) -> + (Contents_list contents, signature)) + (fun (Contents_list contents, signature) -> + Operation_data {contents; signature}) + (obj2 + (req "contents" (dynamic_size contents_list_encoding)) + (opt "signature" Signature.encoding)) + + type contents_or_signature_prefix = + | Actual_contents of packed_contents + | Signature_prefix of Signature.prefix + + let contents_or_signature_prefix_encoding = + let make_contents (PCase (Case {tag; name; encoding; select; proj; inj})) = + assert (not @@ reserved_tag tag) ; + case + (Tag tag) + name + encoding + (function + | Actual_contents o -> ( + match select o with None -> None | Some o -> Some (proj o)) + | _ -> None) + (fun x -> Actual_contents (Contents (inj x))) + in + def "operation.alpha.contents_or_signature_prefix" + @@ union + @@ case + (Tag signature_prefix_tag) + "signature_prefix" + (obj1 (req "signature_prefix" Signature.prefix_encoding)) + (function Signature_prefix prefix -> Some prefix | _ -> None) + (fun prefix -> Signature_prefix prefix) + (* The case signature_prefix is added to the operation's contents so that + we can store the prefix of BLS signatures without breaking the + encoding of operations. *) + :: List.map make_contents contents_cases + + let of_contents_and_signature_prefix contents_and_prefix = + let open Result_syntax in + let rec loop acc = function + | [] -> Ok acc + | Signature_prefix _ :: _ -> Error "Signature prefix must appear last" + | Actual_contents (Contents o) :: os -> ( + match (o, acc) with + | ( Manager_operation _, + Contents_list (Single (Manager_operation _) as rest) ) -> + (loop [@tailcall]) (Contents_list (Cons (o, rest))) os + | Manager_operation _, Contents_list (Cons _ as rest) -> + (loop [@tailcall]) (Contents_list (Cons (o, rest))) os + | _ -> + Error + "Operation list of length > 1 should only contain manager \ + operations.") + in + let rev_contents, prefix = + match List.rev contents_and_prefix with + | Signature_prefix prefix :: rev_contents -> (rev_contents, Some prefix) + | rev_contents -> (rev_contents, None) + in + let+ packed_contents = + match rev_contents with + | [] -> Error "Operation lists should not be empty." + | Signature_prefix _ :: _ -> Error "Signature prefix must appear last" + | Actual_contents (Contents o) :: os -> loop (Contents_list (Single o)) os + in + (packed_contents, prefix) + + let protocol_data_binary_encoding = + conv_with_guard + (fun (Operation_data {contents; signature}) -> + let contents_list = + List.map (fun c -> Actual_contents c) + @@ to_list (Contents_list contents) + in + let contents_and_signature_prefix, sig_suffix = + match signature with + | None -> (contents_list, Signature.(to_bytes zero)) + | Some signature -> ( + let {Signature.prefix; suffix} = + Signature.split_signature signature + in + match prefix with + | None -> (contents_list, suffix) + | Some prefix -> + (contents_list @ [Signature_prefix prefix], suffix)) + in + (contents_and_signature_prefix, sig_suffix)) + (fun (contents_and_signature_prefix, suffix) -> + let open Result_syntax in + let* Contents_list contents, prefix = + of_contents_and_signature_prefix contents_and_signature_prefix + in + let+ signature = + Result.of_option ~error:"Invalid signature" + @@ Signature.of_splitted {Signature.prefix; suffix} + in + let signature = + match prefix with + | None -> + if Signature.(signature = zero) then None else Some signature + | Some _ -> Some signature + in + Operation_data {contents; signature}) + (obj2 + (req + "contents_and_signature_prefix" + (Variable.list contents_or_signature_prefix_encoding)) + (req "signature_suffix" (Fixed.bytes Hex 64))) + + (* The binary and JSON encodings are different for protocol data, because we + have to fit BLS signatures (which are 96 bytes long) in a backward + compatible manner with fixed size signatures of 64 bytes. + + The JSON encoding is the same as in the previous protocols. + + To support BLS signatures, we extract the prefix of the signature and fit + it inside the field [contents] while keeping the 64 bytes suffix in the + same place as the other signature kinds (i.e. at the end). + + For instance the binary protocol data for a transfer operation signed by a + Ed25519 key would look like: + + +----------------+------------+ + | Transaction | signature | + +----+------+----+------------+ + | 6C | ... | 00 | (64 bytes) | + +----+------+----+------------+ + + The same transfer signed by a BLS key would be instead: + + +----------------+----------------------------+-------------------+ + | Transaction | signature prefix | signature suffix | + +----+------+----+----+----+------------------+-------------------+ + | 6C | ... | 00 | ff | 03 | (first 32 bytes) | (last 64 bytes) | + +----+------+----+----+----+------------------+-------------------+ + + Which can also be viewed with an equivalent schema: + + +----------------+----+---------------+--------------------------+ + | Transaction | ff | signature tag | signature | + +----+------+----+----+---------------+--------------------------+ + | 6C | ... | 00 | ff | 03 (BLS) | (96 bytes BLS signature) | + +----+------+----+----+---------------+--------------------------+ + + NOTE: BLS only supports the tagged format and Ed25519, Secp256k1 and P256 + signatures only support the untagged one. The latter restriction is only + here to guarantee unicity of the binary representation for signatures. + *) let protocol_data_encoding = def "operation.alpha.contents_and_signature" - @@ conv - (fun (Operation_data {contents; signature}) -> - (Contents_list contents, signature)) - (fun (Contents_list contents, signature) -> - Operation_data {contents; signature}) - (obj2 - (req "contents" contents_list_encoding) - (req "signature" optional_signature_encoding)) + @@ splitted + ~json:protocol_data_json_encoding + ~binary:protocol_data_binary_encoding let operation_encoding = conv diff --git a/src/proto_alpha/lib_protocol/script_typed_ir.ml b/src/proto_alpha/lib_protocol/script_typed_ir.ml index e2db2ed197c2e00280a4ac15ed0a0b85af49308a..e60332623184d45f6dd77f3cc934bf56df6ade23 100644 --- a/src/proto_alpha/lib_protocol/script_typed_ir.ml +++ b/src/proto_alpha/lib_protocol/script_typed_ir.ml @@ -85,7 +85,7 @@ module Script_signature = struct let compare (Signature_tag x) (Signature_tag y) = Signature.compare x y - let size = Signature.size + let size (Signature_tag s) = Signature.size s end type signature = Script_signature.t diff --git a/src/proto_alpha/lib_protocol/script_typed_ir.mli b/src/proto_alpha/lib_protocol/script_typed_ir.mli index 80f0a539d33e49e536edf5552434119d2e73e9a1..2f89ede2c7c5322f76cda0218431eb4736e8c64a 100644 --- a/src/proto_alpha/lib_protocol/script_typed_ir.mli +++ b/src/proto_alpha/lib_protocol/script_typed_ir.mli @@ -87,7 +87,7 @@ module Script_signature : sig val compare : t -> t -> int - val size : int + val size : t -> int end type signature = Script_signature.t diff --git a/src/proto_alpha/lib_protocol/script_typed_ir_size.ml b/src/proto_alpha/lib_protocol/script_typed_ir_size.ml index dc70b78eaedaecab84901de76294b1e102804c8c..65933b0cfc06b5a323bee6715ec1976a350f9e43 100644 --- a/src/proto_alpha/lib_protocol/script_typed_ir_size.ml +++ b/src/proto_alpha/lib_protocol/script_typed_ir_size.ml @@ -124,13 +124,23 @@ let script_nat_size n = Script_int.to_zint n |> z_size let script_int_size n = Script_int.to_zint n |> z_size -let signature_size = !!96 (* By Obj.reachable_words. *) +let signature_size (Script_signature.Signature_tag x) = + match x with + (* By Obj.reachable_words. *) + | Ed25519 _ | Secp256k1 _ | P256 _ | Unknown _ -> !!96 + | Bls _ -> !!128 let key_hash_size (_x : Signature.public_key_hash) = !!64 (* By Obj.reachable_words. *) let public_key_size (x : public_key) = - h1w +? match x with Ed25519 _ -> 64 | Secp256k1 _ -> 72 | P256 _ -> 96 + h1w + +? + match x with + | Ed25519 _ -> 64 + | Secp256k1 _ -> 72 + | P256 _ -> 96 + | Bls _ -> 64 let mutez_size = h2w @@ -264,7 +274,7 @@ let rec value_size : | Unit_t -> ret_succ accu | Int_t -> ret_succ_adding accu (script_int_size x) | Nat_t -> ret_succ_adding accu (script_nat_size x) - | Signature_t -> ret_succ_adding accu signature_size + | Signature_t -> ret_succ_adding accu (signature_size x) | String_t -> ret_succ_adding accu (script_string_size x) | Bytes_t -> ret_succ_adding accu (bytes_size x) | Mutez_t -> ret_succ_adding accu mutez_size diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index ab1eff9872cde7ea4c17bdbd89b371a650ac6cba..956e16a982d161b4c7c5b08a141a47bc2a5d5a8a 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -903,12 +903,14 @@ module Public_key_hash = struct module Path_Ed25519 = Path_encoding.Make_hex (Ed25519.Public_key_hash) module Path_Secp256k1 = Path_encoding.Make_hex (Secp256k1.Public_key_hash) module Path_P256 = Path_encoding.Make_hex (P256.Public_key_hash) + module Path_Bls = Path_encoding.Make_hex (Bls.Public_key_hash) let to_path (key : public_key_hash) l = match key with | Ed25519 h -> "ed25519" :: Path_Ed25519.to_path h l | Secp256k1 h -> "secp256k1" :: Path_Secp256k1.to_path h l | P256 h -> "p256" :: Path_P256.to_path h l + | Bls h -> "bls" :: Path_Bls.to_path h l let of_path : _ -> public_key_hash option = function | "ed25519" :: rest -> ( @@ -923,13 +925,18 @@ module Public_key_hash = struct match Path_P256.of_path rest with | Some pkh -> Some (P256 pkh) | None -> None) + | "bls" :: rest -> ( + match Path_Bls.of_path rest with + | Some pkh -> Some (Bls pkh) + | None -> None) | _ -> None let path_length = let l1 = Path_Ed25519.path_length and l2 = Path_Secp256k1.path_length - and l3 = Path_P256.path_length in - assert (Compare.Int.(l1 = l2 && l2 = l3)) ; + and l3 = Path_P256.path_length + and l4 = Path_Bls.path_length in + assert (Compare.Int.(l1 = l2 && l2 = l3 && l3 = l4)) ; l1 + 1 end diff --git a/src/proto_alpha/lib_protocol/test/helpers/account.ml b/src/proto_alpha/lib_protocol/test/helpers/account.ml index ee34e31ae9b6f9cb50d1d773e0b64923e61f6d39..3e7d428897e5961cfee0976c9b617a522bf95064 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/account.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/account.ml @@ -40,11 +40,17 @@ let random_seed ~rng_state = Bytes.init Tezos_crypto.Hacl.Ed25519.sk_size (fun _i -> Char.chr (Random.State.int rng_state 256)) +let random_algo ~rng_state : Tezos_crypto.Signature.algo = + match Random.State.int rng_state 3 with + | 0 -> Ed25519 + | 1 -> Secp256k1 + | 2 -> P256 + | 3 -> Bls + | _ -> assert false + let new_account ?(rng_state = Random.State.make_self_init ()) - ?(seed = random_seed ~rng_state) () = - let pkh, pk, sk = - Tezos_crypto.Signature.generate_key ~algo:Ed25519 ~seed () - in + ?(seed = random_seed ~rng_state) ?(algo = random_algo ~rng_state) () = + let pkh, pk, sk = Tezos_crypto.Signature.generate_key ~algo ~seed () in let account = {pkh; pk; sk} in Tezos_crypto.Signature.Public_key_hash.Table.add known_accounts pkh account ; account diff --git a/src/proto_alpha/lib_protocol/test/helpers/account.mli b/src/proto_alpha/lib_protocol/test/helpers/account.mli index d0781041c0756b9a146b6cfb950fec64a75812cc..f97b60d4d88393e17c21a704bff66143e20442ab 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/account.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/account.mli @@ -40,10 +40,16 @@ val activator_account : account val dummy_account : account -(** [new_account ?rng_state ?seed ()] creates a new account with the given [seed] (or - [rng_state] to generate the seed) and add it to the global account state. +(** [new_account ?rng_state ?seed ?algo ()] creates a new account with curve + [algo] with the given [seed] (or [rng_state] to generate the seed) and add + it to the global account state. *) -val new_account : ?rng_state:Random.State.t -> ?seed:Bytes.t -> unit -> account +val new_account : + ?rng_state:Random.State.t -> + ?seed:Bytes.t -> + ?algo:Tezos_crypto.Signature.algo -> + unit -> + account val add_account : t -> unit diff --git a/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml b/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml index 8a8a0cfb024af6179c13d4ef87f245f0fa771620..1441e60aaf8d42e80f8f58466305b545af20d5a1 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml @@ -117,71 +117,119 @@ let pp_kind fmt k = (** {2 Generators} *) +module Gen_hash (H : sig + type t + + val size : int + + val of_bytes_exn : bytes -> t +end) = +struct + let gen = + let open QCheck2.Gen in + let+ str = string_size (pure H.size) in + H.of_bytes_exn (Bytes.unsafe_of_string str) +end + (** {3 Selection in hashes list} *) -let block_hashes = - List.map - Tezos_crypto.Block_hash.of_b58check_exn - [ - "BLbcVY1kYiKQy2MJJfoHJMN2xRk5QPG1PEKWMDSyW2JMxBsMmiL"; - "BLFhLKqQQn32Cc9QXqtEqysYqWNCowNKaypVHP5zEyZcywbXcHo"; - "BLuurCvGmNPTzXSnGCpcFPy5h8A49PwH2LnfAWBnp5R1qv5czwe"; - ] - -let payload_hashes = - List.map - Block_payload_hash.of_b58check_exn - [ - "vh2gWcSUUhJBwvjx4vS7JN5ioMVWpHCSK6W2MKNPr5dn6NUdfFDQ"; - "vh1p1VzeYjZLEW6WDqdTwVy354KEmGCDgPmagEKcLN4NT4X58mNk"; - "vh2TyrWeZ2dydEy9ZjmvrjQvyCs5sdHZPypcZrXDUSM1tNuPermf"; - ] +let gen_block_hash = + let module G = Gen_hash (Tezos_crypto.Block_hash) in + G.gen -let random_payload_hash = QCheck2.Gen.oneofl payload_hashes +let random_payload_hash = + let module G = Gen_hash (Block_payload_hash) in + G.gen -let signatures = - List.map - Tezos_crypto.Signature.of_b58check_exn - [ - "sigaNsiye7D8dJHKSQZBwDbS2aQNXipDP7bw8uQnMgnaXi5pcnoPZRKXrDeFRx4FjWJD2xfyUA9CuBXhwPHhVs7LxkL4vT32"; - "sigvtPBMQvk2DgNtu3AKFU1ZRsagGxsoiZVQyQhJNEojReBY2vE5sDwt3H7Mh8RMe27QHBjemxqhMVVszZqpNsdDux6KAELX"; - "sighje7pEbUUwGtJ4GTP7uzMZe5SFz6dRRC3BvZBHnrRHnc47WHGnVdfiscHPMek7esmj7saTuj54QBWy3SezyA2EGbHkmW5"; - ] +let gen_algo = QCheck2.Gen.oneofl Tezos_crypto.Signature.algos -let random_signature = QCheck2.Gen.oneofl signatures +let random_seed = + let open QCheck2.Gen in + let+ str = string_size (pure Tezos_crypto.Hacl.Ed25519.sk_size) in + Bytes.unsafe_of_string str -let pkhs = - List.map - Tezos_crypto.Signature.Public_key_hash.of_b58check_exn - [ - "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - "tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv"; - "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU"; - ] +let random_keys = + let open QCheck2.Gen in + let* algo = gen_algo in + let+ seed = random_seed in + Tezos_crypto.Signature.generate_key ~algo ~seed () -let random_pkh = QCheck2.Gen.oneofl pkhs +let random_tz1 = + let open QCheck2.Gen in + let+ str = string_size (pure Tezos_crypto.Ed25519.Public_key_hash.size) in + (Ed25519 (Tezos_crypto.Ed25519.Public_key_hash.of_string_exn str) + : public_key_hash) -let pks = - List.map - Tezos_crypto.Signature.Public_key.of_b58check_exn - [ - "edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2"; - "edpkv8EUUH68jmo3f7Um5PezmfGrRF24gnfLpH3sVNwJnV5bVCxL2n"; - "edpkuFrRoDSEbJYgxRtLx2ps82UdaYc1WwfS9sE11yhauZt5DgCHbU"; - ] +let random_tz2 = + let open QCheck2.Gen in + let+ str = string_size (pure Tezos_crypto.Secp256k1.Public_key_hash.size) in + (Secp256k1 (Tezos_crypto.Secp256k1.Public_key_hash.of_string_exn str) + : public_key_hash) -let random_pk = QCheck2.Gen.oneofl pks +let random_tz3 = + let open QCheck2.Gen in + let+ str = string_size (pure Tezos_crypto.P256.Public_key_hash.size) in + (P256 (Tezos_crypto.P256.Public_key_hash.of_string_exn str) : public_key_hash) -let contract_hashes = - List.map - Contract_hash.of_b58check_exn - [ - "KT1WvzYHCNBvDSdwafTHv7nJ1dWmZ8GCYuuC"; - "KT1NkWx47WzJeHCSyB62WjLtFn4tRf3uXBur"; - "KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton"; - ] +let random_tz4 = + let open QCheck2.Gen in + let+ str = string_size (pure Tezos_crypto.Bls.Public_key_hash.size) in + (Bls (Tezos_crypto.Bls.Public_key_hash.of_string_exn str) : public_key_hash) -let random_contract_hash = QCheck2.Gen.oneofl contract_hashes +let random_pkh = + let open QCheck2.Gen in + let* algo = gen_algo in + match algo with + | Ed25519 -> random_tz1 + | Secp256k1 -> random_tz2 + | P256 -> random_tz3 + | Bls -> random_tz4 + +let random_pk = + let open QCheck2.Gen in + let+ _, pk, _ = random_keys in + pk + +let random_signature = + let open QCheck2.Gen in + let* algo = option ~ratio:0.8 gen_algo in + match algo with + | None -> + let+ str = string_size (pure Tezos_crypto.Ed25519.size) in + (Unknown (Bytes.unsafe_of_string str) : Tezos_crypto.Signature.t) + | Some Ed25519 -> + let+ str = string_size (pure Tezos_crypto.Ed25519.size) in + (Ed25519 (Tezos_crypto.Ed25519.of_string_exn str) + : Tezos_crypto.Signature.t) + | Some Secp256k1 -> + let+ str = string_size (pure Tezos_crypto.Secp256k1.size) in + (Secp256k1 (Tezos_crypto.Secp256k1.of_string_exn str) + : Tezos_crypto.Signature.t) + | Some P256 -> + let+ str = string_size (pure Tezos_crypto.P256.size) in + (P256 (Tezos_crypto.P256.of_string_exn str) : Tezos_crypto.Signature.t) + | Some Bls -> + let+ seed = random_seed in + let _, _, sk = Tezos_crypto.Signature.generate_key ~algo:Bls ~seed () in + Tezos_crypto.Signature.sign sk Bytes.empty + +let random_signature = + let open QCheck2.Gen in + graft_corners + random_signature + Tezos_crypto.Signature. + [ + of_ed25519 Tezos_crypto.Ed25519.zero; + of_secp256k1 Tezos_crypto.Secp256k1.zero; + of_p256 Tezos_crypto.P256.zero; + of_bls Tezos_crypto.Bls.zero; + Unknown (Bytes.make 64 '\000'); + ] + () + +let random_contract_hash = + let module G = Gen_hash (Contract_hash) in + G.gen let block_headers = let bh1 = @@ -203,73 +251,33 @@ let block_headers = let random_block_header = QCheck2.Gen.oneofl block_headers -let tx_rollups = - List.filter_map - Tx_rollup.of_b58check_opt - [ - "txr1hFmPcr5y1P2xTm7W2y1sfjLLCUdzCZGvg"; - "txr1jux4nZWf8ToGZc4ojLBbT538BBTTSiJUD"; - "txr1TAFTENC2YACvoMDrpJHCbdvdfSSjcjEjc"; - ] - -let random_tx_rollup = QCheck2.Gen.oneofl tx_rollups +let random_tx_rollup = + let open QCheck2.Gen in + let module G = Gen_hash (Tezos_crypto.Operation_hash) in + let+ oph = G.gen in + let nonce = Origination_nonce.Internal_for_tests.initial oph in + Tx_rollup.Internal_for_tests.originated_tx_rollup nonce -let sc_rollups = - List.map - Sc_rollup.Address.of_b58check_exn - [ - "scr1FPSu51gGtyv9S5HqDqXeH16DJviJ9qpr6"; - "scr1U39BVdpVQun1QjjiXfd3XgoBKcenWt5sb"; - "scr1Kqqbvust2adJMtSu2V4fcd49oQHug4BLb"; - ] +let random_sc_rollup = + let module G = Gen_hash (Sc_rollup.Address) in + G.gen -let random_sc_rollup = QCheck2.Gen.oneofl sc_rollups +let random_proto = + let module G = Gen_hash (Tezos_crypto.Protocol_hash) in + G.gen -let protos = - List.map - (fun s -> Tezos_crypto.Protocol_hash.of_b58check_exn s) - [ - "ProtoALphaALphaALphaALphaALphaALphaALpha61322gcLUGH"; - "ProtoALphaALphaALphaALphaALphaALphaALphabc2a7ebx6WB"; - "ProtoALphaALphaALphaALphaALphaALphaALpha84efbeiF6cm"; - "ProtoALphaALphaALphaALphaALphaALphaALpha91249Z65tWS"; - "ProtoALphaALphaALphaALphaALphaALphaALpha537f5h25LnN"; - "ProtoALphaALphaALphaALphaALphaALphaALpha5c8fefgDYkr"; - "ProtoALphaALphaALphaALphaALphaALphaALpha3f31feSSarC"; - "ProtoALphaALphaALphaALphaALphaALphaALphabe31ahnkxSC"; - "ProtoALphaALphaALphaALphaALphaALphaALphabab3bgRb7zQ"; - "ProtoALphaALphaALphaALphaALphaALphaALphaf8d39cctbpk"; - "ProtoALphaALphaALphaALphaALphaALphaALpha3b981byuYxD"; - "ProtoALphaALphaALphaALphaALphaALphaALphaa116bccYowi"; - "ProtoALphaALphaALphaALphaALphaALphaALphacce68eHqboj"; - "ProtoALphaALphaALphaALphaALphaALphaALpha225c7YrWwR7"; - "ProtoALphaALphaALphaALphaALphaALphaALpha58743cJL6FG"; - "ProtoALphaALphaALphaALphaALphaALphaALphac91bcdvmJFR"; - "ProtoALphaALphaALphaALphaALphaALphaALpha1faaadhV7oW"; - "ProtoALphaALphaALphaALphaALphaALphaALpha98232gD94QJ"; - "ProtoALphaALphaALphaALphaALphaALphaALpha9d1d8cijvAh"; - "ProtoALphaALphaALphaALphaALphaALphaALphaeec52dKF6Gx"; - "ProtoALphaALphaALphaALphaALphaALphaALpha841f2cQqajX"; - ] - -let random_proto = QCheck2.Gen.oneofl protos - -let codes = - List.filter_map - Blinded_public_key_hash.activation_code_of_hex - [ - "41f98b15efc63fa893d61d7d6eee4a2ce9427ac4"; - "411dfef031eeecc506de71c9df9f8e44297cf5ba"; - "08d7d355bc3391d12d140780b39717d9f46fcf87"; - ] - -let random_code = QCheck2.Gen.oneofl codes +let random_code = + let open QCheck2.Gen in + let+ str = string_size (pure Tezos_crypto.Ed25519.Public_key_hash.size) in + let (`Hex hex) = Hex.of_string str in + Blinded_public_key_hash.activation_code_of_hex hex + |> WithExceptions.Option.get ~loc:__LOC__ (** {2 Operations parameters generators} *) let random_shell : Tezos_base.Operation.shell_header QCheck2.Gen.t = let open QCheck2.Gen in - let+ branch = oneofl block_hashes in + let+ branch = gen_block_hash in Tezos_base.Operation.{branch} let gen_slot = @@ -321,8 +329,6 @@ let random_contract = let+ contract_hash = random_contract_hash in Contract.Originated contract_hash -let random_contract_hash = QCheck2.Gen.oneofl contract_hashes - let gen_counters = let open QCheck2.Gen in let+ i = nat in @@ -376,10 +382,9 @@ let generate_op (gen_op : 'kind contents QCheck2.Gen.t) : 'kind operation QCheck2.Gen.t = let open QCheck2.Gen in let* op = gen_op in - let* signature = random_signature in + let* signature = option ~ratio:0.9 random_signature in let+ shell = random_shell in let contents = Single op in - let signature = Some signature in let protocol_data = {contents; signature} in wrap_operation shell protocol_data @@ -437,7 +442,7 @@ let generate_double_baking = let generate_activate_account = let open QCheck2.Gen in let* activation_code = random_code in - let+ id = random_pkh in + let+ id = random_tz1 in let id = match id with | Tezos_crypto.Signature.Ed25519 pkh -> pkh diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_consensus_key.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_consensus_key.ml index 0401f4f2161c28477470df3490f8f0591d743936..336097c14e35f7da8e9dcef8644afa4a49bc95ca 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_consensus_key.ml @@ -176,6 +176,37 @@ let test_drain_empty_delegate ~exclude_ck () = "Drain delegate without enough balance for allocation burn or drain \ fees") +let test_tz4_consensus_key () = + Context.init_with_constants1 constants >>=? fun (genesis, contracts) -> + let account1_pkh = Context.Contract.pkh contracts in + let consensus_account = Account.new_account ~algo:Bls () in + let delegate = account1_pkh in + let consensus_pk = consensus_account.pk in + let consensus_pkh = consensus_account.pkh in + transfer_tokens genesis account1_pkh consensus_pkh Tez.one_mutez + >>=? fun blk' -> + Op.update_consensus_key (B blk') (Contract.Implicit delegate) consensus_pk + >>=? fun operation -> + let tz4_pk = match consensus_pk with Bls pk -> pk | _ -> assert false in + let expect_failure = function + | [ + Environment.Ecoproto_error + (Delegate_consensus_key.Invalid_consensus_key_update_tz4 pk); + ] + when Tezos_crypto.Bls.Public_key.(pk = tz4_pk) -> + return_unit + | err -> + failwith + "Error trace:@,\ + \ %a does not match the \ + [Delegate_consensus_key.Invalid_consensus_key_update_tz4] error" + Error_monad.pp_print_trace + err + in + Incremental.begin_construction blk' >>=? fun inc -> + Incremental.validate_operation ~expect_failure inc operation + >>=? fun (_i : Incremental.t) -> return_unit + let test_endorsement_with_consensus_key () = Context.init_with_constants1 constants >>=? fun (genesis, contracts) -> let account1_pkh = Context.Contract.pkh contracts in @@ -265,6 +296,7 @@ let tests = "test empty drain delegate with ck" `Quick (test_drain_empty_delegate ~exclude_ck:false); + tztest "test tz4 consensus key" `Quick test_tz4_consensus_key; tztest "test endorsement with ck" `Quick diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml index 5ee3598ae88cace66b52a2bf401748171f30e4e3..2aa32630dd84f29ded4fa6fe5e746f1bfae59115 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml @@ -1364,8 +1364,50 @@ let test_registered_self_delegate_key_init_delegation () = Context.Contract.delegate (B b) contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate delegate_pkh >>=? fun () -> return_unit +let test_bls_account_cannot_self_delegate () = + let open Lwt_result_syntax in + let* b, bootstrap = Context.init1 ~consensus_threshold:0 () in + let {Account.pkh = tz4_pkh; pk = tz4_pk; _} = + Account.new_account ~algo:Bls () + in + let tz4_contract = Alpha_context.Contract.Implicit tz4_pkh in + let* operation = + Op.transaction + ~force_reveal:true + (B b) + bootstrap + tz4_contract + (of_int 200_000) + in + let* b = Block.bake ~operation b in + let* operation = Op.revelation (B b) tz4_pk in + let* b = Block.bake ~operation b in + let* operation = Op.delegation (B b) tz4_contract (Some tz4_pkh) in + let* inc = Incremental.begin_construction b in + let tz4_pkh = match tz4_pkh with Bls pkh -> pkh | _ -> assert false in + let expect_failure = function + | [ + Environment.Ecoproto_error + (Contract_delegate_storage.Forbidden_tz4_delegate pkh); + ] + when Tezos_crypto.Bls.Public_key_hash.(pkh = tz4_pkh) -> + return_unit + | err -> + failwith + "Error trace:@,\ + %a does not match the \ + [Contract_delegate_storage.Forbidden_tz4_delegate] error" + Error_monad.pp_print_trace + err + in + let* (_i : Incremental.t) = + Incremental.validate_operation ~expect_failure inc operation + in + return_unit + let tests_delegate_registration = [ + Tztest.tztest "TEST" `Quick test_bls_account_cannot_self_delegate; (*** unregistered delegate key: no self-delegation ***) (* no token transfer, no self-delegation *) Tztest.tztest diff --git a/src/proto_alpha/lib_protocol/test/pbt/dune b/src/proto_alpha/lib_protocol/test/pbt/dune index 749dc5103b288413635a507e7b454574ada5bff4..4c63b4267c7131da0f06b260ea60488eeff1d71a 100644 --- a/src/proto_alpha/lib_protocol/test/pbt/dune +++ b/src/proto_alpha/lib_protocol/test/pbt/dune @@ -18,7 +18,8 @@ test_carbonated_map test_zk_rollup_encoding test_dal_slot_proof - test_compare_operations) + test_compare_operations + test_operation_encoding) (libraries tezos-base tezos-micheline @@ -130,6 +131,11 @@ (package tezos-protocol-alpha-tests) (action (run %{dep:./test_compare_operations.exe}))) +(rule + (alias runtest) + (package tezos-protocol-alpha-tests) + (action (run %{dep:./test_operation_encoding.exe}))) + (rule (alias runtest1) (action (run %{exe:liquidity_baking_pbt.exe}))) (rule (alias runtest1) (action (run %{exe:saturation_fuzzing.exe}))) @@ -161,3 +167,5 @@ (rule (alias runtest3) (action (run %{exe:test_dal_slot_proof.exe}))) (rule (alias runtest3) (action (run %{exe:test_compare_operations.exe}))) + +(rule (alias runtest3) (action (run %{exe:test_operation_encoding.exe}))) diff --git a/src/proto_alpha/lib_protocol/test/pbt/test_operation_encoding.ml b/src/proto_alpha/lib_protocol/test/pbt/test_operation_encoding.ml new file mode 100644 index 0000000000000000000000000000000000000000..deca56bc186e4baa5b49ede22c6b9e32fd512ea0 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/pbt/test_operation_encoding.ml @@ -0,0 +1,66 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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: Protocol Library + Invocation: dune exec \ + src/proto_alpha/lib_protocol/test/pbt/test_operation_encoding.exe + Subject: Encoding for operations +*) + +open Protocol +open QCheck2 +open Lib_test.Qcheck2_helpers + +(** {2 Generators} *) +let generate_operation = + let open Gen in + let+ _kind, (_hash, op) = Operation_generator.generate_operation in + op + +(** {2 Tests} *) + +let test_operation = + let open Alpha_context in + let gen = generate_operation in + let eq {shell = s1; protocol_data = Operation_data d1} + {shell = s2; protocol_data = Operation_data d2} = + let o1 : _ Operation.t = {shell = s1; protocol_data = d1} in + let o2 : _ Operation.t = {shell = s2; protocol_data = d2} in + match Operation.equal o1 o2 with None -> false | Some Eq -> true + in + test_roundtrip + ~count:2000 + ~title:"Operation.t" + ~gen + ~eq + Alpha_context.Operation.encoding + +let () = + let qcheck_wrap = qcheck_wrap ~rand:(Random.State.make_self_init ()) in + Alcotest.run + "Operation_encoding" + [("roundtrip", qcheck_wrap [test_operation])] diff --git a/src/proto_alpha/lib_protocol/test/unit/test_operation_repr.ml b/src/proto_alpha/lib_protocol/test/unit/test_operation_repr.ml index ee36932c65cf8a167a2257bda35d558cd1d42b7a..419f430090de6878cc8a039a19686c893e793fad 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_operation_repr.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_operation_repr.ml @@ -26,7 +26,7 @@ (** Testing ------- Component: Operation_repr - Invocation: dune exec ./src/proto_alpha/lib_protocol/test/unit/main.exe -- test Operation_repr + Invocation: dune exec ./src/proto_alpha/lib_protocol/test/unit/main.exe -- test Operation_repr Dependencies: -- Subject: To test the modules (including the top-level) in operation_repr.ml as individual units, particularly @@ -40,59 +40,115 @@ module Test_operation_repr = struct open Operation_repr let test_of_list_single_case () = - Environment.wrap_tzresult - @@ of_list - [ - Contents - (Manager_operation - { - fee = Obj.magic 0; - operation = Obj.magic 0; - gas_limit = Obj.magic 0; - storage_limit = Obj.magic 0; - counter = Obj.magic 0; - source = Obj.magic 0; - }); - ] - >>?= fun contents_list -> + let op = + Manager_operation + { + fee = Obj.magic 0; + operation = Obj.magic 0; + gas_limit = Obj.magic 0; + storage_limit = Obj.magic 0; + counter = Obj.magic 0; + source = Obj.magic 0; + } + in + Environment.wrap_tzresult @@ of_list [Contents op] >>?= fun contents_list -> match contents_list with - | Contents_list (Single _) -> return_unit + | Contents_list (Single op') when op == Obj.magic op' -> return_unit | _ -> failwith "Unexpected value" let test_of_list_multiple_case () = - Environment.wrap_tzresult - @@ of_list - [ - Contents - (Manager_operation - { - fee = Obj.magic 0; - operation = Obj.magic 0; - gas_limit = Obj.magic 0; - storage_limit = Obj.magic 0; - counter = Obj.magic 0; - source = Obj.magic 0; - }); - Contents - (Manager_operation - { - fee = Obj.magic 0; - operation = Obj.magic 0; - gas_limit = Obj.magic 0; - storage_limit = Obj.magic 0; - counter = Obj.magic 0; - source = Obj.magic 0; - }); - ] + let op1 = + Manager_operation + { + fee = Obj.magic 0; + operation = Obj.magic 0; + gas_limit = Obj.magic 0; + storage_limit = Obj.magic 0; + counter = Obj.magic 0; + source = Obj.magic 0; + } + in + let op2 = + Manager_operation + { + fee = Obj.magic 1; + operation = Obj.magic 0; + gas_limit = Obj.magic 0; + storage_limit = Obj.magic 0; + counter = Obj.magic 0; + source = Obj.magic 0; + } + in + Environment.wrap_tzresult @@ of_list [Contents op1; Contents op2] >>?= fun contents_list -> match contents_list with - | Contents_list (Cons (_, Single _)) -> return_unit + | Contents_list (Cons (op1', Single op2')) + when op1 == Obj.magic op1' && op2 == Obj.magic op2' -> + return_unit | _ -> failwith "Unexpected value" let test_of_list_empty_case () = match of_list [] with | Ok _ -> failwith "of_list of an empty list was expected to fail" | Error _ -> return_unit + + let zero_bls = + match + Tezos_crypto.Signature.(split_signature (Bls Tezos_crypto.Bls.zero)) + with + | {prefix = None; _} -> assert false + | {prefix = Some prefix; suffix} -> + let prefix = + Data_encoding.Binary.to_bytes_exn + Tezos_crypto.Signature.prefix_encoding + prefix + in + (Bytes.cat (Bytes.of_string "\255") prefix, suffix) + + let test_split_signatures error assemble = + let op_bytes = + Data_encoding.Binary.to_bytes_exn + Operation_repr.contents_encoding + (Contents (Failing_noop "")) + in + let prefix, suffix = zero_bls in + let protocol_data_bytes = + Bytes.(concat empty) (assemble op_bytes prefix suffix) + in + match + Data_encoding.Binary.of_bytes + Operation_repr.protocol_data_encoding + protocol_data_bytes + with + | Ok _ -> failwith "Should have failed with %s" error + | Error (User_invariant_guard e) when e = error -> return_unit + | Error e -> + failwith + "Unexpected error: %a instead of %s" + Data_encoding.Binary.pp_read_error + e + error + + let test_only_signature_prefix () = + test_split_signatures "Operation lists should not be empty." + @@ fun _op_bytes prefix suffix -> [prefix; suffix] + + let test_decoding_empty_list () = + test_split_signatures "Operation lists should not be empty." + @@ fun _op_bytes _prefix suffix -> [suffix] + + let test_multiple_signature_prefix () = + test_split_signatures "Signature prefix must appear last" + @@ fun op_bytes prefix suffix -> [op_bytes; prefix; prefix; suffix] + + let test_signature_prefix_not_final () = + test_split_signatures "Signature prefix must appear last" + @@ fun op_bytes prefix suffix -> [prefix; op_bytes; suffix] + + let test_multiple_non_manager () = + test_split_signatures + "Operation list of length > 1 should only contain manager operations." + @@ fun op_bytes prefix suffix -> [op_bytes; op_bytes; prefix; suffix] end let tests = @@ -109,4 +165,24 @@ let tests = "of_list: empty input list" `Quick Test_operation_repr.test_of_list_empty_case; + tztest + "protocol_data_encoding: only signature prefix" + `Quick + Test_operation_repr.test_only_signature_prefix; + tztest + "protocol_data_encoding: empty list" + `Quick + Test_operation_repr.test_decoding_empty_list; + tztest + "protocol_data_encoding: multiple signature prefix" + `Quick + Test_operation_repr.test_multiple_signature_prefix; + tztest + "protocol_data_encoding: signature prefix not final" + `Quick + Test_operation_repr.test_signature_prefix_not_final; + tztest + "protocol_data_encoding: multiple non manager" + `Quick + Test_operation_repr.test_multiple_non_manager; ] diff --git a/src/proto_alpha/lib_protocol/tx_rollup_l2_batch.ml b/src/proto_alpha/lib_protocol/tx_rollup_l2_batch.ml index e37f41c55a34e7695a0479bc40bd8ae759c49d4f..a04ee146e063b3cd07d103ff8e8c7b2d31932029 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_l2_batch.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_l2_batch.ml @@ -96,7 +96,7 @@ module V1 = struct (* --- [operation_content] *) - let compact_operation_content = + let compact_binary_operation_content = let open Data_encoding.Compact in union [ @@ -128,6 +128,103 @@ module V1 = struct Transfer {destination; ticket_hash; qty}); ] + let non_tz4_public_key_hash_encoding = + let open Data_encoding in + conv_with_guard + (fun pkh -> pkh) + (fun (pkh : Signature.public_key_hash) -> + match pkh with + | (Ed25519 _ | Secp256k1 _ | P256 _) as pkh -> Ok pkh + | Bls _ -> + Error + "Withdraw to tz4 address is not supported in the deprecated \ + encoding.") + Signature.Public_key_hash.encoding + + (** JSON encoding for [operation_content] which allows to represent + withdrawals to tz4 accounts. The [deprecated_] variants are kept for + backward compatibility purpose. *) + let json_operation_content = + let open Data_encoding in + let withdraw_deprecated destination = + obj3 + (req "destination" destination) + (req "ticket_hash" Alpha_context.Ticket_hash.encoding) + (req "qty" (Compact.make ~tag_size Tx_rollup_l2_qty.compact_encoding)) + in + let withdraw = + merge_objs + (obj1 (req "direction" (constant "withdraw"))) + (withdraw_deprecated Signature.Public_key_hash.encoding) + in + let transfer_deprecated = + obj3 + (req "destination" (Indexable.encoding Tx_rollup_l2_address.encoding)) + (req "ticket_hash" (Compact.make ~tag_size Ticket_indexable.compact)) + (req "qty" (Compact.make ~tag_size Tx_rollup_l2_qty.compact_encoding)) + in + let transfer = + merge_objs + (obj1 (req "direction" (constant "transfer"))) + transfer_deprecated + in + matching + (function + | Withdraw {destination; ticket_hash; qty} -> + matched 0 withdraw ((), (destination, ticket_hash, qty)) + | Transfer {destination; ticket_hash; qty} -> + matched 1 transfer ((), (destination, ticket_hash, qty))) + [ + case + Json_only + ~title:"withdraw" + withdraw + (function + | Withdraw {destination; ticket_hash; qty} -> + Some ((), (destination, ticket_hash, qty)) + | _ -> None) + (fun ((), (destination, ticket_hash, qty)) -> + Withdraw {destination; ticket_hash; qty}); + case + Json_only + ~title:"transfer" + transfer + (function + | Transfer {destination; ticket_hash; qty} -> + Some ((), (destination, ticket_hash, qty)) + | _ -> None) + (fun ((), (destination, ticket_hash, qty)) -> + Transfer {destination; ticket_hash; qty}); + case + Json_only + ~title:"deprecated_withdraw" + (withdraw_deprecated non_tz4_public_key_hash_encoding) + (function + | Withdraw {destination; ticket_hash; qty} -> + Some (destination, ticket_hash, qty) + | _ -> None) + (fun (destination, ticket_hash, qty) -> + Withdraw {destination; ticket_hash; qty}); + case + Json_only + ~title:"deprecated_transfer" + transfer_deprecated + (function + | Transfer {destination; ticket_hash; qty} -> + Some (destination, ticket_hash, qty) + | _ -> None) + (fun (destination, ticket_hash, qty) -> + Transfer {destination; ticket_hash; qty}); + ] + + let compact_operation_content = + (* This is equivalent to Data_encoding.Compact.splitted *) + Data_encoding.Compact.conv + ~json:json_operation_content + (fun x -> x) + (fun x -> x) + compact_binary_operation_content + let operation_content_encoding = Data_encoding.Compact.make ~tag_size compact_operation_content diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 128c4979492bafcb74a8b752f98eda5c55c1f7fd..df1e74da9c21acf135edb46ee832b1e97e0f8594 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -2393,8 +2393,9 @@ module Manager = struct | Register_global_constant {value} -> let* (_ : Gas.Arith.fp) = consume_decoding_gas remaining_gas value in return_unit - | Delegation _ | Set_deposits_limit _ | Increase_paid_storage _ - | Update_consensus_key _ -> + | Delegation (Some pkh) -> Delegate.check_not_tz4 pkh + | Update_consensus_key pk -> Delegate.Consensus_key.check_not_tz4 pk + | Delegation None | Set_deposits_limit _ | Increase_paid_storage _ -> return_unit | Tx_rollup_origination -> assert_tx_rollup_feature_enabled vi | Tx_rollup_submit_batch {content; _} -> diff --git a/tezt/lib_tezos/constant.ml b/tezt/lib_tezos/constant.ml index 7fd491e5d8ed555af1c83435627ba981f256d1d7..c8e4b49a7cf2837f486e67868666138e54d1c602 100644 --- a/tezt/lib_tezos/constant.ml +++ b/tezt/lib_tezos/constant.ml @@ -112,7 +112,7 @@ let sc_rollup_compressed_state = (** A valid base58 encoded layer-2 account to be used to test transaction and smart contract rollups. *) -let tz4_account : Account.aggregate_key = +let aggregate_tz4_account : Account.aggregate_key = { aggregate_alias = "bls_test_account"; aggregate_public_key_hash = "tz4EECtMxAuJ9UDLaiMZH7G1GCFYUWsj8HZn"; @@ -122,6 +122,15 @@ let tz4_account : Account.aggregate_key = Unencrypted "BLsk1hKAHyGqY9qRbgoSVnjiSmDWpKGjFF3WNQ7BaiaMUA6RMA6Pfq"; } +(** The same as {!aggregate_tz4_account} but for use on layer 1. *) +let tz4_account : Account.key = + { + alias = aggregate_tz4_account.aggregate_alias; + public_key_hash = aggregate_tz4_account.aggregate_public_key_hash; + public_key = aggregate_tz4_account.aggregate_public_key; + secret_key = aggregate_tz4_account.aggregate_secret_key; + } + (** The `echo` kernel that is listed in the “Smart Optimistic Rollups” section of the reference manual. *) let wasm_echo_kernel_boot_sector = diff --git a/tezt/lib_tezos/protocol.ml b/tezt/lib_tezos/protocol.ml index fb9db1709da04c12264622702b9ee6df556acb80..26b16d99ba81a9109e4798870caf13f311cd89b6 100644 --- a/tezt/lib_tezos/protocol.ml +++ b/tezt/lib_tezos/protocol.ml @@ -79,12 +79,19 @@ let encoding_prefix = function type parameter_overrides = (string list * [`None | `Int of int | `String_of_int of int | JSON.u]) list +let default_bootstrap_accounts = + Array.to_list Account.Bootstrap.keys |> List.map @@ fun key -> (key, None) + let write_parameter_file : + ?bootstrap_accounts:(Account.key * int option) list -> ?additional_bootstrap_accounts:(Account.key * int option) list -> base:(string, t * constants option) Either.t -> parameter_overrides -> string Lwt.t = - fun ?(additional_bootstrap_accounts = []) ~base parameter_overrides -> + fun ?(bootstrap_accounts = default_bootstrap_accounts) + ?(additional_bootstrap_accounts = []) + ~base + parameter_overrides -> (* make a copy of the parameters file and update the given constants *) let overriden_parameters = Temp.file "parameters.json" in let original_parameters = @@ -96,6 +103,24 @@ let write_parameter_file : in JSON.parse_file file |> JSON.unannotate in + let parameter_overrides = + if List.mem_assoc ["bootstrap_accounts"] parameter_overrides then + parameter_overrides + else + let bootstrap_accounts = + List.map + (fun ((account : Account.key), default_balance) -> + `A + [ + `String account.public_key; + `String + (string_of_int + (Option.value ~default:4000000000000 default_balance)); + ]) + bootstrap_accounts + in + (["bootstrap_accounts"], `A bootstrap_accounts) :: parameter_overrides + in let parameters = List.fold_left (fun acc (path, value) -> @@ -111,9 +136,9 @@ let write_parameter_file : parameter_overrides in let parameters = - let bootstrap_accounts = ["bootstrap_accounts"] in + let path = ["bootstrap_accounts"] in let existing_accounts = - Ezjsonm.get_list Fun.id (Ezjsonm.find parameters bootstrap_accounts) + Ezjsonm.get_list Fun.id (Ezjsonm.find parameters path) in let additional_bootstrap_accounts = List.map @@ -129,7 +154,7 @@ let write_parameter_file : in Ezjsonm.update parameters - bootstrap_accounts + path (Some (`A (existing_accounts @ additional_bootstrap_accounts))) in JSON.encode_to_file_u overriden_parameters parameters ; diff --git a/tezt/lib_tezos/protocol.mli b/tezt/lib_tezos/protocol.mli index b42795ef0f41a3be8a9b7c1d6889dfd632beaeba..dbc0017027eb88341429033abd86223666339085 100644 --- a/tezt/lib_tezos/protocol.mli +++ b/tezt/lib_tezos/protocol.mli @@ -105,11 +105,14 @@ type parameter_overrides = the default parameters of the given protocol are the base parameters. Then, the base parameters are tweaked with: + - [bootstrap_accounts], when given these accounts are used instead of + [Account.Bootstrap.keys] - [parameters_overrides] - [additional_bootstrap_accounts] (with their optional default balance) are added to the list of bootstrap accounts of the protocol. *) val write_parameter_file : + ?bootstrap_accounts:(Account.key * int option) list -> ?additional_bootstrap_accounts:(Account.key * int option) list -> base:(string, t * constants option) Either.t -> parameter_overrides -> diff --git a/tezt/tests/baker_test.ml b/tezt/tests/baker_test.ml index 8de7b12f872590599d6f41ebb438c7c9da4f77cb..f360c8a0a14c9531978b6986df0d948d95ed8df8 100644 --- a/tezt/tests/baker_test.ml +++ b/tezt/tests/baker_test.ml @@ -30,10 +30,21 @@ Subject: Run the baker while performing a lot of transfers *) -let baker_test ~title ~tags = - Protocol.register_test ~__FILE__ ~title ~tags @@ fun protocol -> +let baker_test protocol ~keys = + let* parameter_file = + Protocol.write_parameter_file + ~bootstrap_accounts:(List.map (fun k -> (k, None)) keys) + ~base:(Right (protocol, None)) + [] + in let* node, client = - Client.init_with_protocol `Client ~protocol ~timestamp:Now () + Client.init_with_protocol + ~keys:(Constant.activator :: keys) + `Client + ~protocol + ~timestamp:Now + ~parameter_file + () in let level_2_promise = Node.wait_for_level node 2 in let level_3_promise = Node.wait_for_level node 3 in @@ -44,7 +55,15 @@ let baker_test ~title ~tags = Log.info "New head arrive level 2" ; let* _ = level_3_promise in Log.info "New head arrive level 3" ; - Lwt.return_unit + Lwt.return client + +let baker_simple_test = + Protocol.register_test ~__FILE__ ~title:"baker test" ~tags:["node"; "baker"] + @@ fun protocol -> + let* _ = + baker_test protocol ~keys:(Account.Bootstrap.keys |> Array.to_list) + in + unit let baker_stresstest = Protocol.register_test @@ -61,6 +80,48 @@ let baker_stresstest = let* () = Client.stresstest ~tps:25 ~transfers:100 client in Lwt.return_unit +let baker_bls_test = + Protocol.register_test + ~__FILE__ + ~title:"No BLS baker test" + ~tags:["node"; "baker"; "bls"] + @@ fun protocol -> + let* client0 = Client.init_mockup ~protocol () in + Log.info "Generate BLS keys for client" ; + let* keys = + Lwt_list.map_s + (fun i -> + Client.gen_and_show_keys + ~alias:(sf "bootstrap_bls_%d" i) + ~sig_alg:"bls" + client0) + (Base.range 1 5) + in + let* parameter_file = + Protocol.write_parameter_file + ~bootstrap_accounts:(List.map (fun k -> (k, None)) keys) + ~base:(Right (protocol, None)) + [] + in + let* _node, client = + Client.init_with_node ~keys:(Constant.activator :: keys) `Client () + in + let activate_process = + Client.spawn_activate_protocol + ~protocol + ~timestamp:Now + ~parameter_file + client + in + let msg = + match protocol with + | Kathmandu | Lima -> rex "Invalid protocol_parameters" + | Alpha -> + rex "The delegate tz4.*\\w is forbidden as it is a BLS public key hash" + in + Process.check_error activate_process ~exit_code:1 ~msg + let register ~protocols = - let () = baker_test ~title:"baker test" ~tags:["node"; "baker"] protocols in - baker_stresstest protocols + baker_simple_test protocols ; + baker_stresstest protocols ; + baker_bls_test protocols diff --git a/tezt/tests/client_commands.ml b/tezt/tests/client_commands.ml index 42b2e07fd297a9ffa8877c1948096aa22f135f3b..f80d76b29b9bc5b0f41c8d6babc4374e0e3ffd2c 100644 --- a/tezt/tests/client_commands.ml +++ b/tezt/tests/client_commands.ml @@ -47,6 +47,56 @@ module Helpers = struct in let* () = Client.bake_for_and_wait client in return contract + + let get_balance pkh client = + RPC.Client.call client + @@ RPC.get_chain_block_context_contract_balance ~id:pkh () + + let supported_signature_schemes = function + | Protocol.Alpha -> ["ed25519"; "secp256k1"; "p256"; "bls"] + | Lima | Kathmandu -> ["ed25519"; "secp256k1"; "p256"] + + let airdrop_and_reveal client accounts = + Log.info "Airdrop 1000tz to each account" ; + let batches = + Ezjsonm.list + (fun account -> + `O + [ + ("destination", `String account.Account.public_key_hash); + ("amount", `String "1000"); + ]) + accounts + in + let*! () = + Client.multiple_transfers + ~giver:Constant.bootstrap1.public_key_hash + ~json_batch:(Ezjsonm.to_string batches) + ~burn_cap:Tez.one + client + in + let* () = Client.bake_for_and_wait client in + let* balances = + Lwt_list.map_p + (fun account -> + let* balance = get_balance account.Account.public_key_hash client in + return (account.alias, balance)) + accounts + in + List.iter + (fun (alias, balance) -> + Check.((Tez.to_string balance = "1000") string) + ~error_msg:(sf "%s has balance %%L instead of %%R" alias)) + balances ; + Log.info "Revealing public keys" ; + let* () = + Lwt_list.iter_p + (fun account -> + let*! () = Client.reveal ~src:account.Account.alias client in + unit) + accounts + in + Client.bake_for_and_wait client end module Simulation = struct @@ -178,9 +228,7 @@ module Simulation = struct end module Transfer = struct - let get_balance pkh client = - RPC.Client.call client - @@ RPC.get_chain_block_context_contract_balance ~id:pkh () + open Helpers let alias_pkh_destination = Protocol.register_test @@ -268,9 +316,179 @@ module Transfer = struct ~error_msg:"Balance of victim should be %R but is %L." ; unit - let register protocol = - alias_pkh_destination protocol ; - alias_pkh_source protocol + let transfer_tz4 = + Protocol.register_test + ~__FILE__ + ~title:"Transfer from and to accounts" + ~tags:["client"; "transfer"; "bls"; "tz4"] + @@ fun protocol -> + let* _node, client = Client.init_with_protocol `Client ~protocol () in + Log.info "Generating new accounts" ; + let gen_accounts i = + Lwt_list.map_s + (fun sig_alg -> + Client.gen_and_show_keys + ~alias:(sf "account_%s_%d" sig_alg i) + ~sig_alg + client) + (supported_signature_schemes protocol) + in + let* accounts1 = gen_accounts 1 in + let* accounts2 = gen_accounts 2 in + let accounts = accounts1 @ accounts2 in + let* () = airdrop_and_reveal client accounts in + let test_transfer (from : Account.key) (dest : Account.key) = + Log.info "Test transfer from %s to %s" from.alias dest.alias ; + let amount = Tez.of_int 10 in + let fee = Tez.of_int 1 in + let* balance_from0 = get_balance from.public_key_hash client + and* balance_dest0 = get_balance dest.public_key_hash client in + let* () = + Client.transfer + ~amount + ~giver:from.public_key_hash + ~receiver:dest.public_key_hash + ~fee + client + in + let* () = Client.bake_for_and_wait client in + let* balance_from = get_balance from.public_key_hash client + and* balance_dest = get_balance dest.public_key_hash client in + let expected_balance_from = Tez.(balance_from0 - (amount + fee)) in + let expected_balance_dest = Tez.(balance_dest0 + amount) in + Check.( + (Tez.to_string balance_from = Tez.to_string expected_balance_from) + string) + ~error_msg:(sf "Sender %s has balance %%L instead of %%R" from.alias) ; + Check.( + (Tez.to_string balance_dest = Tez.to_string expected_balance_dest) + string) + ~error_msg:(sf "Receiver %s has balance %%L instead of %%R" dest.alias) ; + unit + in + Lwt_list.iter_s + (fun from -> + Lwt_list.iter_s (fun dest -> test_transfer from dest) accounts2) + accounts1 + + let batch_transfers_tz4 = + Protocol.register_test + ~__FILE__ + ~title:"Batch transfers" + ~tags:["client"; "batch"; "transfer"; "bls"; "tz4"] + @@ fun protocol -> + let* _node, client = Client.init_with_protocol `Client ~protocol () in + Log.info "Generating new accounts" ; + let gen_accounts i = + Lwt_list.map_s + (fun sig_alg -> + Client.gen_and_show_keys + ~alias:(sf "account_%s_%d" sig_alg i) + ~sig_alg + client) + (supported_signature_schemes protocol) + in + let* accounts = gen_accounts 1 in + let* dests = gen_accounts 2 in + let* () = airdrop_and_reveal client (accounts @ dests) in + let test_batch_transfer (from : Account.key) = + Log.info "Test batch transfer from %s" from.alias ; + let* balance_from0 = get_balance from.public_key_hash client + and* balance_dests0 = + Lwt_list.map_p + (fun dest -> get_balance dest.Account.public_key_hash client) + dests + in + let amount = Tez.of_int 10 in + let fee = Tez.of_int 1 in + let batches = + Ezjsonm.list + (fun account -> + `O + [ + ("destination", `String account.Account.public_key_hash); + ("amount", `String (Tez.to_string amount)); + ("fee", `String (Tez.to_string fee)); + ]) + dests + in + let*! () = + Client.multiple_transfers + ~giver:from.alias + ~json_batch:(Ezjsonm.to_string batches) + ~fee_cap:(Tez.of_int 10) + client + in + let* () = Client.bake_for_and_wait client in + let* balance_from = get_balance from.public_key_hash client + and* balance_dests = + Lwt_list.map_p + (fun dest -> get_balance dest.Account.public_key_hash client) + dests + in + let expected_balance_from = + let total = + Tez.( + mutez_int64 (amount + fee) + |> Int64.(mul @@ of_int @@ List.length dests) + |> of_mutez_int64) + in + Tez.(balance_from0 - total) + in + let expected_balance_dests = + List.map (fun b -> Tez.(b + amount)) balance_dests0 + in + Check.( + (Tez.to_string balance_from = Tez.to_string expected_balance_from) + string) + ~error_msg:(sf "Sender %s has balance %%L instead of %%R" from.alias) ; + List.iter2 + (fun balance_dest expected_balance_dest -> + Check.( + (Tez.to_string balance_dest = Tez.to_string expected_balance_dest) + string) + ~error_msg:"Receiver has balance %L instead of %R") + balance_dests + expected_balance_dests ; + unit + in + Lwt_list.iter_s test_batch_transfer accounts + + let forbidden_set_delegate_tz4 = + Protocol.register_test + ~__FILE__ + ~title:"Set delegate forbidden on tz4" + ~tags:["client"; "set_delegate"; "bls"; "tz4"] + @@ fun protocol -> + let* _node, client = Client.init_with_protocol `Client ~protocol () in + let* () = + match protocol with + | Kathmandu | Lima -> unit + | Alpha -> + let* () = Client.import_secret_key client Constant.tz4_account in + airdrop_and_reveal client [Constant.tz4_account] + in + let*? set_delegate_process = + Client.set_delegate + client + ~src:Constant.tz4_account.public_key_hash + ~delegate:Constant.tz4_account.public_key_hash + in + let msg = + match protocol with + | Kathmandu | Lima -> rex "Invalid contract notation \"tz4.*\"" + | Alpha -> + rex + "The delegate tz4.*\\w is forbidden as it is a BLS public key hash" + in + Process.check_error set_delegate_process ~exit_code:1 ~msg + + let register protocols = + alias_pkh_destination protocols ; + alias_pkh_source protocols ; + transfer_tz4 protocols ; + batch_transfers_tz4 protocols ; + forbidden_set_delegate_tz4 protocols end module Dry_run = struct @@ -372,7 +590,64 @@ module Dry_run = struct let register protocols = test_gas_consumed protocols end +module Signatures = struct + open Helpers + + let test_check_signature = + Protocol.register_test + ~__FILE__ + ~title:"Test client signatures and on chain check" + ~tags:["client"; "signature"; "check"; "bls"] + @@ fun protocol -> + let* _node, client = Client.init_with_protocol `Client ~protocol () in + let prg = "file:./tezt/tests/contracts/proto_alpha/check_signature.tz" in + let contract = "check_sig_contract" in + let* _hash = + Client.originate_contract + ~alias:contract + ~amount:Tez.zero + ~src:Constant.bootstrap2.alias + ~burn_cap:(Tez.of_int 10) + ~prg + client + in + Log.info "Generating new accounts" ; + let* accounts = + Lwt_list.map_s + (fun sig_alg -> + Client.gen_and_show_keys + ~alias:(sf "account_%s" sig_alg) + ~sig_alg + client) + (supported_signature_schemes protocol) + in + let* () = airdrop_and_reveal client accounts in + let test (account : Account.key) = + let msg = "0x" ^ Hex.show (Hex.of_string "Some nerdy quote") in + let* signature = + Client.sign_bytes ~signer:account.alias ~data:msg client + in + Client.transfer + client + ~amount:Tez.zero + ~giver:account.public_key_hash + ~receiver:contract + ~arg:(sf "Pair %S %S %s" account.public_key signature msg) + in + let* () = Lwt_list.iter_s test accounts in + let* () = Client.bake_for_and_wait client in + let* block = RPC.Client.call client @@ RPC.get_chain_block () in + let ops = JSON.(block |-> "operations" |=> 3 |> as_list) in + Check.( + (List.length ops = List.length (supported_signature_schemes protocol)) int) + ~error_msg:"Block contains %L operations but should have %R" ; + unit + + let register protocols = test_check_signature protocols +end + let register ~protocols = Simulation.register protocols ; Transfer.register protocols ; - Dry_run.register protocols + Dry_run.register protocols ; + Signatures.register protocols diff --git a/tezt/tests/client_keys.ml b/tezt/tests/client_keys.ml index 97fb3a5cb627d6396369ed9e191ad7fc3cedff0c..8101ec8fc4764086e231ec53dc2d8e435db7abb5 100644 --- a/tezt/tests/client_keys.ml +++ b/tezt/tests/client_keys.ml @@ -30,103 +30,190 @@ Subject: Checks client wallet commands *) -let check_shown_account ~__LOC__ (expected : Account.aggregate_key) - (shown : Account.aggregate_key) = - if expected.aggregate_public_key_hash <> shown.aggregate_public_key_hash then - Test.fail - ~__LOC__ - "Expecting %s, got %s as public key hash from the client " - expected.aggregate_public_key_hash - shown.aggregate_public_key_hash - else if expected.aggregate_public_key <> shown.aggregate_public_key then - Test.fail - ~__LOC__ - "Expecting %s, got %s as public key from the client " - expected.aggregate_public_key - shown.aggregate_public_key - else if expected.aggregate_secret_key <> shown.aggregate_secret_key then - let (Unencrypted sk) = shown.aggregate_secret_key in - let (Unencrypted expected_sk) = shown.aggregate_secret_key in - Test.fail - ~__LOC__ - "Expecting %s, got %s as secret key from the client " - expected_sk - sk - else return () +module BLS_aggregate_wallet = struct + let check_shown_account ~__LOC__ (expected : Account.aggregate_key) + (shown : Account.aggregate_key) = + if expected.aggregate_public_key_hash <> shown.aggregate_public_key_hash + then + Test.fail + ~__LOC__ + "Expecting %s, got %s as public key hash from the client " + expected.aggregate_public_key_hash + shown.aggregate_public_key_hash + else if expected.aggregate_public_key <> shown.aggregate_public_key then + Test.fail + ~__LOC__ + "Expecting %s, got %s as public key from the client " + expected.aggregate_public_key + shown.aggregate_public_key + else if expected.aggregate_secret_key <> shown.aggregate_secret_key then + let (Unencrypted sk) = shown.aggregate_secret_key in + let (Unencrypted expected_sk) = shown.aggregate_secret_key in + Test.fail + ~__LOC__ + "Expecting %s, got %s as secret key from the client " + expected_sk + sk + else return () -let test_bls_import_secret_key () = - Test.register - ~__FILE__ - ~tags:["client"; "keys"] - ~title:"Import BLS secret key" - (fun () -> - let* client = Client.init () in - let* () = Client.bls_import_secret_key Constant.tz4_account client in - let* shown_account = - Client.bls_show_address - ~alias:Constant.tz4_account.Account.aggregate_alias - client - in - check_shown_account ~__LOC__ Constant.tz4_account shown_account) + let test_bls_import_secret_key () = + Test.register + ~__FILE__ + ~tags:["aggregate"; "client"; "keys"] + ~title:"Import BLS secret key in aggregate wallet" + (fun () -> + let* client = Client.init () in + let* () = + Client.bls_import_secret_key Constant.aggregate_tz4_account client + in + let* shown_account = + Client.bls_show_address + ~alias:Constant.aggregate_tz4_account.Account.aggregate_alias + client + in + check_shown_account + ~__LOC__ + Constant.aggregate_tz4_account + shown_account) -let test_bls_show_address () = - Test.register - ~__FILE__ - ~tags:["client"; "keys"] - ~title:"Shows the address of a registered BLS account" - (fun () -> - let* client = Client.init () in - let* () = Client.bls_import_secret_key Constant.tz4_account client in - let* shown_account = - Client.bls_show_address - ~alias:Constant.tz4_account.Account.aggregate_alias - client - in - check_shown_account ~__LOC__ Constant.tz4_account shown_account) + let test_bls_show_address () = + Test.register + ~__FILE__ + ~tags:["aggregate"; "client"; "keys"] + ~title:"Shows the address of a registered BLS account in aggregate wallet" + (fun () -> + let* client = Client.init () in + let* () = + Client.bls_import_secret_key Constant.aggregate_tz4_account client + in + let* shown_account = + Client.bls_show_address + ~alias:Constant.aggregate_tz4_account.Account.aggregate_alias + client + in + check_shown_account + ~__LOC__ + Constant.aggregate_tz4_account + shown_account) -let test_bls_gen_keys () = - Test.register - ~__FILE__ - ~tags:["client"; "keys"] - ~title:"Generates new tz4 keys" - (fun () -> - let* client = Client.init () in - let* alias = Client.bls_gen_keys client in - let* _account = Client.bls_show_address ~alias client in - return ()) + let test_bls_gen_keys () = + Test.register + ~__FILE__ + ~tags:["aggregate"; "client"; "keys"] + ~title:"Generates new tz4 keys in aggregate wallet" + (fun () -> + let* client = Client.init () in + let* alias = Client.bls_gen_keys client in + let* _account = Client.bls_show_address ~alias client in + return ()) -let test_bls_list_keys () = - Test.register - ~__FILE__ - ~tags:["client"; "keys"] - ~title:"Lists known BLS aliases in the client" - (fun () -> - let* client = Client.init () in - let Account.{aggregate_alias; aggregate_public_key_hash; _} = - Constant.tz4_account - in - let* () = Client.bls_import_secret_key Constant.tz4_account client in - let* maybe_keys = Client.bls_list_keys client in - let expected_keys = [(aggregate_alias, aggregate_public_key_hash)] in - if List.equal ( = ) expected_keys maybe_keys then return () - else - let pp ppf l = - Format.pp_print_list - ~pp_sep:(fun ppf () -> Format.fprintf ppf "\n") - (fun ppf (a, k) -> Format.fprintf ppf "%s: %s" a k) - ppf - l + let test_bls_list_keys () = + Test.register + ~__FILE__ + ~tags:["aggregate"; "client"; "keys"] + ~title:"Lists known BLS aliases in the client's aggregate wallet" + (fun () -> + let* client = Client.init () in + let Account.{aggregate_alias; aggregate_public_key_hash; _} = + Constant.aggregate_tz4_account in - Test.fail - ~__LOC__ - "Expecting\n@[%a@]\ngot\n@[%a@]\nas keys from the client " - pp - expected_keys - pp - maybe_keys) + let* () = + Client.bls_import_secret_key Constant.aggregate_tz4_account client + in + let* maybe_keys = Client.bls_list_keys client in + let expected_keys = [(aggregate_alias, aggregate_public_key_hash)] in + if List.equal ( = ) expected_keys maybe_keys then return () + else + let pp ppf l = + Format.pp_print_list + ~pp_sep:(fun ppf () -> Format.fprintf ppf "\n") + (fun ppf (a, k) -> Format.fprintf ppf "%s: %s" a k) + ppf + l + in + Test.fail + ~__LOC__ + "Expecting\n@[%a@]\ngot\n@[%a@]\nas keys from the client " + pp + expected_keys + pp + maybe_keys) + + let register_protocol_independent () = + test_bls_import_secret_key () ; + test_bls_show_address () ; + test_bls_gen_keys () ; + test_bls_list_keys () +end + +module BLS_normal_wallet = struct + let check_shown_account ~__LOC__ (expected : Account.key) + (shown : Account.key) = + if expected.public_key_hash <> shown.public_key_hash then + Test.fail + ~__LOC__ + "Expecting %s, got %s as public key hash from the client " + expected.public_key_hash + shown.public_key_hash + else if expected.public_key <> shown.public_key then + Test.fail + ~__LOC__ + "Expecting %s, got %s as public key from the client " + expected.public_key + shown.public_key + else if expected.secret_key <> shown.secret_key then + let (Unencrypted sk) = shown.secret_key in + let (Unencrypted expected_sk) = shown.secret_key in + Test.fail + ~__LOC__ + "Expecting %s, got %s as secret key from the client " + expected_sk + sk + else return () + + let test_bls_import_secret_key () = + Test.register + ~__FILE__ + ~tags:["bls"; "client"; "keys"] + ~title:"Import BLS secret key" + (fun () -> + let* client = Client.init () in + let* () = Client.import_secret_key client Constant.tz4_account in + let* shown_account = + Client.show_address ~alias:Constant.tz4_account.Account.alias client + in + check_shown_account ~__LOC__ Constant.tz4_account shown_account) + + let test_bls_show_address () = + Test.register + ~__FILE__ + ~tags:["bls"; "client"; "keys"] + ~title:"Shows the address of a registered BLS account" + (fun () -> + let* client = Client.init () in + let* () = Client.import_secret_key client Constant.tz4_account in + let* shown_account = + Client.show_address ~alias:Constant.tz4_account.Account.alias client + in + check_shown_account ~__LOC__ Constant.tz4_account shown_account) + + let test_bls_gen_keys () = + Test.register + ~__FILE__ + ~tags:["bls"; "client"; "keys"] + ~title:"Generates new tz4 keys" + (fun () -> + let* client = Client.init () in + let* alias = Client.gen_keys ~sig_alg:"bls" client in + let* _account = Client.show_address ~alias client in + return ()) + + let register_protocol_independent () = + test_bls_import_secret_key () ; + test_bls_show_address () ; + test_bls_gen_keys () +end let register_protocol_independent () = - test_bls_import_secret_key () ; - test_bls_show_address () ; - test_bls_gen_keys () ; - test_bls_list_keys () + BLS_aggregate_wallet.register_protocol_independent () ; + BLS_normal_wallet.register_protocol_independent () diff --git a/tezt/tests/consensus_key.ml b/tezt/tests/consensus_key.ml index 23c1eba1c4b59a70ae4845ba56a81ebfdb7bc92c..7392fdaa8abf0f9d06fa95f6bd06fbc2a5bc09fc 100644 --- a/tezt/tests/consensus_key.ml +++ b/tezt/tests/consensus_key.ml @@ -90,6 +90,7 @@ let test_update_consensus_key = let* key_a = Client.gen_and_show_keys client in let* key_b = Client.gen_and_show_keys client in let* key_c = Client.gen_and_show_keys client in + let* key_bls = Client.gen_and_show_keys ~sig_alg:"bls" client in let* destination = Client.gen_and_show_keys client in let* () = @@ -145,6 +146,14 @@ let test_update_consensus_key = ~pk:Constant.bootstrap1.alias client in + Log.info "Invalid update: changing the consensus key to a BLS key." ; + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap1.alias + ~pk:key_bls.alias + client + in Log.info "Trying a valid consensus key update." ; let* () = diff --git a/tezt/tests/contracts/proto_alpha/check_signature.tz b/tezt/tests/contracts/proto_alpha/check_signature.tz new file mode 100644 index 0000000000000000000000000000000000000000..fb23f14dd789f9aab19a48a31f750dcc6d4bdbba --- /dev/null +++ b/tezt/tests/contracts/proto_alpha/check_signature.tz @@ -0,0 +1,20 @@ +parameter (pair key signature bytes) ; +storage unit ; +code { CAR ; + UNPAIR ; + SWAP ; + UNPAIR ; + DUP 3 ; + HASH_KEY ; + IMPLICIT_ACCOUNT ; + SENDER ; + SWAP ; + ADDRESS ; + COMPARE ; + EQ ; + IF {} { PUSH string "invalid signer" ; FAILWITH } ; + DIG 2 ; + CHECK_SIGNATURE ; + IF { UNIT } { PUSH string "invalid signature" ; FAILWITH } ; + NIL operation ; + PAIR } \ No newline at end of file diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 6a7d00e3b1169a06159e60b8d16e0a4b114e74da..3c07ee4ee674c00a6f27f31f52b9cd30cbd7eda1 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -1488,14 +1488,12 @@ let mode_publish mode publishes sc_rollup_node sc_rollup_client sc_rollup node and* () = Client.Admin.trust_address client' ~peer:node in let* () = Client.Admin.connect_address client ~peer:node' in let* () = Sc_rollup_node.run sc_rollup_node [] in - let level = Node.get_level node in let* levels_to_commitment = get_sc_rollup_commitment_period_in_blocks client in let* () = send_messages levels_to_commitment client in - let* level = - Sc_rollup_node.wait_for_level sc_rollup_node (level + levels_to_commitment) - in + let level = Node.get_level node in + let* _ = Sc_rollup_node.wait_for_level sc_rollup_node level in Log.info "Starting other rollup node." ; let purposes = ["publish"; "cement"; "add_messages"] in let operators = @@ -1521,9 +1519,7 @@ let mode_publish mode publishes sc_rollup_node sc_rollup_client sc_rollup node let* _level = Sc_rollup_node.wait_for_level sc_rollup_other_node level in Log.info "Other rollup node synchronized." ; let* () = send_messages levels_to_commitment client in - let* level = - Sc_rollup_node.wait_for_level sc_rollup_node (level + levels_to_commitment) - in + let level = Node.get_level node in let* _ = Sc_rollup_node.wait_for_level sc_rollup_node level and* _ = Sc_rollup_node.wait_for_level sc_rollup_other_node level in Log.info "Both rollup nodes have reached level %d." level ; @@ -2563,7 +2559,7 @@ let test_scenario_client_with_account ~account ~variant ~kind f = ------------------------------------------------------------------- *) let test_rollup_client_show_address ~kind = - let account = Constant.tz4_account in + let account = Constant.aggregate_tz4_account in test_scenario_client_with_account ~account ~kind ~variant:"show address" @@ fun rollup_client -> let* shown_account = @@ -2589,7 +2585,7 @@ let test_rollup_client_show_address ~kind = ---------------------------------------- *) let test_rollup_client_generate_keys ~kind = - let account = Constant.tz4_account in + let account = Constant.aggregate_tz4_account in test_scenario_client_with_account ~account ~kind ~variant:"gen address" @@ fun rollup_client -> let alias = "test_key" in @@ -2601,7 +2597,7 @@ let test_rollup_client_generate_keys ~kind = ------------------------------------ *) let test_rollup_client_list_keys ~kind = - let account = Constant.tz4_account in + let account = Constant.aggregate_tz4_account in test_scenario_client_with_account ~account ~kind ~variant:"list alias" @@ fun rollup_client -> let* maybe_keys = Sc_rollup_client.list_keys rollup_client in diff --git a/tezt/tests/signer_test.ml b/tezt/tests/signer_test.ml index 9f7ec6cf562fb71ccbdb6623f0ba9c3b5d2aff1b..fb0c6425251ccc9c4613fe3b3dffc83524c6d86f 100644 --- a/tezt/tests/signer_test.ml +++ b/tezt/tests/signer_test.ml @@ -31,16 +31,22 @@ *) (* same as `baker_test`, `baker_test.ml` but using the signer *) -let signer_simple_test ~title ~tags ~keys = - Protocol.register_test ~__FILE__ ~title ~tags @@ fun protocol -> +let signer_test protocol ~keys = (* init the signer and import all the bootstrap_keys *) let* signer = Signer.init ~keys () in + let* parameter_file = + Protocol.write_parameter_file + ~bootstrap_accounts:(List.map (fun k -> (k, None)) keys) + ~base:(Right (protocol, None)) + [] + in let* node, client = Client.init_with_protocol ~keys:[Constant.activator] `Client ~protocol ~timestamp:Now + ~parameter_file () in let* _ = @@ -57,11 +63,60 @@ let signer_simple_test ~title ~tags ~keys = Log.info "New head arrive level 2" ; let* _ = level_3_promise in Log.info "New head arrive level 3" ; - Lwt.return_unit + return client -let register ~protocols = - signer_simple_test +let signer_simple_test = + Protocol.register_test + ~__FILE__ ~title:"signer test" - ~tags:["node"; "baker"; "signer"] - ~keys:(Account.Bootstrap.keys |> Array.to_list) - protocols + ~tags:["node"; "baker"; "signer"; "tz1"] + @@ fun protocol -> + let* _ = + signer_test protocol ~keys:(Account.Bootstrap.keys |> Array.to_list) + in + unit + +let signer_bls_test = + Protocol.register_test + ~__FILE__ + ~title:"BLS signer test" + ~tags:["node"; "baker"; "signer"; "bls"] + @@ fun protocol -> + let* _node, client = Client.init_with_protocol `Client ~protocol () in + let* signer = Signer.init ~keys:[Constant.tz4_account] () in + let* () = + let uri = Signer.uri signer in + Client.import_signer_key client Constant.tz4_account uri + in + let* () = + Client.transfer + ~amount:(Tez.of_int 10) + ~giver:Constant.bootstrap1.public_key_hash + ~receiver:Constant.tz4_account.public_key_hash + ~burn_cap:(Tez.of_int 1) + client + in + let* () = Client.bake_for_and_wait client in + let get_balance_tz4 client = + RPC.Client.call client + @@ RPC.get_chain_block_context_contract_balance + ~id:Constant.tz4_account.public_key_hash + () + in + let* balance_0 = get_balance_tz4 client in + let* () = + Client.transfer + ~amount:(Tez.of_int 5) + ~giver:Constant.tz4_account.public_key_hash + ~receiver:Constant.bootstrap1.public_key_hash + client + in + let* () = Client.bake_for_and_wait client in + let* balance_1 = get_balance_tz4 client in + Check.((Tez.mutez_int64 balance_0 > Tez.mutez_int64 balance_1) int64) + ~error_msg:"Tz4 sender %s has decreased balance after transfer" ; + unit + +let register ~protocols = + signer_simple_test protocols ; + signer_bls_test [Alpha] diff --git a/tezt/tests/tx_rollup_l2_node.ml b/tezt/tests/tx_rollup_l2_node.ml index f435e997a82af305d866f0746a886714e1210674..fc365a4e4e451666ac8d9197523ef650236b5611 100644 --- a/tezt/tests/tx_rollup_l2_node.ml +++ b/tezt/tests/tx_rollup_l2_node.ml @@ -88,11 +88,12 @@ let check_inbox_success (inbox : Tx_rollup_node.Inbox.t) = match result |->? "deposit_result" with | None -> (* Not a deposit, must be a batch *) - let results = - JSON.( - result |->? "batch_v1_result" |> Option.get |-> "results" - |> as_list) + let batch_result = + match result |->? "batch_v1_result" with + | Some r -> r + | None -> JSON.(result |-> "batch_v2_result") in + let results = JSON.(batch_result |-> "results" |> as_list) in List.iteri (fun j tr_json -> match JSON.(tr_json |=> 1 |> as_string_opt) with @@ -2234,8 +2235,9 @@ let test_tickets_context = ~error_msg:"Ticket is %L but expected %R" ; unit) -let test_round_trip ~title ?before_init ~originator ~operator ~batch_signer - ~finalize_commitment_signer ~dispatch_withdrawals_signer () = +let test_round_trip ~title ?before_init + ?(withdraw_dest = Constant.bootstrap2.public_key_hash) ~originator ~operator + ~batch_signer ~finalize_commitment_signer ~dispatch_withdrawals_signer () = Protocol.register_test ~__FILE__ ~title @@ -2334,7 +2336,7 @@ let test_round_trip ~title ?before_init ~originator ~operator ~batch_signer craft_withdraw_and_sign tx_client ~signer:bls_key_2 - ~dest:Constant.bootstrap2.public_key_hash + ~dest:withdraw_dest ~ticket:ticket_id ~qty:5L in @@ -2343,7 +2345,7 @@ let test_round_trip ~title ?before_init ~originator ~operator ~batch_signer craft_withdraw_and_sign tx_client ~signer:bls_key_1 - ~dest:Constant.bootstrap2.public_key_hash + ~dest:withdraw_dest ~ticket:ticket_id ~qty:10L in @@ -2406,7 +2408,7 @@ let test_round_trip ~title ?before_init ~originator ~operator ~batch_signer let*! () = Client.transfer_tickets ~qty:15L - ~src:Constant.bootstrap2.public_key_hash + ~src:withdraw_dest ~destination:withdraw_contract ~entrypoint:"default" ~contents:{|"toru"|} @@ -2428,6 +2430,30 @@ let test_withdrawals = ~dispatch_withdrawals_signer:Constant.bootstrap3.public_key_hash () +let test_withdrawals_tz4 = + let before_init client = + let* () = Client.import_secret_key client Constant.tz4_account in + let* () = + Client.transfer + ~amount:(Tez.of_int 1_000) + ~giver:Constant.bootstrap1.public_key_hash + ~receiver:Constant.tz4_account.alias + ~burn_cap:Tez.one + client + in + Client.bake_for_and_wait client + in + test_round_trip + ~title:"TX_rollup: dispatch withdrawals to tz4 account" + ~originator:Constant.bootstrap2.public_key_hash + ~operator:Constant.bootstrap1.public_key_hash + ~batch_signer:Constant.bootstrap5.public_key_hash + ~finalize_commitment_signer:Constant.bootstrap4.public_key_hash + ~dispatch_withdrawals_signer:Constant.bootstrap3.public_key_hash + ~before_init + ~withdraw_dest:Constant.tz4_account.public_key_hash + () + let test_single_signer = let operator = Constant.bootstrap1.public_key_hash in test_round_trip @@ -2712,12 +2738,17 @@ let test_withdraw_command = ~ticket:ticket_id in let* _ = + let dest = + match protocol with + | Lima | Kathmandu -> Constant.bootstrap2.public_key_hash + | Alpha -> Constant.tz4_account.public_key_hash + in inject_withdraw ~counter:2L tx_client ~source:bls_key_1.aggregate_alias ~qty:1L - ~dest:Constant.bootstrap2.public_key_hash + ~dest ~ticket:ticket_id in let* () = Client.bake_for_and_wait client in @@ -2960,6 +2991,7 @@ let register ~protocols = test_committer protocols ; test_tickets_context protocols ; test_withdrawals protocols ; + test_withdrawals_tz4 [Alpha] ; test_single_signer protocols ; test_signer_reveals protocols ; test_accuser protocols ;