Alpha: add a faucet counter to prevent replay.
This commit is contained in:
parent
d351bb1608
commit
512fde82e4
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ->
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -27,6 +27,7 @@ and anonymous_operation =
|
||||
}
|
||||
| Faucet of {
|
||||
id: Ed25519.Public_key_hash.t ;
|
||||
counter: counter ;
|
||||
nonce: MBytes.t ;
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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 () =
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user