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 () =
|
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
|
||||||
|
@ -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
|
||||||
|
@ -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 ->
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 =
|
||||||
|
@ -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 () =
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user