From 512fde82e40d2a0956a7777cb3213db606e449e7 Mon Sep 17 00:00:00 2001 From: Benjamin Canou Date: Wed, 31 May 2017 16:35:10 +0200 Subject: [PATCH] Alpha: add a faucet counter to prevent replay. --- .../embedded/alpha/client_proto_context.ml | 4 +- .../embedded/alpha/client_proto_rpcs.ml | 7 ++- .../embedded/alpha/client_proto_rpcs.mli | 6 ++- src/proto/alpha/apply.ml | 4 +- src/proto/alpha/contract_storage.ml | 50 +++++++++++++++++++ src/proto/alpha/contract_storage.mli | 4 ++ src/proto/alpha/operation_repr.ml | 8 +-- src/proto/alpha/operation_repr.mli | 1 + src/proto/alpha/services.ml | 7 +++ src/proto/alpha/services_registration.ml | 2 + src/proto/alpha/storage.ml | 10 ++++ src/proto/alpha/storage.mli | 6 +++ src/proto/alpha/tezos_context.mli | 7 +++ src/tezos-deps.opam | 1 + 14 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/client/embedded/alpha/client_proto_context.ml b/src/client/embedded/alpha/client_proto_context.ml index 4ce8aba35..cb234750a 100644 --- a/src/client/embedded/alpha/client_proto_context.ml +++ b/src/client/embedded/alpha/client_proto_context.ml @@ -100,8 +100,10 @@ let originate_contract rpc_config let faucet rpc_config block ?force ~manager_pkh () = Client_node_rpcs.Blocks.net rpc_config block >>=? fun net -> + Client_proto_rpcs.Context.faucet_counter rpc_config block >>=? fun pcounter -> + let counter = Int32.succ pcounter in Client_proto_rpcs.Helpers.Forge.Anonymous.faucet - rpc_config block ~net ~id:manager_pkh () >>=? fun bytes -> + rpc_config block ~net ~id:manager_pkh counter >>=? fun bytes -> originate rpc_config ?force ~block bytes let delegate_contract rpc_config diff --git a/src/client/embedded/alpha/client_proto_rpcs.ml b/src/client/embedded/alpha/client_proto_rpcs.ml index ce417a43b..1b943456f 100644 --- a/src/client/embedded/alpha/client_proto_rpcs.ml +++ b/src/client/embedded/alpha/client_proto_rpcs.ml @@ -69,6 +69,9 @@ module Context = struct let next_level cctxt block = call_error_service1 cctxt Services.Context.next_level block () + let faucet_counter cctxt block = + call_error_service1 cctxt Services.Context.faucet_counter block () + module Nonce = struct type nonce_info = Services.Context.Nonce.nonce_info = @@ -248,9 +251,9 @@ module Helpers = struct block ~net ~level ~nonce () = operations cctxt block ~net [Seed_nonce_revelation { level ; nonce }] let faucet cctxt - block ~net ~id () = + block ~net ~id counter = let nonce = Sodium.Random.Bigbytes.generate 16 in - operations cctxt block ~net [Faucet { id ; nonce }] + operations cctxt block ~net [Faucet { id ; counter ; nonce }] end let block cctxt block ~net ~predecessor ~timestamp ~fitness ~operations_hash diff --git a/src/client/embedded/alpha/client_proto_rpcs.mli b/src/client/embedded/alpha/client_proto_rpcs.mli index 84d69d736..4e20e69e4 100644 --- a/src/client/embedded/alpha/client_proto_rpcs.mli +++ b/src/client/embedded/alpha/client_proto_rpcs.mli @@ -60,6 +60,10 @@ module Context : sig (** [next_level cctxt blk] returns the (protocol view of the) level of the successor of [blk]. *) + val faucet_counter: + Client_rpcs.config -> + block -> int32 tzresult Lwt.t + module Nonce : sig val hash: Client_rpcs.config -> @@ -306,7 +310,7 @@ module Helpers : sig block -> net:Net_id.t -> id:public_key_hash -> - unit -> MBytes.t tzresult Lwt.t + int32 -> MBytes.t tzresult Lwt.t end val block: Client_rpcs.config -> diff --git a/src/proto/alpha/apply.ml b/src/proto/alpha/apply.ml index 2c8438d66..067be76c4 100644 --- a/src/proto/alpha/apply.ml +++ b/src/proto/alpha/apply.ml @@ -196,7 +196,9 @@ let apply_anonymous_operation ctxt miner_contract origination_nonce kind = ctxt contract Constants.seed_nonce_revelation_tip >>=? fun ctxt -> return (ctxt, origination_nonce) end - | Faucet { id = manager } -> + | Faucet { id = manager ; counter } -> + Contract.check_faucet_counter_increment ctxt counter >>=? fun () -> + Contract.increment_faucet_counter ctxt >>=? fun ctxt -> (* Free tez for all! *) begin match miner_contract with diff --git a/src/proto/alpha/contract_storage.ml b/src/proto/alpha/contract_storage.ml index 59a25dc35..5581c455e 100644 --- a/src/proto/alpha/contract_storage.ml +++ b/src/proto/alpha/contract_storage.ml @@ -13,6 +13,8 @@ type error += | Cannot_pay_storage_fee of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Temporary *) | Counter_in_the_past of Contract_repr.contract * int32 * int32 (* `Branch *) | Counter_in_the_future of Contract_repr.contract * int32 * int32 (* `Temporary *) + | Faucet_counter_in_the_past of int32 * int32 (* `Branch *) + | Faucet_counter_in_the_future of int32 * int32 (* `Temporary *) | Unspendable_contract of Contract_repr.contract (* `Permanent *) | Non_existing_contract of Contract_repr.contract (* `Temporary *) | Non_delegatable_contract of Contract_repr.contract (* `Permanent *) @@ -105,6 +107,36 @@ let () = (req "found" int32)) (function Counter_in_the_past (c, x, y) -> Some (c, x, y) | _ -> None) (fun (c, x, y) -> Counter_in_the_past (c, x, y)) ; + register_error_kind + `Temporary + ~id:"contract.faucet_counter_in_the_future" + ~title:"Invalid faucet counter (not yet reached) in a manager operation" + ~description:"An operation assumed a faucet counter in the future" + ~pp:(fun ppf (exp, found) -> + Format.fprintf ppf + "Faucet counter %ld not yet reached (expected %ld)" + found exp) + Data_encoding. + (obj2 + (req "expected" int32) + (req "found" int32)) + (function Faucet_counter_in_the_future (x, y) -> Some (x, y) | _ -> None) + (fun (x, y) -> Faucet_counter_in_the_future (x, y)) ; + register_error_kind + `Branch + ~id:"contract.faucet_counter_in_the_past" + ~title:"Invalid faucet counter (already used) in a manager operation" + ~description:"An operation assumed a faucet counter in the past" + ~pp:(fun ppf (exp, found) -> + Format.fprintf ppf + "Faucet counter %ld already used (expected %ld)" + found exp) + Data_encoding. + (obj2 + (req "expected" int32) + (req "found" int32)) + (function Faucet_counter_in_the_past (x, y) -> Some (x, y) | _ -> None) + (fun (x, y) -> Faucet_counter_in_the_past (x, y)) ; register_error_kind `Temporary ~id:"contract.non_existing_contract" @@ -228,6 +260,23 @@ let increment_counter c contract = Storage.Contract.Counter.get c contract >>=? fun contract_counter -> Storage.Contract.Counter.set c contract (Int32.succ contract_counter) +let get_faucet_counter c = + Storage.Contract.Faucet_counter.get c + +let check_faucet_counter_increment c counter = + Storage.Contract.Faucet_counter.get c >>=? fun faucet_counter -> + let expected = Int32.succ faucet_counter in + if Compare.Int32.(expected = counter) + then return () + else if Compare.Int32.(expected > counter) then + fail (Faucet_counter_in_the_past (expected, counter)) + else + fail (Faucet_counter_in_the_future (expected, counter)) + +let increment_faucet_counter c = + Storage.Contract.Faucet_counter.get c >>=? fun faucet_counter -> + Storage.Contract.Faucet_counter.set c (Int32.succ faucet_counter) + let get_script c contract = Storage.Contract.Code.get_option c contract >>=? fun code -> Storage.Contract.Storage.get_option c contract >>=? fun storage -> @@ -367,4 +416,5 @@ let originate c nonce ~balance ~manager ?script ~delegate ~spendable ~delegatabl return (c, contract, nonce) let init c = + Storage.Contract.Faucet_counter.init c 0l >>=? fun c -> Storage.Contract.Global_counter.init c 0l diff --git a/src/proto/alpha/contract_storage.mli b/src/proto/alpha/contract_storage.mli index 57a841b30..c09d2f768 100644 --- a/src/proto/alpha/contract_storage.mli +++ b/src/proto/alpha/contract_storage.mli @@ -28,6 +28,10 @@ val list: Storage.t -> Contract_repr.t list tzresult Lwt.t val check_counter_increment: Storage.t -> Contract_repr.t -> int32 -> unit tzresult Lwt.t val increment_counter: Storage.t -> Contract_repr.t -> Storage.t tzresult Lwt.t +val get_faucet_counter: Storage.t -> int32 tzresult Lwt.t +val check_faucet_counter_increment: Storage.t -> int32 -> unit tzresult Lwt.t +val increment_faucet_counter: Storage.t -> Storage.t tzresult Lwt.t + val is_delegatable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t val is_spendable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t diff --git a/src/proto/alpha/operation_repr.ml b/src/proto/alpha/operation_repr.ml index dda246eae..2a8003b27 100644 --- a/src/proto/alpha/operation_repr.ml +++ b/src/proto/alpha/operation_repr.ml @@ -27,6 +27,7 @@ and anonymous_operation = } | Faucet of { id: Ed25519.Public_key_hash.t ; + counter: counter ; nonce: MBytes.t ; } @@ -276,18 +277,19 @@ module Encoding = struct (fun ((), level, nonce) -> Seed_nonce_revelation { level ; nonce }) let faucet_encoding = - (obj3 + (obj4 (req "kind" (constant "faucet")) (req "id" Ed25519.Public_key_hash.encoding) + (req "counter" int32) (req "nonce" (Fixed.bytes 16))) let faucet_case tag = case ~tag faucet_encoding (function - | Faucet { id ; nonce } -> Some ((), id, nonce) + | Faucet { id ; counter ; nonce } -> Some ((), id, counter, nonce) | _ -> None ) - (fun ((), id, nonce) -> Faucet { id ; nonce }) + (fun ((), id, counter, nonce) -> Faucet { id ; counter ; nonce }) let unsigned_operation_case tag = case ~tag diff --git a/src/proto/alpha/operation_repr.mli b/src/proto/alpha/operation_repr.mli index a808d4b70..6e9a4d34f 100644 --- a/src/proto/alpha/operation_repr.mli +++ b/src/proto/alpha/operation_repr.mli @@ -27,6 +27,7 @@ and anonymous_operation = } | Faucet of { id: Ed25519.Public_key_hash.t ; + counter: counter ; nonce: MBytes.t ; } diff --git a/src/proto/alpha/services.ml b/src/proto/alpha/services.ml index b78165e45..2829bcac4 100644 --- a/src/proto/alpha/services.ml +++ b/src/proto/alpha/services.ml @@ -136,6 +136,13 @@ module Context = struct describe ~title: "detailled level info" Level.encoding) RPC.Path.(custom_root / "context" / "next_level") + let faucet_counter custom_root = + RPC.service + ~description: "Access the global faucet counter." + ~input: empty + ~output: (wrap_tzerror int32) + RPC.Path.(custom_root / "context" / "faucet_counter") + module Nonce = struct type nonce_info = diff --git a/src/proto/alpha/services_registration.ml b/src/proto/alpha/services_registration.ml index 0a1ed73f3..c9fc7488f 100644 --- a/src/proto/alpha/services_registration.ml +++ b/src/proto/alpha/services_registration.ml @@ -140,6 +140,8 @@ let next_level ctxt = let () = register0 Services.Context.next_level next_level +let () = register0 Services.Context.faucet_counter Contract.get_faucet_counter + (*-- Context.Nonce -----------------------------------------------------------*) let nonce ctxt raw_level () = diff --git a/src/proto/alpha/storage.ml b/src/proto/alpha/storage.ml index 8b9adaffe..1512032bd 100644 --- a/src/proto/alpha/storage.ml +++ b/src/proto/alpha/storage.ml @@ -108,6 +108,8 @@ module Key = struct let global_counter = store_root ["global_counter"] + let faucet_counter = store_root ["faucet_counter"] + let next_cycle_to_be_rewarded = store_root ["next_cycle_to_be_rewarded"] let rewards = store_root ["rewards"] @@ -265,6 +267,14 @@ module Contract = struct let encoding = Data_encoding.int32 end) + module Faucet_counter = + Make_single_data_storage(struct + type value = int32 + let name = "faucet counter" + let key = Key.faucet_counter + let encoding = Data_encoding.int32 + end) + (** FIXME REMOVE : use 'list' *) module Set = Make_data_set_storage(struct diff --git a/src/proto/alpha/storage.mli b/src/proto/alpha/storage.mli index a02c82663..5ec644398 100644 --- a/src/proto/alpha/storage.mli +++ b/src/proto/alpha/storage.mli @@ -122,6 +122,12 @@ module Contract : sig val init : t -> int32 -> t tzresult Lwt.t end + module Faucet_counter : sig + val get : t -> int32 tzresult Lwt.t + val set : t -> int32 -> t tzresult Lwt.t + val init : t -> int32 -> t tzresult Lwt.t + end + (** The domain of alive contracts *) module Set : Data_set_storage with type value = Contract_repr.t diff --git a/src/proto/alpha/tezos_context.mli b/src/proto/alpha/tezos_context.mli index a374bd0fe..ed6a6cc07 100644 --- a/src/proto/alpha/tezos_context.mli +++ b/src/proto/alpha/tezos_context.mli @@ -369,6 +369,12 @@ module Contract : sig val check_counter_increment: context -> contract -> int32 -> unit tzresult Lwt.t + val get_faucet_counter: context -> int32 tzresult Lwt.t + + val check_faucet_counter_increment: context -> int32 -> unit tzresult Lwt.t + + val increment_faucet_counter: context -> context tzresult Lwt.t + end module Vote : sig @@ -435,6 +441,7 @@ and anonymous_operation = } | Faucet of { id: Ed25519.Public_key_hash.t ; + counter: counter ; nonce: MBytes.t ; } diff --git a/src/tezos-deps.opam b/src/tezos-deps.opam index 7e207c448..8de320dec 100644 --- a/src/tezos-deps.opam +++ b/src/tezos-deps.opam @@ -23,6 +23,7 @@ depends: [ "git-unix" "ipv6-multicast" "irmin-watcher" (* for `irmin.unix` *) + "ezjsonm" {= "0.4.3" } "irmin" {>= "0.12" & < "1.0" } "lwt" {>= "3.0.0" } "lwt_ssl"