Alpha: add a faucet counter to prevent replay.

This commit is contained in:
Benjamin Canou 2017-05-31 16:35:10 +02:00
parent d351bb1608
commit 512fde82e4
14 changed files with 109 additions and 8 deletions

View File

@ -100,8 +100,10 @@ let originate_contract rpc_config
let faucet rpc_config block ?force ~manager_pkh () = let faucet rpc_config block ?force ~manager_pkh () =
Client_node_rpcs.Blocks.net rpc_config block >>=? fun net -> 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 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 originate rpc_config ?force ~block bytes
let delegate_contract rpc_config let delegate_contract rpc_config

View File

@ -69,6 +69,9 @@ module Context = struct
let next_level cctxt block = let next_level cctxt block =
call_error_service1 cctxt Services.Context.next_level 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 module Nonce = struct
type nonce_info = Services.Context.Nonce.nonce_info = type nonce_info = Services.Context.Nonce.nonce_info =
@ -248,9 +251,9 @@ module Helpers = struct
block ~net ~level ~nonce () = block ~net ~level ~nonce () =
operations cctxt block ~net [Seed_nonce_revelation { level ; nonce }] operations cctxt block ~net [Seed_nonce_revelation { level ; nonce }]
let faucet cctxt let faucet cctxt
block ~net ~id () = block ~net ~id counter =
let nonce = Sodium.Random.Bigbytes.generate 16 in let nonce = Sodium.Random.Bigbytes.generate 16 in
operations cctxt block ~net [Faucet { id ; nonce }] operations cctxt block ~net [Faucet { id ; counter ; nonce }]
end end
let block cctxt let block cctxt
block ~net ~predecessor ~timestamp ~fitness ~operations_hash block ~net ~predecessor ~timestamp ~fitness ~operations_hash

View File

@ -60,6 +60,10 @@ module Context : sig
(** [next_level cctxt blk] returns the (protocol view of the) level (** [next_level cctxt blk] returns the (protocol view of the) level
of the successor of [blk]. *) of the successor of [blk]. *)
val faucet_counter:
Client_rpcs.config ->
block -> int32 tzresult Lwt.t
module Nonce : sig module Nonce : sig
val hash: val hash:
Client_rpcs.config -> Client_rpcs.config ->
@ -306,7 +310,7 @@ module Helpers : sig
block -> block ->
net:Net_id.t -> net:Net_id.t ->
id:public_key_hash -> id:public_key_hash ->
unit -> MBytes.t tzresult Lwt.t int32 -> MBytes.t tzresult Lwt.t
end end
val block: val block:
Client_rpcs.config -> Client_rpcs.config ->

View File

@ -196,7 +196,9 @@ let apply_anonymous_operation ctxt miner_contract origination_nonce kind =
ctxt contract Constants.seed_nonce_revelation_tip >>=? fun ctxt -> ctxt contract Constants.seed_nonce_revelation_tip >>=? fun ctxt ->
return (ctxt, origination_nonce) return (ctxt, origination_nonce)
end 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! *) (* Free tez for all! *)
begin begin
match miner_contract with match miner_contract with

View File

@ -13,6 +13,8 @@ type error +=
| Cannot_pay_storage_fee of Contract_repr.contract * Tez_repr.t * Tez_repr.t (* `Temporary *) | 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_past of Contract_repr.contract * int32 * int32 (* `Branch *)
| Counter_in_the_future of Contract_repr.contract * int32 * int32 (* `Temporary *) | 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 *) | Unspendable_contract of Contract_repr.contract (* `Permanent *)
| Non_existing_contract of Contract_repr.contract (* `Temporary *) | Non_existing_contract of Contract_repr.contract (* `Temporary *)
| Non_delegatable_contract of Contract_repr.contract (* `Permanent *) | Non_delegatable_contract of Contract_repr.contract (* `Permanent *)
@ -105,6 +107,36 @@ let () =
(req "found" int32)) (req "found" int32))
(function Counter_in_the_past (c, x, y) -> Some (c, x, y) | _ -> None) (function Counter_in_the_past (c, x, y) -> Some (c, x, y) | _ -> None)
(fun (c, x, y) -> Counter_in_the_past (c, x, y)) ; (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 register_error_kind
`Temporary `Temporary
~id:"contract.non_existing_contract" ~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.get c contract >>=? fun contract_counter ->
Storage.Contract.Counter.set c contract (Int32.succ 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 = let get_script c contract =
Storage.Contract.Code.get_option c contract >>=? fun code -> Storage.Contract.Code.get_option c contract >>=? fun code ->
Storage.Contract.Storage.get_option c contract >>=? fun storage -> 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) return (c, contract, nonce)
let init c = let init c =
Storage.Contract.Faucet_counter.init c 0l >>=? fun c ->
Storage.Contract.Global_counter.init c 0l Storage.Contract.Global_counter.init c 0l

View File

@ -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 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 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_delegatable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t
val is_spendable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t val is_spendable : Storage.t -> Contract_repr.t -> bool tzresult Lwt.t

View File

@ -27,6 +27,7 @@ and anonymous_operation =
} }
| Faucet of { | Faucet of {
id: Ed25519.Public_key_hash.t ; id: Ed25519.Public_key_hash.t ;
counter: counter ;
nonce: MBytes.t ; nonce: MBytes.t ;
} }
@ -276,18 +277,19 @@ module Encoding = struct
(fun ((), level, nonce) -> Seed_nonce_revelation { level ; nonce }) (fun ((), level, nonce) -> Seed_nonce_revelation { level ; nonce })
let faucet_encoding = let faucet_encoding =
(obj3 (obj4
(req "kind" (constant "faucet")) (req "kind" (constant "faucet"))
(req "id" Ed25519.Public_key_hash.encoding) (req "id" Ed25519.Public_key_hash.encoding)
(req "counter" int32)
(req "nonce" (Fixed.bytes 16))) (req "nonce" (Fixed.bytes 16)))
let faucet_case tag = let faucet_case tag =
case ~tag faucet_encoding case ~tag faucet_encoding
(function (function
| Faucet { id ; nonce } -> Some ((), id, nonce) | Faucet { id ; counter ; nonce } -> Some ((), id, counter, nonce)
| _ -> None | _ -> None
) )
(fun ((), id, nonce) -> Faucet { id ; nonce }) (fun ((), id, counter, nonce) -> Faucet { id ; counter ; nonce })
let unsigned_operation_case tag = let unsigned_operation_case tag =
case ~tag case ~tag

View File

@ -27,6 +27,7 @@ and anonymous_operation =
} }
| Faucet of { | Faucet of {
id: Ed25519.Public_key_hash.t ; id: Ed25519.Public_key_hash.t ;
counter: counter ;
nonce: MBytes.t ; nonce: MBytes.t ;
} }

View File

@ -136,6 +136,13 @@ module Context = struct
describe ~title: "detailled level info" Level.encoding) describe ~title: "detailled level info" Level.encoding)
RPC.Path.(custom_root / "context" / "next_level") 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 module Nonce = struct
type nonce_info = type nonce_info =

View File

@ -140,6 +140,8 @@ let next_level ctxt =
let () = register0 Services.Context.next_level next_level let () = register0 Services.Context.next_level next_level
let () = register0 Services.Context.faucet_counter Contract.get_faucet_counter
(*-- Context.Nonce -----------------------------------------------------------*) (*-- Context.Nonce -----------------------------------------------------------*)
let nonce ctxt raw_level () = let nonce ctxt raw_level () =

View File

@ -108,6 +108,8 @@ module Key = struct
let global_counter = store_root ["global_counter"] 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 next_cycle_to_be_rewarded = store_root ["next_cycle_to_be_rewarded"]
let rewards = store_root ["rewards"] let rewards = store_root ["rewards"]
@ -265,6 +267,14 @@ module Contract = struct
let encoding = Data_encoding.int32 let encoding = Data_encoding.int32
end) 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' *) (** FIXME REMOVE : use 'list' *)
module Set = module Set =
Make_data_set_storage(struct Make_data_set_storage(struct

View File

@ -122,6 +122,12 @@ module Contract : sig
val init : t -> int32 -> t tzresult Lwt.t val init : t -> int32 -> t tzresult Lwt.t
end 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 *) (** The domain of alive contracts *)
module Set : Data_set_storage module Set : Data_set_storage
with type value = Contract_repr.t with type value = Contract_repr.t

View File

@ -369,6 +369,12 @@ module Contract : sig
val check_counter_increment: val check_counter_increment:
context -> contract -> int32 -> unit tzresult Lwt.t 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 end
module Vote : sig module Vote : sig
@ -435,6 +441,7 @@ and anonymous_operation =
} }
| Faucet of { | Faucet of {
id: Ed25519.Public_key_hash.t ; id: Ed25519.Public_key_hash.t ;
counter: counter ;
nonce: MBytes.t ; nonce: MBytes.t ;
} }

View File

@ -23,6 +23,7 @@ depends: [
"git-unix" "git-unix"
"ipv6-multicast" "ipv6-multicast"
"irmin-watcher" (* for `irmin.unix` *) "irmin-watcher" (* for `irmin.unix` *)
"ezjsonm" {= "0.4.3" }
"irmin" {>= "0.12" & < "1.0" } "irmin" {>= "0.12" & < "1.0" }
"lwt" {>= "3.0.0" } "lwt" {>= "3.0.0" }
"lwt_ssl" "lwt_ssl"