Client: cache public keys to limit queries to remote/hardware signers

This commit is contained in:
Benjamin Canou 2018-06-16 20:51:19 +02:00
parent 25948e423d
commit 66c9caedd6
8 changed files with 74 additions and 49 deletions

View File

@ -50,15 +50,6 @@ end
let uri_encoding = let uri_encoding =
Data_encoding.(conv Uri.to_string Uri.of_string string) Data_encoding.(conv Uri.to_string Uri.of_string string)
module Entity (Name : sig val name: string end) = struct
include Name
type t = Uri.t
let pp = Uri.pp_hum
let of_source s = return (Uri.of_string s)
let to_source t = return (Uri.to_string t)
let encoding = uri_encoding
end
type pk_uri = Uri.t type pk_uri = Uri.t
let make_pk_uri x = x let make_pk_uri x = x
@ -93,9 +84,38 @@ let sk_uri_param ?name ?desc params =
params params
module Secret_key = module Secret_key =
Client_aliases.Alias (Entity(struct let name = "secret_key" end)) Client_aliases.Alias (struct
let name = "secret_key"
type t = Uri.t
let pp = Uri.pp_hum
let of_source s = return (Uri.of_string s)
let to_source t = return (Uri.to_string t)
let encoding = uri_encoding
end)
module Public_key = module Public_key =
Client_aliases.Alias (Entity(struct let name = "public_key" end)) Client_aliases.Alias (struct
let name = "public_key"
type t = Uri.t * Signature.Public_key.t option
let pp ppf (loc, _) = Uri.pp_hum ppf loc
let of_source s = return (Uri.of_string s, None)
let to_source (t, _) = return (Uri.to_string t)
let encoding =
let open Data_encoding in
union
[ case Json_only
~title: "Locator_only"
uri_encoding
(function (uri, None) -> Some uri | (_, Some _) -> None)
(fun uri -> (uri, None)) ;
case Json_only
~title: "Locator_and_full_key"
(obj2
(req "locator" uri_encoding)
(req "key" Signature.Public_key.encoding))
(function (uri, Some key) -> Some (uri, key) | (_, None) -> None)
(fun (uri, key) -> (uri, Some key)) ]
end)
module type SIGNER = sig module type SIGNER = sig
val scheme : string val scheme : string
@ -103,7 +123,7 @@ module type SIGNER = sig
val description : string val description : string
val neuterize : sk_uri -> pk_uri tzresult Lwt.t val neuterize : sk_uri -> pk_uri tzresult Lwt.t
val public_key : pk_uri -> Signature.Public_key.t tzresult Lwt.t val public_key : pk_uri -> Signature.Public_key.t tzresult Lwt.t
val public_key_hash : pk_uri -> Signature.Public_key_hash.t tzresult Lwt.t val public_key_hash : pk_uri -> (Signature.Public_key_hash.t * Signature.Public_key.t option) tzresult Lwt.t
val sign : val sign :
?watermark: Signature.watermark -> ?watermark: Signature.watermark ->
sk_uri -> MBytes.t -> Signature.t tzresult Lwt.t sk_uri -> MBytes.t -> Signature.t tzresult Lwt.t
@ -154,7 +174,7 @@ let public_key pk_uri =
let public_key_hash pk_uri = let public_key_hash pk_uri =
public_key pk_uri >>=? fun pk -> public_key pk_uri >>=? fun pk ->
return (Signature.Public_key.hash pk) return (Signature.Public_key.hash pk, Some pk)
let sign ?watermark sk_uri buf = let sign ?watermark sk_uri buf =
let scheme = Option.unopt ~default:"" (Uri.scheme sk_uri) in let scheme = Option.unopt ~default:"" (Uri.scheme sk_uri) in
@ -176,8 +196,8 @@ let check ?watermark pk_uri signature buf =
public_key pk_uri >>=? fun pk -> public_key pk_uri >>=? fun pk ->
return (Signature.check ?watermark pk signature buf) return (Signature.check ?watermark pk signature buf)
let register_key cctxt ?(force=false) (public_key_hash, pk_uri, sk_uri) name = let register_key cctxt ?(force=false) (public_key_hash, pk_uri, sk_uri) ?public_key name =
Public_key.add ~force cctxt name pk_uri >>=? fun () -> Public_key.add ~force cctxt name (pk_uri, public_key) >>=? fun () ->
Secret_key.add ~force cctxt name sk_uri >>=? fun () -> Secret_key.add ~force cctxt name sk_uri >>=? fun () ->
Public_key_hash.add ~force cctxt name public_key_hash >>=? fun () -> Public_key_hash.add ~force cctxt name public_key_hash >>=? fun () ->
return () return ()
@ -187,19 +207,15 @@ let raw_get_key (cctxt : #Client_context.wallet) pkh =
Public_key_hash.rev_find cctxt pkh >>=? function Public_key_hash.rev_find cctxt pkh >>=? function
| None -> failwith "no keys for the source contract manager" | None -> failwith "no keys for the source contract manager"
| Some n -> | Some n ->
Public_key.find_opt cctxt n >>=? fun pk_uri ->
Secret_key.find_opt cctxt n >>=? fun sk_uri -> Secret_key.find_opt cctxt n >>=? fun sk_uri ->
begin Public_key.find_opt cctxt n >>=? begin function
Option.unopt_map | None -> return None
~default:Lwt.return_none | Some (_, Some pk) -> return (Some pk)
~f:(fun pkh -> | Some (pk_uri, None) ->
public_key pkh >>= function public_key pk_uri >>=? fun pk ->
| Error e -> Public_key.update cctxt n (pk_uri, Some pk) >>=? fun () ->
Format.eprintf "PLOP: %a@." pp_print_error e ; return (Some pk)
Lwt.return_none end >>=? fun pk ->
| Ok pk -> Lwt.return_some pk)
pk_uri
end >>= fun pk ->
return (n, pk, sk_uri) return (n, pk, sk_uri)
end >>= function end >>= function
| (Ok (_, None, None) | Error _) as initial_result -> begin | (Ok (_, None, None) | Error _) as initial_result -> begin
@ -232,9 +248,14 @@ let get_keys (cctxt : #Client_context.wallet) =
Secret_key.load cctxt >>=? fun sks -> Secret_key.load cctxt >>=? fun sks ->
Lwt_list.filter_map_s begin fun (name, sk_uri) -> Lwt_list.filter_map_s begin fun (name, sk_uri) ->
begin begin
Public_key.find cctxt name >>=? fun pk_uri ->
Public_key_hash.find cctxt name >>=? fun pkh -> Public_key_hash.find cctxt name >>=? fun pkh ->
Public_key.find cctxt name >>=? begin function
| _, Some pk -> return pk
| pk_uri, None ->
public_key pk_uri >>=? fun pk -> public_key pk_uri >>=? fun pk ->
Public_key.update cctxt name (pk_uri, Some pk) >>=? fun () ->
return pk
end >>=? fun pk ->
return (name, pkh, pk, sk_uri) return (name, pkh, pk, sk_uri)
end >>= function end >>= function
| Ok r -> Lwt.return (Some r) | Ok r -> Lwt.return (Some r)

View File

@ -25,7 +25,7 @@ type error += Invalid_uri of Uri.t
module Public_key_hash : module Public_key_hash :
Client_aliases.Alias with type t = Signature.Public_key_hash.t Client_aliases.Alias with type t = Signature.Public_key_hash.t
module Public_key : module Public_key :
Client_aliases.Alias with type t = pk_uri Client_aliases.Alias with type t = pk_uri * Signature.Public_key.t option
module Secret_key : module Secret_key :
Client_aliases.Alias with type t = sk_uri Client_aliases.Alias with type t = sk_uri
@ -50,8 +50,10 @@ module type SIGNER = sig
val public_key : pk_uri -> Signature.Public_key.t tzresult Lwt.t val public_key : pk_uri -> Signature.Public_key.t tzresult Lwt.t
(** [public_key pk] is the Ed25519 version of [pk]. *) (** [public_key pk] is the Ed25519 version of [pk]. *)
val public_key_hash : pk_uri -> Signature.Public_key_hash.t tzresult Lwt.t val public_key_hash : pk_uri -> (Signature.Public_key_hash.t * Signature.Public_key.t option) tzresult Lwt.t
(** [public_key_hash pk] is the hash of [pk]. *) (** [public_key_hash pk] is the hash of [pk].
As some signers will query the full public key to obtain the hash,
it can be optionally returned to reduce the amount of queries. *)
val sign : val sign :
?watermark: Signature.watermark -> ?watermark: Signature.watermark ->
@ -69,7 +71,7 @@ val registered_signers : unit -> (string * (module SIGNER)) list
val public_key : pk_uri -> Signature.Public_key.t tzresult Lwt.t val public_key : pk_uri -> Signature.Public_key.t tzresult Lwt.t
val public_key_hash : pk_uri -> Signature.Public_key_hash.t tzresult Lwt.t val public_key_hash : pk_uri -> (Signature.Public_key_hash.t * Signature.Public_key.t option) tzresult Lwt.t
val neuterize : sk_uri -> pk_uri tzresult Lwt.t val neuterize : sk_uri -> pk_uri tzresult Lwt.t
@ -88,7 +90,9 @@ val check :
val register_key : val register_key :
#Client_context.wallet -> #Client_context.wallet ->
?force:bool -> ?force:bool ->
(Signature.Public_key_hash.t * pk_uri * sk_uri) -> string -> unit tzresult Lwt.t (Signature.Public_key_hash.t * pk_uri * sk_uri) ->
?public_key: Signature.Public_key.t ->
string -> unit tzresult Lwt.t
val list_keys : val list_keys :
#Client_context.wallet -> #Client_context.wallet ->

View File

@ -174,17 +174,17 @@ let commands () : Client_context.io_wallet Clic.command list =
begin begin
Public_key.find_opt cctxt name >>=? function Public_key.find_opt cctxt name >>=? function
| None -> return () | None -> return ()
| Some pk -> | Some (pk_uri_found, _) ->
fail_unless (pk_uri = pk || force) fail_unless (pk_uri = pk_uri_found || force)
(failure (failure
"public and secret keys '%s' don't correspond, \ "public and secret keys '%s' don't correspond, \
please don't use -force" name) please don't use -force" name)
end >>=? fun () -> end >>=? fun () ->
Client_keys.public_key_hash pk_uri >>=? fun pkh -> Client_keys.public_key_hash pk_uri >>=? fun (pkh, public_key) ->
cctxt#message cctxt#message
"Tezos address added: %a" "Tezos address added: %a"
Signature.Public_key_hash.pp pkh >>= fun () -> Signature.Public_key_hash.pp pkh >>= fun () ->
register_key cctxt ~force (pkh, pk_uri, sk_uri) name) ; register_key cctxt ~force (pkh, pk_uri, sk_uri) ?public_key name) ;
command ~group ~desc: "Add a public key to the wallet." command ~group ~desc: "Add a public key to the wallet."
(args1 (Public_key.force_switch ())) (args1 (Public_key.force_switch ()))
@ -195,12 +195,12 @@ let commands () : Client_context.io_wallet Clic.command list =
@@ stop) @@ stop)
(fun force name pk_uri (cctxt : Client_context.io_wallet) -> (fun force name pk_uri (cctxt : Client_context.io_wallet) ->
Public_key.of_fresh cctxt force name >>=? fun name -> Public_key.of_fresh cctxt force name >>=? fun name ->
Client_keys.public_key_hash pk_uri >>=? fun pkh -> Client_keys.public_key_hash pk_uri >>=? fun (pkh, public_key) ->
Public_key_hash.add ~force cctxt name pkh >>=? fun () -> Public_key_hash.add ~force cctxt name pkh >>=? fun () ->
cctxt#message cctxt#message
"Tezos address added: %a" "Tezos address added: %a"
Signature.Public_key_hash.pp pkh >>= fun () -> Signature.Public_key_hash.pp pkh >>= fun () ->
Public_key.add ~force cctxt name pk_uri) ; Public_key.add ~force cctxt name (pk_uri, public_key)) ;
command ~group ~desc: "Add an identity to the wallet." command ~group ~desc: "Add an identity to the wallet."
(args1 (Public_key.force_switch ())) (args1 (Public_key.force_switch ()))

View File

@ -55,7 +55,7 @@ module Make(N : sig val scheme : string end) = struct
let public_key_hash uri = let public_key_hash uri =
public_key uri >>=? fun pk -> public_key uri >>=? fun pk ->
return (Signature.Public_key.hash pk) return (Signature.Public_key.hash pk, Some pk)
let sign ?watermark uri msg = let sign ?watermark uri msg =
parse (uri : sk_uri :> Uri.t) >>=? fun (base, pkh) -> parse (uri : sk_uri :> Uri.t) >>=? fun (base, pkh) ->

View File

@ -188,10 +188,10 @@ let public_key (pk_uri : pk_uri) =
let public_key_hash pk_uri = let public_key_hash pk_uri =
match Hashtbl.find_opt pkhs pk_uri with match Hashtbl.find_opt pkhs pk_uri with
| Some pkh -> return pkh | Some pkh -> return (pkh, None)
| None -> | None ->
public_key pk_uri >>=? fun _pk -> public_key pk_uri >>=? fun pk ->
return (Hashtbl.find pkhs pk_uri) return (Hashtbl.find pkhs pk_uri, Some pk)
let sign ?watermark sk_uri msg = let sign ?watermark sk_uri msg =
pkh_of_sk_uri sk_uri >>=? fun pkh -> pkh_of_sk_uri sk_uri >>=? fun pkh ->
@ -318,7 +318,7 @@ let commands =
cctxt#message "Found a valid Tezos application running on %s %s at [%s]." cctxt#message "Found a valid Tezos application running on %s %s at [%s]."
manufacturer product device_info.path >>= fun () -> manufacturer product device_info.path >>= fun () ->
public_key pk_uri >>=? fun pk -> public_key pk_uri >>=? fun pk ->
public_key_hash pk_uri >>=? fun pkh -> public_key_hash pk_uri >>=? fun (pkh, _) ->
let pkh_bytes = Signature.Public_key_hash.to_bytes pkh in let pkh_bytes = Signature.Public_key_hash.to_bytes pkh in
sign ~watermark:Generic_operation sign ~watermark:Generic_operation
sk_uri pkh_bytes >>=? fun signature -> sk_uri pkh_bytes >>=? fun signature ->

View File

@ -81,7 +81,7 @@ module Make(P : sig
let public_key_hash uri = let public_key_hash uri =
public_key uri >>=? fun pk -> public_key uri >>=? fun pk ->
return (Signature.Public_key.hash pk) return (Signature.Public_key.hash pk, Some pk)
let sign ?watermark uri msg = let sign ?watermark uri msg =
parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) -> parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) ->
@ -122,7 +122,7 @@ module Make(P : sig
let public_key_hash uri = let public_key_hash uri =
public_key uri >>=? fun pk -> public_key uri >>=? fun pk ->
return (Signature.Public_key.hash pk) return (Signature.Public_key.hash pk, Some pk)
let sign ?watermark uri msg = let sign ?watermark uri msg =
parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) -> parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) ->

View File

@ -45,7 +45,7 @@ let neuterize sk_uri =
let public_key_hash pk_uri = let public_key_hash pk_uri =
public_key pk_uri >>=? fun pk -> public_key pk_uri >>=? fun pk ->
return (Signature.Public_key.hash pk) return (Signature.Public_key.hash pk, Some pk)
let sign ?watermark sk_uri buf = let sign ?watermark sk_uri buf =
secret_key sk_uri >>=? fun sk -> secret_key sk_uri >>=? fun sk ->

View File

@ -264,7 +264,7 @@ let commands () =
@@ Clic.param ~name:"signature" ~desc:"the signature to check" @@ Clic.param ~name:"signature" ~desc:"the signature to check"
signature_parameter signature_parameter
@@ stop) @@ stop)
(fun quiet hashed (_, key_locator) signature (cctxt : #Proto_alpha.full) -> (fun quiet hashed (_, (key_locator, _)) signature (cctxt : #Proto_alpha.full) ->
Client_keys.check key_locator signature hashed >>=? fun res -> Client_keys.check key_locator signature hashed >>=? fun res ->
begin begin
if quiet if quiet