Client: simplify Client_keys.SIGNER
We now use `Uri.t` to represent public and secret keys. The 'remote' scheme is replaced by `https://`, `unix:///` and `tcp://`.
This commit is contained in:
parent
80dd9ebf24
commit
f69d4a5186
@ -190,35 +190,35 @@ log_endorser() {
|
||||
|
||||
BOOTSTRAP1_IDENTITY="tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"
|
||||
BOOTSTRAP1_PUBLIC="edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav"
|
||||
BOOTSTRAP1_SECRET="edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh"
|
||||
BOOTSTRAP1_SECRET="unencrypted:edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh"
|
||||
|
||||
BOOTSTRAP2_IDENTITY="tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN"
|
||||
BOOTSTRAP2_PUBLIC="edpktzNbDAUjUk697W7gYg2CRuBQjyPxbEg8dLccYYwKSKvkPvjtV9"
|
||||
BOOTSTRAP2_SECRET="edsk39qAm1fiMjgmPkw1EgQYkMzkJezLNewd7PLNHTkr6w9XA2zdfo"
|
||||
BOOTSTRAP2_SECRET="unencrypted:edsk39qAm1fiMjgmPkw1EgQYkMzkJezLNewd7PLNHTkr6w9XA2zdfo"
|
||||
|
||||
BOOTSTRAP3_IDENTITY="tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU"
|
||||
BOOTSTRAP3_PUBLIC="edpkuTXkJDGcFd5nh6VvMz8phXxU3Bi7h6hqgywNFi1vZTfQNnS1RV"
|
||||
BOOTSTRAP3_SECRET="edsk4ArLQgBTLWG5FJmnGnT689VKoqhXwmDPBuGx3z4cvwU9MmrPZZ"
|
||||
BOOTSTRAP3_SECRET="unencrypted:edsk4ArLQgBTLWG5FJmnGnT689VKoqhXwmDPBuGx3z4cvwU9MmrPZZ"
|
||||
|
||||
BOOTSTRAP4_IDENTITY="tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv"
|
||||
BOOTSTRAP4_PUBLIC="edpkuFrRoDSEbJYgxRtLx2ps82UdaYc1WwfS9sE11yhauZt5DgCHbU"
|
||||
BOOTSTRAP4_SECRET="edsk2uqQB9AY4FvioK2YMdfmyMrer5R8mGFyuaLLFfSRo8EoyNdht3"
|
||||
BOOTSTRAP4_SECRET="unencrypted:edsk2uqQB9AY4FvioK2YMdfmyMrer5R8mGFyuaLLFfSRo8EoyNdht3"
|
||||
|
||||
BOOTSTRAP5_IDENTITY="tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv"
|
||||
BOOTSTRAP5_PUBLIC="edpkv8EUUH68jmo3f7Um5PezmfGrRF24gnfLpH3sVNwJnV5bVCxL2n"
|
||||
BOOTSTRAP5_SECRET="edsk4QLrcijEffxV31gGdN2HU7UpyJjA8drFoNcmnB28n89YjPNRFm"
|
||||
BOOTSTRAP5_SECRET="unencrypted:edsk4QLrcijEffxV31gGdN2HU7UpyJjA8drFoNcmnB28n89YjPNRFm"
|
||||
|
||||
DICTATOR_SECRET="edsk31vznjHSSpGExDMHYASz45VZqXN4DPxvsa4hAyY8dHM28cZzp6"
|
||||
DICTATOR_SECRET="unencrypted:edsk31vznjHSSpGExDMHYASz45VZqXN4DPxvsa4hAyY8dHM28cZzp6"
|
||||
|
||||
add_sandboxed_bootstrap_identities() {
|
||||
|
||||
${client} import unencrypted secret key bootstrap1 ${BOOTSTRAP1_SECRET}
|
||||
${client} import unencrypted secret key bootstrap2 ${BOOTSTRAP2_SECRET}
|
||||
${client} import unencrypted secret key bootstrap3 ${BOOTSTRAP3_SECRET}
|
||||
${client} import unencrypted secret key bootstrap4 ${BOOTSTRAP4_SECRET}
|
||||
${client} import unencrypted secret key bootstrap5 ${BOOTSTRAP5_SECRET}
|
||||
${client} import secret key bootstrap1 ${BOOTSTRAP1_SECRET}
|
||||
${client} import secret key bootstrap2 ${BOOTSTRAP2_SECRET}
|
||||
${client} import secret key bootstrap3 ${BOOTSTRAP3_SECRET}
|
||||
${client} import secret key bootstrap4 ${BOOTSTRAP4_SECRET}
|
||||
${client} import secret key bootstrap5 ${BOOTSTRAP5_SECRET}
|
||||
|
||||
${client} import unencrypted secret key dictator ${DICTATOR_SECRET}
|
||||
${client} import secret key dictator ${DICTATOR_SECRET}
|
||||
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ let sign (cctxt : #Client_context.wallet) pkh data =
|
||||
(MBytes.get_uint8 data 0) >>= fun () ->
|
||||
Client_keys.get_key cctxt pkh >>=? fun (name, _pkh, sk_uri) ->
|
||||
log "Signing data for key %s" name >>= fun () ->
|
||||
Client_keys.sign cctxt sk_uri data >>=? fun signature ->
|
||||
Client_keys.sign sk_uri data >>=? fun signature ->
|
||||
return signature
|
||||
|
||||
let public_key (cctxt : #Client_context.wallet) pkh =
|
||||
|
@ -163,7 +163,9 @@ let main () =
|
||||
inherit Client_context_unix.unix_wallet ~base_dir
|
||||
end in
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Encrypted) ;
|
||||
(module Tezos_signer_backends.Encrypted.Make(struct
|
||||
let cctxt = new Client_context_unix.unix_prompter
|
||||
end)) ;
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Unencrypted) ;
|
||||
let commands =
|
||||
|
@ -30,136 +30,63 @@ module Public_key_hash = Client_aliases.Alias (struct
|
||||
let name = "public key hash"
|
||||
end)
|
||||
|
||||
type location = string list
|
||||
|
||||
module type LOCATOR = sig
|
||||
val name : string
|
||||
type t
|
||||
|
||||
val create : scheme:string -> location:location -> t
|
||||
val scheme : t -> string
|
||||
val location : t -> location
|
||||
val to_string : t -> string
|
||||
val pp : Format.formatter -> t -> unit
|
||||
end
|
||||
|
||||
type sk_uri = Sk_locator of { scheme : string ; location : location }
|
||||
type pk_uri = Pk_locator of { scheme : string ; location : location }
|
||||
|
||||
module Sk_locator = struct
|
||||
let name = "secret key"
|
||||
type t = sk_uri
|
||||
|
||||
let create ~scheme ~location =
|
||||
Sk_locator { scheme ; location }
|
||||
|
||||
let scheme (Sk_locator { scheme }) = scheme
|
||||
let location (Sk_locator { location }) = location
|
||||
|
||||
let to_string (Sk_locator { scheme ; location }) =
|
||||
String.concat "/" ((scheme ^ ":") :: List.map Uri.pct_encode location)
|
||||
|
||||
let pp ppf loc =
|
||||
Format.pp_print_string ppf (to_string loc)
|
||||
end
|
||||
|
||||
module Pk_locator = struct
|
||||
let name = "public key"
|
||||
type t = pk_uri
|
||||
|
||||
let create ~scheme ~location =
|
||||
Pk_locator { scheme ; location }
|
||||
|
||||
let scheme (Pk_locator { scheme }) = scheme
|
||||
let location (Pk_locator { location }) = location
|
||||
|
||||
let to_string (Pk_locator { scheme ; location }) =
|
||||
String.concat "/" ((scheme ^ ":") :: List.map Uri.pct_encode location)
|
||||
|
||||
let pp ppf loc =
|
||||
Format.pp_print_string ppf (to_string loc)
|
||||
end
|
||||
|
||||
module type KEY = sig
|
||||
type t
|
||||
val to_b58check : t -> string
|
||||
val of_b58check_exn : string -> t
|
||||
end
|
||||
|
||||
module Locator (K : KEY) (L : LOCATOR) = struct
|
||||
include L
|
||||
let uri_encoding =
|
||||
Data_encoding.(conv Uri.to_string Uri.of_string string)
|
||||
|
||||
let of_unencrypted k =
|
||||
L.create ~scheme:"unencrypted"
|
||||
~location:[K.to_b58check k]
|
||||
|
||||
let of_string s =
|
||||
match String.index s ':' with
|
||||
| exception Not_found ->
|
||||
of_unencrypted (K.of_b58check_exn s)
|
||||
| i ->
|
||||
let len = String.length s in
|
||||
let scheme = String.sub s 0 i in
|
||||
let location =
|
||||
String.sub s (i+1) (len-i-1) |>
|
||||
String.split '/' |>
|
||||
List.map Uri.pct_decode |>
|
||||
List.filter ((<>) "") in
|
||||
create ~scheme ~location
|
||||
|
||||
let of_source s = return (of_string s)
|
||||
let to_source t = return (to_string t)
|
||||
|
||||
let encoding = Data_encoding.(conv to_string 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
|
||||
|
||||
module Secret_key_locator = Locator(Signature.Secret_key)(Sk_locator)
|
||||
module Secret_key = Client_aliases.Alias (Secret_key_locator)
|
||||
module Public_key_locator = Locator(Signature.Public_key)(Pk_locator)
|
||||
module Public_key = Client_aliases.Alias (Public_key_locator)
|
||||
type pk_uri = Uri.t
|
||||
let make_pk_uri x = x
|
||||
|
||||
type sk_uri = Uri.t
|
||||
let make_sk_uri x = x
|
||||
|
||||
module Secret_key =
|
||||
Client_aliases.Alias (Entity(struct let name = "secret_key" end))
|
||||
module Public_key =
|
||||
Client_aliases.Alias (Entity(struct let name = "public_key" end))
|
||||
|
||||
module type SIGNER = sig
|
||||
type secret_key
|
||||
type public_key
|
||||
val scheme : string
|
||||
val title : string
|
||||
val description : string
|
||||
val init : #Client_context.io_wallet -> unit tzresult Lwt.t
|
||||
val sk_locator_of_human_input : #Client_context.io_wallet -> string list -> sk_uri tzresult Lwt.t
|
||||
val pk_locator_of_human_input : #Client_context.io_wallet -> string list -> pk_uri tzresult Lwt.t
|
||||
val sk_of_locator : sk_uri -> secret_key tzresult Lwt.t
|
||||
val pk_of_locator : pk_uri -> public_key tzresult Lwt.t
|
||||
val sk_to_locator : secret_key -> sk_uri Lwt.t
|
||||
val pk_to_locator : public_key -> pk_uri Lwt.t
|
||||
val neuterize : secret_key -> public_key Lwt.t
|
||||
val public_key : public_key -> Signature.Public_key.t tzresult Lwt.t
|
||||
val public_key_hash : public_key -> Signature.Public_key_hash.t 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_hash : pk_uri -> Signature.Public_key_hash.t tzresult Lwt.t
|
||||
val sign :
|
||||
?watermark: Signature.watermark ->
|
||||
secret_key -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||
sk_uri -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||
end
|
||||
|
||||
let signers_table : (string, (module SIGNER) * bool) Hashtbl.t = Hashtbl.create 13
|
||||
let signers_table : (string, (module SIGNER)) Hashtbl.t = Hashtbl.create 13
|
||||
|
||||
let register_signer signer =
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Hashtbl.replace signers_table Signer.scheme (signer, false)
|
||||
Hashtbl.replace signers_table Signer.scheme signer
|
||||
|
||||
let find_signer_for_key cctxt ~scheme =
|
||||
let find_signer_for_key ~scheme =
|
||||
match Hashtbl.find signers_table scheme with
|
||||
| exception Not_found ->
|
||||
fail (Unregistered_key_scheme scheme)
|
||||
| signer, false ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.init cctxt >>=? fun () ->
|
||||
Hashtbl.replace signers_table scheme (signer, true) ;
|
||||
return signer
|
||||
| signer, true -> return signer
|
||||
| signer -> return signer
|
||||
|
||||
let registered_signers () : (string * (module SIGNER)) list =
|
||||
Hashtbl.fold (fun k (v, _) acc -> (k, v) :: acc) signers_table []
|
||||
Hashtbl.fold (fun k v acc -> (k, v) :: acc) signers_table []
|
||||
|
||||
type error += Signature_mismatch of Secret_key_locator.t
|
||||
type error += Signature_mismatch of sk_uri
|
||||
|
||||
let () =
|
||||
register_error_kind `Permanent
|
||||
@ -170,134 +97,87 @@ let () =
|
||||
(fun ppf sk ->
|
||||
Format.fprintf ppf
|
||||
"The signer for %a produced an invalid signature"
|
||||
Secret_key_locator.pp sk)
|
||||
Data_encoding.(obj1 (req "locator" Secret_key_locator.encoding))
|
||||
Uri.pp_hum sk)
|
||||
Data_encoding.(obj1 (req "locator" uri_encoding))
|
||||
(function Signature_mismatch sk -> Some sk | _ -> None)
|
||||
(fun sk -> Signature_mismatch sk)
|
||||
|
||||
let sign ?watermark cctxt ((Sk_locator { scheme }) as skloc) buf =
|
||||
find_signer_for_key cctxt ~scheme >>=? fun signer ->
|
||||
let neuterize sk_uri =
|
||||
let scheme = Option.unopt ~default:"" (Uri.scheme sk_uri) in
|
||||
find_signer_for_key ~scheme >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.sk_of_locator skloc >>=? fun t ->
|
||||
Signer.sign ?watermark t buf >>=? fun signature ->
|
||||
Signer.neuterize t >>= fun pk ->
|
||||
Signer.public_key pk >>=? fun pubkey ->
|
||||
Signer.neuterize sk_uri
|
||||
|
||||
let public_key pk_uri =
|
||||
let scheme = Option.unopt ~default:"" (Uri.scheme pk_uri) in
|
||||
find_signer_for_key ~scheme >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.public_key pk_uri
|
||||
|
||||
let public_key_hash pk_uri =
|
||||
public_key pk_uri >>=? fun pk ->
|
||||
return (Signature.Public_key.hash pk)
|
||||
|
||||
let sign ?watermark sk_uri buf =
|
||||
let scheme = Option.unopt ~default:"" (Uri.scheme sk_uri) in
|
||||
find_signer_for_key ~scheme >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.sign ?watermark sk_uri buf >>=? fun signature ->
|
||||
Signer.neuterize sk_uri >>=? fun pk_uri ->
|
||||
public_key pk_uri >>=? fun pubkey ->
|
||||
fail_unless
|
||||
(Signature.check ?watermark pubkey signature buf)
|
||||
(Signature_mismatch skloc) >>=? fun () ->
|
||||
(Signature_mismatch sk_uri) >>=? fun () ->
|
||||
return signature
|
||||
|
||||
let append ?watermark cctxt loc buf =
|
||||
sign ?watermark cctxt loc buf >>|? fun signature ->
|
||||
let append ?watermark loc buf =
|
||||
sign ?watermark loc buf >>|? fun signature ->
|
||||
Signature.concat buf signature
|
||||
|
||||
let register_key cctxt ?(force=false)
|
||||
(public_key_hash, public_key, secret_key) name =
|
||||
Secret_key.add ~force cctxt name
|
||||
(Secret_key_locator.of_unencrypted secret_key) >>=? fun () ->
|
||||
Public_key.add ~force cctxt name
|
||||
(Public_key_locator.of_unencrypted public_key) >>=? fun () ->
|
||||
Public_key_hash.add ~force
|
||||
cctxt name public_key_hash >>=? fun () ->
|
||||
let register_key cctxt ?(force=false) (public_key_hash, pk_uri, sk_uri) name =
|
||||
Public_key.add ~force cctxt name pk_uri >>=? fun () ->
|
||||
Secret_key.add ~force cctxt name sk_uri >>=? fun () ->
|
||||
Public_key_hash.add ~force cctxt name public_key_hash >>=? fun () ->
|
||||
return ()
|
||||
|
||||
let gen_keys ?(force=false) ?algo ?seed (cctxt : #Client_context.io_wallet) name =
|
||||
let key = Signature.generate_key ?algo ?seed () in
|
||||
register_key cctxt ~force key name
|
||||
|
||||
let gen_keys_containing ?(prefix=false) ?(force=false) ~containing ~name (cctxt : #Client_context.io_wallet) =
|
||||
let unrepresentable =
|
||||
List.filter (fun s -> not @@ Base58.Alphabet.all_in_alphabet Base58.Alphabet.bitcoin s) containing in
|
||||
match unrepresentable with
|
||||
| _ :: _ ->
|
||||
cctxt#warning
|
||||
"The following can't be written in the key alphabet (%a): %a"
|
||||
Base58.Alphabet.pp Base58.Alphabet.bitcoin
|
||||
(Format.pp_print_list
|
||||
~pp_sep:(fun ppf () -> Format.fprintf ppf ", ")
|
||||
(fun ppf s -> Format.fprintf ppf "'%s'" s))
|
||||
unrepresentable >>= return
|
||||
| [] ->
|
||||
Public_key_hash.mem cctxt name >>=? fun name_exists ->
|
||||
if name_exists && not force
|
||||
then
|
||||
cctxt#warning
|
||||
"Key for name '%s' already exists. Use -force to update." name >>= return
|
||||
else
|
||||
begin
|
||||
cctxt#warning "This process uses a brute force search and \
|
||||
may take a long time to find a key." >>= fun () ->
|
||||
let matches =
|
||||
if prefix then
|
||||
let containing_tz1 = List.map ((^) "tz1") containing in
|
||||
(fun key -> List.exists
|
||||
(fun containing ->
|
||||
String.sub key 0 (String.length containing) = containing)
|
||||
containing_tz1)
|
||||
else
|
||||
let re = Re.Str.regexp (String.concat "\\|" containing) in
|
||||
(fun key -> try ignore (Re.Str.search_forward re key 0); true
|
||||
with Not_found -> false) in
|
||||
let rec loop attempts =
|
||||
let public_key_hash, public_key, secret_key =
|
||||
Signature.generate_key () in
|
||||
let hash = Signature.Public_key_hash.to_b58check @@
|
||||
Signature.Public_key.hash public_key in
|
||||
if matches hash
|
||||
then
|
||||
Secret_key.add ~force cctxt name
|
||||
(Secret_key_locator.of_unencrypted secret_key) >>=? fun () ->
|
||||
Public_key.add ~force cctxt name
|
||||
(Public_key_locator.of_unencrypted public_key) >>=? fun () ->
|
||||
Public_key_hash.add ~force cctxt name public_key_hash >>=? fun () ->
|
||||
return hash
|
||||
else begin if attempts mod 25_000 = 0
|
||||
then cctxt#message "Tried %d keys without finding a match" attempts
|
||||
else Lwt.return () end >>= fun () ->
|
||||
loop (attempts + 1) in
|
||||
loop 1 >>=? fun key_hash ->
|
||||
cctxt#message
|
||||
"Generated '%s' under the name '%s'." key_hash name >>= fun () ->
|
||||
return ()
|
||||
end
|
||||
|
||||
let get_key (cctxt : #Client_context.wallet) pkh =
|
||||
let raw_get_key (cctxt : #Client_context.wallet) pkh =
|
||||
Public_key_hash.rev_find cctxt pkh >>=? function
|
||||
| None -> failwith "no keys for the source contract manager"
|
||||
| Some n ->
|
||||
Public_key.find cctxt n >>=? fun pk ->
|
||||
Secret_key.find cctxt n >>=? fun sk ->
|
||||
let scheme = Secret_key_locator.scheme sk in
|
||||
find_signer_for_key cctxt ~scheme >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.pk_of_locator pk >>=? fun pk ->
|
||||
Signer.public_key pk >>=? fun pk ->
|
||||
return (n, pk, sk)
|
||||
Public_key.find_opt cctxt n >>=? fun pk_uri ->
|
||||
Secret_key.find_opt cctxt n >>=? fun sk_uri ->
|
||||
begin
|
||||
Option.unopt_map
|
||||
~default:Lwt.return_none
|
||||
~f:(fun pkh ->
|
||||
public_key pkh >>= function
|
||||
| Error e ->
|
||||
Format.eprintf "PLOP: %a@." pp_print_error e ;
|
||||
Lwt.return_none
|
||||
| Ok pk -> Lwt.return_some pk)
|
||||
pk_uri
|
||||
end >>= fun pk ->
|
||||
return (n, pk, sk_uri)
|
||||
|
||||
let get_public_key (cctxt : #Client_context.wallet) pkh =
|
||||
Public_key_hash.rev_find cctxt pkh >>=? function
|
||||
| None -> failwith "no keys for the source contract manager"
|
||||
| Some n ->
|
||||
Public_key.find cctxt n >>=? fun pk ->
|
||||
let scheme = Public_key_locator.scheme pk in
|
||||
find_signer_for_key cctxt ~scheme >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.pk_of_locator pk >>=? fun pk ->
|
||||
Signer.public_key pk >>=? fun pk ->
|
||||
return (n, pk)
|
||||
let get_key cctxt pkh =
|
||||
raw_get_key cctxt pkh >>=? function
|
||||
| (pkh, Some pk, Some sk) -> return (pkh, pk, sk)
|
||||
| (_pkh, _pk, None) -> failwith "... FIXME ... E"
|
||||
| (_pkh, None, _sk) -> failwith "... FIXME ... F"
|
||||
|
||||
let get_keys (wallet : #Client_context.io_wallet) =
|
||||
Secret_key.load wallet >>=? fun sks ->
|
||||
Lwt_list.filter_map_s begin fun (name, sk) ->
|
||||
let get_public_key cctxt pkh =
|
||||
raw_get_key cctxt pkh >>=? function
|
||||
| (pkh, Some pk, _sk) -> return (pkh, pk)
|
||||
| (_pkh, None, _sk) -> failwith "... FIXME ... G"
|
||||
|
||||
let get_keys (cctxt : #Client_context.wallet) =
|
||||
Secret_key.load cctxt >>=? fun sks ->
|
||||
Lwt_list.filter_map_s begin fun (name, sk_uri) ->
|
||||
begin
|
||||
Public_key.find wallet name >>=? fun pk ->
|
||||
Public_key_hash.find wallet name >>=? fun pkh ->
|
||||
let scheme = Public_key_locator.scheme pk in
|
||||
find_signer_for_key wallet ~scheme >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.pk_of_locator pk >>=? fun pk ->
|
||||
Signer.public_key pk >>=? fun pk ->
|
||||
return (name, pkh, pk, sk)
|
||||
Public_key.find cctxt name >>=? fun pk_uri ->
|
||||
Public_key_hash.find cctxt name >>=? fun pkh ->
|
||||
public_key pk_uri >>=? fun pk ->
|
||||
return (name, pkh, pk, sk_uri)
|
||||
end >>= function
|
||||
| Ok r -> Lwt.return (Some r)
|
||||
| Error _ -> Lwt.return_none
|
||||
@ -308,24 +188,18 @@ let list_keys cctxt =
|
||||
Public_key_hash.load cctxt >>=? fun l ->
|
||||
map_s
|
||||
(fun (name, pkh) ->
|
||||
Public_key.find_opt cctxt name >>=? fun pkm ->
|
||||
Secret_key.find_opt cctxt name >>=? fun pks ->
|
||||
return (name, pkh, pkm, pks))
|
||||
raw_get_key cctxt pkh >>= function
|
||||
| Ok (_name, pk, sk_uri) ->
|
||||
return (name, pkh, pk, sk_uri)
|
||||
| Error _ ->
|
||||
return (name, pkh, None, None))
|
||||
l
|
||||
|
||||
let alias_keys cctxt name =
|
||||
Public_key_hash.load cctxt >>=? fun l ->
|
||||
let rec find_key = function
|
||||
| [] -> return None
|
||||
| (key_name, pkh) :: tl ->
|
||||
if key_name = name
|
||||
then begin
|
||||
Public_key.find_opt cctxt name >>=? fun pkm ->
|
||||
Secret_key.find_opt cctxt name >>=? fun pks ->
|
||||
return (Some (pkh, pkm, pks))
|
||||
end
|
||||
else find_key tl
|
||||
in find_key l
|
||||
Public_key_hash.find cctxt name >>=? fun pkh ->
|
||||
raw_get_key cctxt pkh >>= function
|
||||
| Ok (_name, pk, sk_uri) -> return (Some (pkh, pk, sk_uri))
|
||||
| Error _ -> return None
|
||||
|
||||
let force_switch () =
|
||||
Clic.switch
|
||||
|
@ -7,28 +7,11 @@
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
(** {2 Location of keys using schemes} *)
|
||||
|
||||
type location = string list
|
||||
type sk_uri = Sk_locator of { scheme : string ; location : location }
|
||||
type pk_uri = Pk_locator of { scheme : string ; location : location }
|
||||
|
||||
module type LOCATOR = sig
|
||||
val name : string
|
||||
type t
|
||||
|
||||
val create : scheme:string -> location:location -> t
|
||||
val scheme : t -> string
|
||||
val location : t -> location
|
||||
val to_string : t -> string
|
||||
val pp : Format.formatter -> t -> unit
|
||||
end
|
||||
|
||||
module Secret_key_locator : LOCATOR with type t = sk_uri
|
||||
module Public_key_locator : LOCATOR with type t = pk_uri
|
||||
|
||||
(** {2 Cryptographic keys tables } *)
|
||||
|
||||
type pk_uri = private Uri.t
|
||||
type sk_uri = private Uri.t
|
||||
|
||||
module Public_key_hash :
|
||||
Client_aliases.Alias with type t = Signature.Public_key_hash.t
|
||||
module Public_key :
|
||||
@ -39,8 +22,6 @@ module Secret_key :
|
||||
(** {2 Interface for external signing modules.} *)
|
||||
|
||||
module type SIGNER = sig
|
||||
type secret_key
|
||||
type public_key
|
||||
|
||||
val scheme : string
|
||||
(** [scheme] is the name of the scheme implemented by this signer
|
||||
@ -53,47 +34,18 @@ module type SIGNER = sig
|
||||
(** [description] is a multi-line human readable description of the
|
||||
signer, that should include the format of key specifications. *)
|
||||
|
||||
val init :
|
||||
#Client_context.io_wallet -> unit tzresult Lwt.t
|
||||
(** [init wallet] initialized the signer module (plugin
|
||||
dependent). *)
|
||||
|
||||
val sk_locator_of_human_input :
|
||||
#Client_context.io_wallet -> string list -> sk_uri tzresult Lwt.t
|
||||
(** [sk_locator_of_human_input wallet spec] is the [sk_locator]
|
||||
corresponding to the human readable specification [spec] (plugin
|
||||
dependent). *)
|
||||
|
||||
val pk_locator_of_human_input :
|
||||
#Client_context.io_wallet -> string list -> pk_uri tzresult Lwt.t
|
||||
(** [pk_locator_of_human_input wallet spec] is the [pk_locator]
|
||||
corresponding to the human readable specification [spec] (plugin
|
||||
dependent). *)
|
||||
|
||||
val sk_of_locator : sk_uri -> secret_key tzresult Lwt.t
|
||||
(** [sk_of_locator skloc] is the secret key at [skloc]. *)
|
||||
|
||||
val pk_of_locator : pk_uri -> public_key tzresult Lwt.t
|
||||
(** [pk_of_locator pkloc] is the public key at [pkloc]. *)
|
||||
|
||||
val sk_to_locator : secret_key -> sk_uri Lwt.t
|
||||
(** [sk_to_locator sk] is the location of secret key [sk]. *)
|
||||
|
||||
val pk_to_locator : public_key -> pk_uri Lwt.t
|
||||
(** [pk_to_locator pk] is the location of public key [pk]. *)
|
||||
|
||||
val neuterize : secret_key -> public_key Lwt.t
|
||||
val neuterize : sk_uri -> pk_uri tzresult Lwt.t
|
||||
(** [neuterize sk] is the corresponding [pk]. *)
|
||||
|
||||
val public_key : public_key -> 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]. *)
|
||||
|
||||
val public_key_hash : public_key -> Signature.Public_key_hash.t tzresult Lwt.t
|
||||
val public_key_hash : pk_uri -> Signature.Public_key_hash.t tzresult Lwt.t
|
||||
(** [public_key_hash pk] is the hash of [pk]. *)
|
||||
|
||||
val sign :
|
||||
?watermark: Signature.watermark ->
|
||||
secret_key -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||
sk_uri -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||
(** [sign ?watermark sk data] is signature obtained by signing [data] with
|
||||
[sk]. *)
|
||||
end
|
||||
@ -104,59 +56,51 @@ val register_signer : (module SIGNER) -> unit
|
||||
|
||||
val registered_signers : unit -> (string * (module SIGNER)) list
|
||||
|
||||
val find_signer_for_key :
|
||||
#Client_context.io_wallet -> scheme:string -> (module SIGNER) 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 neuterize : sk_uri -> pk_uri tzresult Lwt.t
|
||||
|
||||
val sign :
|
||||
?watermark:Signature.watermark ->
|
||||
#Client_context.io_wallet ->
|
||||
sk_uri -> MBytes.t -> Signature.t tzresult Lwt.t
|
||||
|
||||
val append :
|
||||
?watermark:Signature.watermark ->
|
||||
#Client_context.io_wallet ->
|
||||
sk_uri -> MBytes.t -> MBytes.t tzresult Lwt.t
|
||||
|
||||
val gen_keys :
|
||||
?force:bool ->
|
||||
?algo:Signature.algo ->
|
||||
?seed:MBytes.t ->
|
||||
#Client_context.io_wallet -> string -> unit tzresult Lwt.t
|
||||
|
||||
val register_key :
|
||||
#Client_context.wallet ->
|
||||
?force:bool ->
|
||||
(Signature.Public_key_hash.t *
|
||||
Signature.Public_key.t *
|
||||
Signature.Secret_key.t) -> string -> unit tzresult Lwt.t
|
||||
|
||||
val gen_keys_containing :
|
||||
?prefix:bool ->
|
||||
?force:bool ->
|
||||
containing:string list ->
|
||||
name:string ->
|
||||
#Client_context.io_wallet -> unit tzresult Lwt.t
|
||||
(Signature.Public_key_hash.t * pk_uri * sk_uri) -> string -> unit tzresult Lwt.t
|
||||
|
||||
val list_keys :
|
||||
#Client_context.wallet ->
|
||||
(string * Public_key_hash.t * pk_uri option * sk_uri option) list tzresult Lwt.t
|
||||
(string * Public_key_hash.t * Signature.public_key option * sk_uri option) list tzresult Lwt.t
|
||||
|
||||
val alias_keys :
|
||||
#Client_context.wallet -> string ->
|
||||
(Public_key_hash.t * pk_uri option * sk_uri option) option tzresult Lwt.t
|
||||
(Public_key_hash.t * Signature.public_key option * sk_uri option) option tzresult Lwt.t
|
||||
|
||||
val get_key:
|
||||
#Client_context.io_wallet ->
|
||||
val get_key :
|
||||
#Client_context.wallet ->
|
||||
Public_key_hash.t ->
|
||||
(string * Signature.Public_key.t * sk_uri) tzresult Lwt.t
|
||||
|
||||
val get_public_key:
|
||||
#Client_context.io_wallet ->
|
||||
val get_public_key :
|
||||
#Client_context.wallet ->
|
||||
Public_key_hash.t ->
|
||||
(string * Signature.Public_key.t) tzresult Lwt.t
|
||||
|
||||
val get_keys:
|
||||
#Client_context.io_wallet ->
|
||||
#Client_context.wallet ->
|
||||
(string * Public_key_hash.t * Signature.Public_key.t * sk_uri) list tzresult Lwt.t
|
||||
|
||||
val force_switch : unit -> (bool, 'ctx) Clic.arg
|
||||
|
||||
(**/**)
|
||||
|
||||
val make_pk_uri : Uri.t -> pk_uri
|
||||
val make_sk_uri : Uri.t -> sk_uri
|
||||
|
||||
|
@ -77,9 +77,15 @@ let main select_commands =
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Unencrypted) ;
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Encrypted) ;
|
||||
(module Tezos_signer_backends.Encrypted.Make(struct
|
||||
let cctxt = new Client_context_unix.unix_prompter
|
||||
end)) ;
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Remote) ;
|
||||
(module Tezos_signer_backends.Https) ;
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Socket.Unix) ;
|
||||
Client_keys.register_signer
|
||||
(module Tezos_signer_backends.Socket.Tcp) ;
|
||||
Lwt.catch begin fun () -> begin
|
||||
Client_config.parse_config_args
|
||||
(new unix_full
|
||||
|
@ -22,7 +22,64 @@ let sig_algo_arg =
|
||||
~default: "ed25519"
|
||||
(Signature.algo_param ())
|
||||
|
||||
let commands () =
|
||||
let gen_keys_containing
|
||||
?(prefix=false) ?(force=false)
|
||||
~containing ~name (cctxt : #Client_context.io_wallet) =
|
||||
let unrepresentable =
|
||||
List.filter (fun s -> not @@ Base58.Alphabet.all_in_alphabet Base58.Alphabet.bitcoin s) containing in
|
||||
match unrepresentable with
|
||||
| _ :: _ ->
|
||||
cctxt#warning
|
||||
"The following can't be written in the key alphabet (%a): %a"
|
||||
Base58.Alphabet.pp Base58.Alphabet.bitcoin
|
||||
(Format.pp_print_list
|
||||
~pp_sep:(fun ppf () -> Format.fprintf ppf ", ")
|
||||
(fun ppf s -> Format.fprintf ppf "'%s'" s))
|
||||
unrepresentable >>= return
|
||||
| [] ->
|
||||
Public_key_hash.mem cctxt name >>=? fun name_exists ->
|
||||
if name_exists && not force
|
||||
then
|
||||
cctxt#warning
|
||||
"Key for name '%s' already exists. Use -force to update." name >>= return
|
||||
else
|
||||
begin
|
||||
cctxt#warning "This process uses a brute force search and \
|
||||
may take a long time to find a key." >>= fun () ->
|
||||
let matches =
|
||||
if prefix then
|
||||
let containing_tz1 = List.map ((^) "tz1") containing in
|
||||
(fun key -> List.exists
|
||||
(fun containing ->
|
||||
String.sub key 0 (String.length containing) = containing)
|
||||
containing_tz1)
|
||||
else
|
||||
let re = Re.Str.regexp (String.concat "\\|" containing) in
|
||||
(fun key -> try ignore (Re.Str.search_forward re key 0); true
|
||||
with Not_found -> false) in
|
||||
let rec loop attempts =
|
||||
let public_key_hash, public_key, secret_key =
|
||||
Signature.generate_key () in
|
||||
let hash = Signature.Public_key_hash.to_b58check @@
|
||||
Signature.Public_key.hash public_key in
|
||||
if matches hash
|
||||
then
|
||||
let pk_uri = Tezos_signer_backends.Unencrypted.make_pk public_key in
|
||||
let sk_uri = Tezos_signer_backends.Unencrypted.make_sk secret_key in
|
||||
register_key cctxt ~force
|
||||
(public_key_hash, pk_uri, sk_uri) name >>=? fun () ->
|
||||
return hash
|
||||
else begin if attempts mod 25_000 = 0
|
||||
then cctxt#message "Tried %d keys without finding a match" attempts
|
||||
else Lwt.return () end >>= fun () ->
|
||||
loop (attempts + 1) in
|
||||
loop 1 >>=? fun key_hash ->
|
||||
cctxt#message
|
||||
"Generated '%s' under the name '%s'." key_hash name >>= fun () ->
|
||||
return ()
|
||||
end
|
||||
|
||||
let commands () : Client_context.io_wallet Clic.command list =
|
||||
let open Clic in
|
||||
let show_private_switch =
|
||||
switch
|
||||
@ -61,7 +118,10 @@ let commands () =
|
||||
@@ stop)
|
||||
(fun (force, algo) name (cctxt : #Client_context.io_wallet) ->
|
||||
Secret_key.of_fresh cctxt force name >>=? fun name ->
|
||||
gen_keys ~force ~algo cctxt name) ;
|
||||
let (pkh, pk, sk) = Signature.generate_key ~algo () in
|
||||
let pk_uri = Tezos_signer_backends.Unencrypted.make_pk pk in
|
||||
let sk_uri = Tezos_signer_backends.Unencrypted.make_sk sk in
|
||||
register_key cctxt ~force (pkh, pk_uri, sk_uri) name) ;
|
||||
|
||||
command ~group ~desc: "Generate (unencrypted) keys including the given string."
|
||||
(args2
|
||||
@ -82,69 +142,53 @@ let commands () =
|
||||
command ~group ~desc: "Add a secret key to the wallet."
|
||||
(args1 (Secret_key.force_switch ()))
|
||||
(prefix "import"
|
||||
@@ string
|
||||
~name:"scheme"
|
||||
~desc:"signer to use for this secret key\n\
|
||||
Use command `list signing schemes` for a list of \
|
||||
supported signers."
|
||||
@@ prefixes [ "secret" ; "key" ]
|
||||
@@ Secret_key.fresh_alias_param
|
||||
@@ seq_of_param
|
||||
(string
|
||||
~name:"spec"
|
||||
~desc:"secret key specification\n\
|
||||
Varies from one scheme to the other.\n\
|
||||
Use command `list signing schemes` for more \
|
||||
information."))
|
||||
(fun force scheme name spec cctxt ->
|
||||
@@ param
|
||||
~name:"uri"
|
||||
~desc:"secret key\n\
|
||||
Varies from one scheme to the other.\n\
|
||||
Use command `list signing schemes` for more \
|
||||
information."
|
||||
(parameter (fun _ s ->
|
||||
try return (Client_keys.make_sk_uri @@ Uri.of_string s)
|
||||
with Failure s -> failwith "Error while parsing uri: %s" s))
|
||||
@@ stop)
|
||||
(fun force name sk_uri (cctxt : Client_context.io_wallet) ->
|
||||
Secret_key.of_fresh cctxt force name >>=? fun name ->
|
||||
find_signer_for_key ~scheme cctxt >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.sk_locator_of_human_input
|
||||
(cctxt :> Client_context.io_wallet) spec >>=? fun skloc ->
|
||||
Signer.sk_of_locator skloc >>=? fun sk ->
|
||||
Signer.neuterize sk >>= fun pk ->
|
||||
Signer.pk_to_locator pk >>= fun pkloc ->
|
||||
Public_key.find_opt cctxt name >>=? function
|
||||
| None ->
|
||||
Signer.public_key_hash pk >>=? fun pkh ->
|
||||
Secret_key.add ~force cctxt name skloc >>=? fun () ->
|
||||
Public_key_hash.add ~force cctxt name pkh >>=? fun () ->
|
||||
Public_key.add ~force cctxt name pkloc
|
||||
| Some pk ->
|
||||
fail_unless (pkloc = pk || force)
|
||||
(failure
|
||||
"public and secret keys '%s' don't correspond, \
|
||||
please don't use -force" name) >>=? fun () ->
|
||||
Secret_key.add ~force cctxt name skloc) ;
|
||||
Client_keys.neuterize sk_uri >>=? fun pk_uri ->
|
||||
begin
|
||||
Public_key.find_opt cctxt name >>=? function
|
||||
| None -> return ()
|
||||
| Some pk ->
|
||||
fail_unless (pk_uri = pk || force)
|
||||
(failure
|
||||
"public and secret keys '%s' don't correspond, \
|
||||
please don't use -force" name)
|
||||
end >>=? fun () ->
|
||||
Client_keys.public_key_hash pk_uri >>=? fun pkh ->
|
||||
register_key cctxt ~force (pkh, pk_uri, sk_uri) name) ;
|
||||
|
||||
command ~group ~desc: "Add a public key to the wallet."
|
||||
(args1 (Public_key.force_switch ()))
|
||||
(prefix "import"
|
||||
@@ string
|
||||
~name:"scheme"
|
||||
~desc:"signer to use for this public key\n\
|
||||
Use command `list signing schemes` for a list of \
|
||||
supported signers."
|
||||
@@ prefixes [ "public" ; "key" ]
|
||||
@@ Public_key.fresh_alias_param
|
||||
@@ seq_of_param
|
||||
(string
|
||||
~name:"spec"
|
||||
~desc:"public key specification\n\
|
||||
Varies from one scheme to the other.\n\
|
||||
Use command `list signing schemes` for more \
|
||||
information."))
|
||||
(fun force scheme name location cctxt ->
|
||||
@@ param
|
||||
~name:"uri"
|
||||
~desc:"public key\n\
|
||||
Varies from one scheme to the other.\n\
|
||||
Use command `list signing schemes` for more \
|
||||
information."
|
||||
(parameter (fun _ s ->
|
||||
try return (Client_keys.make_pk_uri @@ Uri.of_string s)
|
||||
with Failure s -> failwith "Error while parsing uri: %s" s))
|
||||
@@ stop)
|
||||
(fun force name pk_uri (cctxt : Client_context.io_wallet) ->
|
||||
Public_key.of_fresh cctxt force name >>=? fun name ->
|
||||
find_signer_for_key ~scheme cctxt >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.pk_locator_of_human_input
|
||||
(cctxt :> Client_context.io_wallet) location >>=? fun pkloc ->
|
||||
Signer.pk_of_locator pkloc >>=? fun pk ->
|
||||
Signer.public_key_hash pk >>=? fun pkh ->
|
||||
Client_keys.public_key_hash pk_uri >>=? fun pkh ->
|
||||
Public_key_hash.add ~force cctxt name pkh >>=? fun () ->
|
||||
Public_key.add ~force cctxt name pkloc) ;
|
||||
Public_key.add ~force cctxt name pk_uri) ;
|
||||
|
||||
command ~group ~desc: "Add an identity to the wallet."
|
||||
(args1 (Public_key.force_switch ()))
|
||||
@ -166,10 +210,13 @@ let commands () =
|
||||
begin match pk, sk with
|
||||
| None, None ->
|
||||
cctxt#message "%s: %s" name v
|
||||
| _, Some Sk_locator { scheme } ->
|
||||
| _, Some uri ->
|
||||
let scheme =
|
||||
Option.unopt ~default:"unencrypted" @@
|
||||
Uri.scheme (uri : sk_uri :> Uri.t) in
|
||||
cctxt#message "%s: %s (%s sk known)" name v scheme
|
||||
| Some Pk_locator { scheme }, _ ->
|
||||
cctxt#message "%s: %s (%s pk known)" name v scheme
|
||||
| Some _, _ ->
|
||||
cctxt#message "%s: %s (pk known)" name v
|
||||
end >>= fun () -> return ()
|
||||
end l) ;
|
||||
|
||||
@ -179,36 +226,35 @@ let commands () =
|
||||
@@ Public_key_hash.alias_param
|
||||
@@ stop)
|
||||
(fun show_private (name, _) (cctxt : #Client_context.io_wallet) ->
|
||||
let ok_lwt x = x >>= (fun x -> return x) in
|
||||
alias_keys cctxt name >>=? fun key_info ->
|
||||
match key_info with
|
||||
| None -> ok_lwt @@ cctxt#message "No keys found for identity"
|
||||
| None ->
|
||||
cctxt#message "No keys found for identity" >>= fun () ->
|
||||
return ()
|
||||
| Some (pkh, pk, skloc) ->
|
||||
ok_lwt @@ cctxt#message "Hash: %a"
|
||||
Signature.Public_key_hash.pp pkh >>=? fun () ->
|
||||
cctxt#message "Hash: %a"
|
||||
Signature.Public_key_hash.pp pkh >>= fun () ->
|
||||
match pk with
|
||||
| None -> return ()
|
||||
| Some (Pk_locator { scheme } as pkloc) ->
|
||||
find_signer_for_key ~scheme cctxt >>=? fun signer ->
|
||||
let module Signer = (val signer : SIGNER) in
|
||||
Signer.pk_of_locator pkloc >>=? fun pk ->
|
||||
Signer.public_key pk >>=? fun pk ->
|
||||
ok_lwt @@ cctxt#message "Public Key: %a"
|
||||
Signature.Public_key.pp pk >>=? fun () ->
|
||||
| Some pk ->
|
||||
cctxt#message "Public Key: %a"
|
||||
Signature.Public_key.pp pk >>= fun () ->
|
||||
if show_private then
|
||||
match skloc with
|
||||
| None -> return ()
|
||||
| Some skloc ->
|
||||
Secret_key.to_source skloc >>=? fun skloc ->
|
||||
ok_lwt @@ cctxt#message "Secret Key: %s" skloc
|
||||
else return ()) ;
|
||||
cctxt#message "Secret Key: %s" skloc >>= fun () ->
|
||||
return ()
|
||||
else
|
||||
return ()) ;
|
||||
|
||||
command ~group ~desc: "Forget the entire wallet of keys."
|
||||
(args1 (Clic.switch
|
||||
~long:"force" ~short:'f'
|
||||
~doc:"you got to use the force for that" ()))
|
||||
(fixed [ "forget" ; "all" ; "keys" ])
|
||||
(fun force cctxt ->
|
||||
(fun force (cctxt : Client_context.io_wallet) ->
|
||||
fail_unless force
|
||||
(failure "this can only used with option -force") >>=? fun () ->
|
||||
Public_key.set cctxt [] >>=? fun () ->
|
||||
|
@ -6,7 +6,8 @@
|
||||
(libraries (tezos-base
|
||||
tezos-client-base
|
||||
tezos-rpc
|
||||
tezos-shell-services))
|
||||
tezos-shell-services
|
||||
tezos-signer-backends))
|
||||
(library_flags (:standard -linkall))
|
||||
(flags (:standard -w -9+27-30-32-40@8
|
||||
-safe-string
|
||||
|
@ -11,153 +11,156 @@ open Client_keys
|
||||
|
||||
let scheme = "encrypted"
|
||||
|
||||
let title =
|
||||
"Built-in signer using encrypted keys."
|
||||
module Raw = struct
|
||||
|
||||
let description =
|
||||
"If you try to import a secret key without additional argument, you will \
|
||||
be asked to either generate a new key, or to import the elements \
|
||||
from your fundraiser paper wallet.\n\
|
||||
If you add an argument when importing a secret key, \
|
||||
the format is the raw Base58-encoded key (starting with 'edsk').\n\
|
||||
The format for importing public keys is the raw Base58-encoded \
|
||||
key (starting with 'edpk')."
|
||||
(* https://tools.ietf.org/html/rfc2898#section-4.1 *)
|
||||
let salt_len = 8
|
||||
|
||||
type secret_key = Signature.Secret_key.t
|
||||
type public_key = Signature.Public_key.t
|
||||
(* Fixed zero nonce *)
|
||||
let nonce = Crypto_box.zero_nonce
|
||||
|
||||
(* https://tools.ietf.org/html/rfc2898#section-4.1 *)
|
||||
let salt_len = 8
|
||||
let pbkdf ~salt ~password =
|
||||
Cstruct.to_bigarray
|
||||
(Pbkdf.pbkdf2 ~prf:`SHA512 ~count:2048 ~dk_len:32l
|
||||
~salt: (Cstruct.of_bigarray salt)
|
||||
~password: (Cstruct.of_bigarray password))
|
||||
|
||||
(* Fixed zero nonce *)
|
||||
let nonce = Crypto_box.zero_nonce
|
||||
let encrypt ~password sk =
|
||||
let salt = Rand.generate salt_len in
|
||||
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~password ~salt) in
|
||||
let msg = Data_encoding.Binary.to_bytes_exn Signature.Secret_key.encoding sk in
|
||||
let encrypted_passwd = Crypto_box.Secretbox.box key msg nonce in
|
||||
MBytes.concat "" [ salt ; encrypted_passwd ]
|
||||
|
||||
(* skloc -> Signature.Secret_key.t *)
|
||||
let decrypted_sks = Hashtbl.create 13
|
||||
let decrypt ~password ~encrypted_sk =
|
||||
let len = MBytes.length encrypted_sk in
|
||||
let salt = MBytes.sub encrypted_sk 0 salt_len in
|
||||
let encrypted_sk = MBytes.sub encrypted_sk salt_len (len - salt_len) in
|
||||
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~salt ~password) in
|
||||
match Crypto_box.Secretbox.box_open key encrypted_sk nonce with
|
||||
| None -> return None
|
||||
| Some bytes ->
|
||||
match Data_encoding.Binary.of_bytes Signature.Secret_key.encoding bytes with
|
||||
| None -> failwith "... FIXME ... D" (* corrupted data *)
|
||||
| Some sk -> return (Some sk)
|
||||
|
||||
let pbkdf ~salt ~password =
|
||||
let open Cstruct in
|
||||
let salt = of_bigarray salt in
|
||||
let password = of_bigarray password in
|
||||
to_bigarray
|
||||
(Pbkdf.pbkdf2 ~prf:`SHA512 ~count:2048 ~dk_len:32l ~salt ~password)
|
||||
end
|
||||
|
||||
let rec decrypt_sk sk salt = function
|
||||
| [] -> None
|
||||
| password :: pws ->
|
||||
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~password ~salt) in
|
||||
match Crypto_box.Secretbox.box_open key sk nonce with
|
||||
| None -> decrypt_sk sk salt pws
|
||||
| Some sk -> Some sk
|
||||
let decrypted = Hashtbl.create 13
|
||||
let passwords = ref []
|
||||
|
||||
let salt_skenc_of_skloc skloc =
|
||||
let open Cstruct in
|
||||
let skloc = of_string skloc in
|
||||
let len = len skloc in
|
||||
let salt = sub skloc 0 salt_len in
|
||||
let skenc = sub skloc salt_len (len - salt_len) in
|
||||
to_bigarray salt, to_bigarray skenc
|
||||
let rec interactive_decrypt_loop
|
||||
(cctxt : #Client_context.prompter)
|
||||
?name ~encrypted_sk =
|
||||
begin
|
||||
match name with
|
||||
| None ->
|
||||
cctxt#prompt_password
|
||||
"Enter password for encrypted key: "
|
||||
| Some name ->
|
||||
cctxt#prompt_password
|
||||
"Enter password for encrypted key \"%s\": " name
|
||||
end >>=? fun password ->
|
||||
Raw.decrypt ~password ~encrypted_sk >>=? function
|
||||
| None ->
|
||||
interactive_decrypt_loop cctxt ?name ~encrypted_sk
|
||||
| Some sk ->
|
||||
passwords := password :: !passwords ;
|
||||
return sk
|
||||
|
||||
let rec passwd_ask_loop (cctxt : #Client_context.io_wallet) ~name ~salt ~skenc =
|
||||
cctxt#prompt_password "Enter password for encrypted key %s: " name >>=? fun password ->
|
||||
let key = pbkdf ~salt ~password in
|
||||
let key = Crypto_box.Secretbox.unsafe_of_bytes key in
|
||||
match Crypto_box.Secretbox.box_open key skenc nonce with
|
||||
| None -> passwd_ask_loop cctxt ~name ~salt ~skenc
|
||||
| Some decrypted_sk ->
|
||||
return (password, (Data_encoding.Binary.of_bytes_exn
|
||||
Signature.Secret_key.encoding
|
||||
decrypted_sk))
|
||||
let rec noninteractice_decrypt_loop ~encrypted_sk = function
|
||||
| [] -> return None
|
||||
| password :: passwords ->
|
||||
Raw.decrypt ~password ~encrypted_sk >>=? function
|
||||
| None -> noninteractice_decrypt_loop ~encrypted_sk passwords
|
||||
| Some sk -> return (Some sk)
|
||||
|
||||
let ask_all_passwords (cctxt : #Client_context.io_wallet) sks =
|
||||
fold_left_s begin fun a (name, skloc) ->
|
||||
if Secret_key_locator.scheme skloc <> scheme then
|
||||
return a
|
||||
else
|
||||
match Secret_key_locator.location skloc with
|
||||
|location :: _ -> begin
|
||||
match Base58.safe_decode location with
|
||||
| None -> Lwt.fail Exit
|
||||
| Some payload ->
|
||||
let salt, skenc = salt_skenc_of_skloc payload in
|
||||
match decrypt_sk skenc salt a with
|
||||
| Some sk ->
|
||||
Hashtbl.replace decrypted_sks location
|
||||
(Data_encoding.Binary.of_bytes_exn Signature.Secret_key.encoding sk);
|
||||
return a
|
||||
| None ->
|
||||
passwd_ask_loop
|
||||
cctxt ~name ~salt ~skenc >>=? fun (passwd, decrypted_sk) ->
|
||||
Hashtbl.replace decrypted_sks location decrypted_sk ;
|
||||
return (passwd :: a)
|
||||
end
|
||||
|_ -> Lwt.fail Exit
|
||||
end [] sks
|
||||
let decrypt_payload cctxt ?name encrypted_sk =
|
||||
match Base58.safe_decode encrypted_sk with
|
||||
| None -> failwith "... FIXME ... A"
|
||||
| Some encrypted_sk ->
|
||||
let encrypted_sk = MBytes.of_string encrypted_sk in
|
||||
noninteractice_decrypt_loop ~encrypted_sk !passwords >>=? function
|
||||
| Some sk -> return sk
|
||||
| None -> interactive_decrypt_loop cctxt ?name ~encrypted_sk
|
||||
|
||||
let init cctxt =
|
||||
let decrypt cctxt ?name sk_uri =
|
||||
let payload = Uri.path (sk_uri : sk_uri :> Uri.t) in
|
||||
decrypt_payload cctxt ?name payload >>=? fun sk ->
|
||||
Hashtbl.replace decrypted sk_uri sk ;
|
||||
return sk
|
||||
|
||||
let decrypt_all (cctxt : #Client_context.io_wallet) =
|
||||
Secret_key.load cctxt >>=? fun sks ->
|
||||
Lwt.try_bind
|
||||
(fun () -> ask_all_passwords cctxt sks)
|
||||
(fun _ -> return ())
|
||||
(fun _ -> failwith "Corrupted secret key database. Aborting.")
|
||||
iter_s begin fun (name, sk_uri) ->
|
||||
if Uri.scheme (sk_uri : sk_uri :> Uri.t) <> Some scheme then
|
||||
return ()
|
||||
else
|
||||
decrypt cctxt ~name sk_uri >>=? fun _ ->
|
||||
return ()
|
||||
end sks
|
||||
|
||||
let input_new_passphrase (cctxt : #Client_context.io_wallet) =
|
||||
cctxt#prompt_password "Enter passphrase to encrypt your key: " >>=? fun password ->
|
||||
cctxt#prompt_password "Confirm passphrase: " >>=? fun confirm ->
|
||||
if password <> confirm then
|
||||
failwith "Passphrases do not match."
|
||||
else return password
|
||||
let rec read_passphrase (cctxt : #Client_context.io) =
|
||||
cctxt#prompt_password
|
||||
"Enter passphrase to encrypt your key: " >>=? fun password ->
|
||||
cctxt#prompt_password
|
||||
"Confirm passphrase: " >>=? fun confirm ->
|
||||
if not (MBytes.equal password confirm) then
|
||||
cctxt#message "Passphrases do not match." >>= fun () ->
|
||||
read_passphrase cctxt
|
||||
else
|
||||
return password
|
||||
|
||||
let encrypt_sk cctxt sk =
|
||||
input_new_passphrase cctxt >>=? fun password ->
|
||||
let salt = Rand.generate salt_len in
|
||||
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~password ~salt) in
|
||||
let msg = Data_encoding.Binary.to_bytes_exn Signature.Secret_key.encoding sk in
|
||||
let encrypted_passwd = Crypto_box.Secretbox.box key msg nonce in
|
||||
let payload = MBytes.(to_string (concat "" [salt; encrypted_passwd])) in
|
||||
let location = Base58.safe_encode payload in
|
||||
Hashtbl.replace decrypted_sks location sk ;
|
||||
return (Secret_key_locator.create ~scheme ~location:[location])
|
||||
let encrypt cctxt sk =
|
||||
read_passphrase cctxt >>=? fun password ->
|
||||
let payload = Raw.encrypt ~password sk in
|
||||
let path = Base58.safe_encode (MBytes.to_string payload) in
|
||||
let sk_uri = Client_keys.make_sk_uri (Uri.make ~scheme ~path ()) in
|
||||
Hashtbl.replace decrypted sk_uri sk ;
|
||||
return sk_uri
|
||||
|
||||
let rec get_boolean_answer (cctxt : #Client_context.io_wallet) ~default ~msg =
|
||||
let prompt = if default then "(Y/n/q)" else "(y/N/q)" in
|
||||
cctxt#prompt "%s %s: " msg prompt >>=? fun gen ->
|
||||
match default, String.lowercase_ascii gen with
|
||||
| default, "" -> return default
|
||||
| _, "y" -> return true
|
||||
| _, "n" -> return false
|
||||
| _, "q" -> failwith "Exit by user request."
|
||||
| _ -> get_boolean_answer cctxt ~msg ~default
|
||||
(*
|
||||
|
||||
let rec sk_of_mnemonic (cctxt : #Client_context.io_wallet) =
|
||||
cctxt#prompt "Enter the e-mail used for the paper wallet: " >>=? fun email ->
|
||||
let rec loop_words acc i =
|
||||
if i > 14 then return (List.rev acc) else
|
||||
cctxt#prompt_password "Enter word %d: " i >>=? fun word ->
|
||||
match Bip39.index_of_word (MBytes.to_string word) with
|
||||
| None -> loop_words acc i
|
||||
| Some wordidx -> loop_words (wordidx :: acc) (succ i) in
|
||||
loop_words [] 0 >>=? fun words ->
|
||||
match Bip39.of_indices words with
|
||||
| None -> assert false
|
||||
| Some t ->
|
||||
cctxt#prompt_password
|
||||
"Enter the password used for the paper wallet: " >>=? fun password ->
|
||||
(* TODO: unicode normalization (NFKD)... *)
|
||||
let sk = Bip39.to_seed ~passphrase:(email ^ MBytes.to_string password) t in
|
||||
let sk = Cstruct.(to_bigarray (sub sk 0 32)) in
|
||||
let sk : Signature.Secret_key.t =
|
||||
Ed25519
|
||||
(Data_encoding.Binary.of_bytes_exn Ed25519.Secret_key.encoding sk) in
|
||||
let pk = Signature.Secret_key.to_public_key sk in
|
||||
let pkh = Signature.Public_key.hash pk in
|
||||
let msg = Format.asprintf
|
||||
"Your public Tezos address is %a is that correct?"
|
||||
Signature.Public_key_hash.pp pkh in
|
||||
get_boolean_answer cctxt ~msg ~default:true >>=? function
|
||||
| true -> return sk
|
||||
| false -> sk_of_mnemonic cctxt
|
||||
let rec get_boolean_answer (cctxt : #Client_context.io_wallet) ~default ~msg =
|
||||
let prompt = if default then "(Y/n/q)" else "(y/N/q)" in
|
||||
cctxt#prompt "%s %s: " msg prompt >>= fun gen ->
|
||||
match default, String.lowercase_ascii gen with
|
||||
| default, "" -> return default
|
||||
| _, "y" -> return true
|
||||
| _, "n" -> return false
|
||||
| _, "q" -> failwith "Exit by user request."
|
||||
| _ -> get_boolean_answer cctxt ~msg ~default
|
||||
|
||||
let rec sk_of_mnemonic (cctxt : #Client_context.io_wallet) =
|
||||
cctxt#prompt "Enter the e-mail used for the paper wallet: " >>= fun email ->
|
||||
let rec loop_words acc i =
|
||||
if i > 14 then Lwt.return (List.rev acc) else
|
||||
cctxt#prompt_password "Enter word %d: " i >>= fun word ->
|
||||
match Bip39.index_of_word word with
|
||||
| None -> loop_words acc i
|
||||
| Some wordidx -> loop_words (wordidx :: acc) (succ i) in
|
||||
loop_words [] 0 >>= fun words ->
|
||||
match Bip39.of_indices words with
|
||||
| None -> assert false
|
||||
| Some t ->
|
||||
cctxt#prompt_password
|
||||
"Enter the password used for the paper wallet: " >>= fun password ->
|
||||
(* TODO: unicode normalization (NFKD)... *)
|
||||
let sk = Bip39.to_seed ~passphrase:(email ^ password) t in
|
||||
let sk = Cstruct.(to_bigarray (sub sk 0 32)) in
|
||||
let sk : Signature.Secret_key.t =
|
||||
Ed25519
|
||||
(Data_encoding.Binary.of_bytes_exn Ed25519.Secret_key.encoding sk) in
|
||||
let pk = Signature.Secret_key.to_public_key sk in
|
||||
let pkh = Signature.Public_key.hash pk in
|
||||
let msg = Format.asprintf
|
||||
"Your public Tezos address is %a is that correct?"
|
||||
Signature.Public_key_hash.pp pkh in
|
||||
get_boolean_answer cctxt ~msg ~default:true >>=? function
|
||||
| true -> return sk
|
||||
| false -> sk_of_mnemonic cctxt
|
||||
*)
|
||||
(*
|
||||
let sk_locator_of_human_input cctxt = function
|
||||
| sk :: _ ->
|
||||
Lwt.return (Signature.Secret_key.of_b58check sk) >>=? fun sk ->
|
||||
@ -176,37 +179,38 @@ let sk_locator_of_human_input cctxt = function
|
||||
sk_of_mnemonic cctxt >>=? fun sk ->
|
||||
encrypt_sk cctxt sk
|
||||
end
|
||||
*)
|
||||
|
||||
(*
|
||||
let pk_locator_of_human_input _cctxt = function
|
||||
| [] -> failwith "Missing public key argument."
|
||||
| pk :: _ -> return (Public_key_locator.create ~scheme ~location:[pk])
|
||||
*)
|
||||
|
||||
let sk_of_locator = function
|
||||
| (Sk_locator { location = [location] }) -> begin
|
||||
match Hashtbl.find decrypted_sks location with
|
||||
| exception Not_found -> failwith "Unknown secret key location."
|
||||
| sk -> return sk
|
||||
end
|
||||
| (Sk_locator { location = _ }) ->
|
||||
failwith "Wrong location type."
|
||||
|
||||
let pk_of_locator = function
|
||||
|(Pk_locator { location = [location] }) ->
|
||||
Lwt.return (Signature.Public_key.of_b58check location)
|
||||
|(Pk_locator { location = _ }) ->
|
||||
failwith "Wrong location type."
|
||||
module Make(C : sig val cctxt: Client_context.prompter end) = struct
|
||||
|
||||
let sk_to_locator sk =
|
||||
Secret_key_locator.create
|
||||
~scheme ~location:[Signature.Secret_key.to_b58check sk] |>
|
||||
Lwt.return
|
||||
let scheme = "encrypted"
|
||||
|
||||
let pk_to_locator pk =
|
||||
Public_key_locator.create
|
||||
~scheme ~location:[Signature.Public_key.to_b58check pk] |>
|
||||
Lwt.return
|
||||
let title =
|
||||
"Built-in signer using encrypted keys."
|
||||
|
||||
let neuterize x = Lwt.return (Signature.Secret_key.to_public_key x)
|
||||
let public_key x = return x
|
||||
let public_key_hash x = return (Signature.Public_key.hash x)
|
||||
let sign ?watermark t buf = return (Signature.sign ?watermark t buf)
|
||||
let description =
|
||||
"If you try to import a secret key without additional argument, you will \
|
||||
be asked to either generate a new key, or to import the elements \
|
||||
from your fundraiser paper wallet.\n\
|
||||
If you add an argument when importing a secret key, \
|
||||
the format is the raw Base58-encoded key (starting with 'edsk').\n\
|
||||
The format for importing public keys is the raw Base58-encoded \
|
||||
key (starting with 'edpk')."
|
||||
|
||||
let public_key = Unencrypted.public_key
|
||||
let public_key_hash = Unencrypted.public_key_hash
|
||||
let neuterize sk_uri =
|
||||
decrypt C.cctxt sk_uri >>=? fun sk ->
|
||||
return (Unencrypted.make_pk (Signature.Secret_key.to_public_key sk))
|
||||
let sign ?watermark sk_uri buf =
|
||||
decrypt C.cctxt sk_uri >>=? fun sk ->
|
||||
return (Signature.sign ?watermark sk buf)
|
||||
|
||||
end
|
||||
|
@ -7,4 +7,16 @@
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
include Client_keys.SIGNER
|
||||
module Make(C : sig val cctxt: Client_context.prompter end) : Client_keys.SIGNER
|
||||
|
||||
val decrypt:
|
||||
#Client_context.io_wallet ->
|
||||
?name:string ->
|
||||
Client_keys.sk_uri -> Signature.secret_key tzresult Lwt.t
|
||||
|
||||
val decrypt_all:
|
||||
#Client_context.io_wallet -> unit tzresult Lwt.t
|
||||
|
||||
val encrypt:
|
||||
#Client_context.io ->
|
||||
Signature.secret_key -> Client_keys.sk_uri tzresult Lwt.t
|
||||
|
49
src/lib_signer_backends/https.ml
Normal file
49
src/lib_signer_backends/https.ml
Normal file
@ -0,0 +1,49 @@
|
||||
(**************************************************************************)
|
||||
(* *)
|
||||
(* Copyright (c) 2014 - 2018. *)
|
||||
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||||
(* *)
|
||||
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
open Client_keys
|
||||
|
||||
let scheme = "https"
|
||||
|
||||
let title = "..."
|
||||
|
||||
let description = "..."
|
||||
|
||||
let parse uri =
|
||||
let path = String.split '/' (Uri.path uri) in
|
||||
match List.rev path with
|
||||
| [] -> invalid_arg "..."
|
||||
| key :: rev_path ->
|
||||
Lwt.return (Signature.Public_key_hash.of_b58check key) >>=? fun key ->
|
||||
return (Uri.with_path uri (String.concat "/" (List.rev rev_path)),
|
||||
key)
|
||||
|
||||
let public_key uri =
|
||||
parse (uri : pk_uri :> Uri.t) >>=? fun (base, pkh) ->
|
||||
RPC_client.call_service
|
||||
Media_type.all_media_types
|
||||
~base Signer_services.public_key ((), pkh) () ()
|
||||
|
||||
let neuterize uri =
|
||||
return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t))
|
||||
|
||||
let public_key_hash uri =
|
||||
public_key uri >>=? fun pk ->
|
||||
return (Signature.Public_key.hash pk)
|
||||
|
||||
let sign ?watermark uri msg =
|
||||
parse (uri : sk_uri :> Uri.t) >>=? fun (base, pkh) ->
|
||||
let msg =
|
||||
match watermark with
|
||||
| None -> msg
|
||||
| Some watermark ->
|
||||
MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in
|
||||
RPC_client.call_service
|
||||
Media_type.all_media_types
|
||||
~base Signer_services.sign ((), pkh) () msg
|
@ -1,6 +1,6 @@
|
||||
(**************************************************************************)
|
||||
(* *)
|
||||
(* Copyright (c) 2014 - 2017. *)
|
||||
(* Copyright (c) 2014 - 2018. *)
|
||||
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||||
(* *)
|
||||
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
@ -14,8 +14,7 @@
|
||||
-open Tezos_stdlib_unix
|
||||
-open Tezos_client_base
|
||||
-open Tezos_signer_services
|
||||
-open Tezos_rpc_http
|
||||
-w -9))))
|
||||
-open Tezos_rpc_http))))
|
||||
|
||||
(alias
|
||||
((name runtest_indent)
|
||||
|
@ -1,172 +0,0 @@
|
||||
(**************************************************************************)
|
||||
(* *)
|
||||
(* Copyright (c) 2014 - 2018. *)
|
||||
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||||
(* *)
|
||||
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
open Client_keys
|
||||
open Signer_messages
|
||||
|
||||
type path =
|
||||
| Socket of Lwt_utils_unix.Socket.addr
|
||||
| Https of string * int
|
||||
|
||||
let socket_sign path pkh data =
|
||||
let req = { Sign.Request.pkh ; data } in
|
||||
Lwt_utils_unix.Socket.connect path >>=? fun conn ->
|
||||
Lwt_utils_unix.Socket.send conn Request.encoding (Request.Sign req) >>=? fun () ->
|
||||
let encoding = result_encoding Sign.Response.encoding in
|
||||
Lwt_utils_unix.Socket.recv conn encoding >>=? fun res ->
|
||||
Lwt_unix.close conn >>= fun () ->
|
||||
Lwt.return res
|
||||
|
||||
let socket_request_public_key path pkh =
|
||||
Lwt_utils_unix.Socket.connect path >>=? fun conn ->
|
||||
Lwt_utils_unix.Socket.send conn Request.encoding (Request.Public_key pkh) >>=? fun () ->
|
||||
let encoding = result_encoding Public_key.Response.encoding in
|
||||
Lwt_utils_unix.Socket.recv conn encoding >>=? fun res ->
|
||||
Lwt_unix.close conn >>= fun () ->
|
||||
Lwt.return res
|
||||
|
||||
let sign path pkh data =
|
||||
match path with
|
||||
| Socket path -> socket_sign path pkh data
|
||||
| Https (host, port) ->
|
||||
RPC_client.call_service
|
||||
Media_type.all_media_types
|
||||
~base: (Uri.of_string (Format.asprintf "https://%s:%d" host port))
|
||||
Signer_services.sign ((), pkh) () data
|
||||
|
||||
let request_public_key path pkh =
|
||||
match path with
|
||||
| Socket path -> socket_request_public_key path pkh
|
||||
| Https (host, port) ->
|
||||
RPC_client.call_service
|
||||
Media_type.all_media_types
|
||||
~base: (Uri.of_string (Format.asprintf "https://%s:%d" host port))
|
||||
Signer_services.public_key ((), pkh) () ()
|
||||
|
||||
module Remote_signer : SIGNER = struct
|
||||
let scheme = "remote"
|
||||
|
||||
let title =
|
||||
"Built-in tezos-signer using remote wallet."
|
||||
|
||||
let description =
|
||||
"Valid locators are one of these two forms:\n\
|
||||
\ - unix [path to local signer socket] <remote key alias>\n\
|
||||
\ - tcp [host] [port] <remote key alias>\n\
|
||||
\ - https [host] [port] <remote key alias>\n\
|
||||
All fields except the key can be of the form '$VAR', \
|
||||
in which case their value is taken from environment variable \
|
||||
VAR each time the key is accessed.\n\
|
||||
Not specifiyng fields sets them to $TEZOS_SIGNER_UNIX_PATH, \
|
||||
$TEZOS_SIGNER_TCP_HOST and $TEZOS_SIGNER_TCP_PORT, \
|
||||
$TEZOS_SIGNER_HTTPS_HOST and $TEZOS_SIGNER_HTTPS_PORT, \
|
||||
that get evaluated to default values '$HOME/.tezos-signer-socket', \
|
||||
localhost and 6732, and can be set later on."
|
||||
|
||||
type key_path = path * Signature.Public_key_hash.t
|
||||
|
||||
(* secret key is the identifier of the location key identifier *)
|
||||
type secret_key = key_path
|
||||
(* public key is the identifier of the location key identifier *)
|
||||
type public_key = key_path * Signature.Public_key.t
|
||||
|
||||
let pks : (secret_key, Signature.Public_key.t) Hashtbl.t = Hashtbl.create 53
|
||||
|
||||
(* load and init the remote wallet. initialize the connection *)
|
||||
let init _cctxt = return ()
|
||||
|
||||
let path_of_human_input = function
|
||||
| "unix" :: key :: [] ->
|
||||
return (Socket (Unix "$TEZOS_SIGNER_UNIX_PATH"),
|
||||
Signature.Public_key_hash.of_b58check_exn key)
|
||||
| "unix" :: file :: key :: [] ->
|
||||
return (Socket (Unix file),
|
||||
Signature.Public_key_hash.of_b58check_exn key)
|
||||
| "tcp" :: host :: port :: key :: [] ->
|
||||
return (Socket (Tcp (host, int_of_string port)),
|
||||
Signature.Public_key_hash.of_b58check_exn key)
|
||||
(* Temporary FIXME *)
|
||||
(* | "tcp" :: host :: key :: [] -> *)
|
||||
(* return (Socket (Tcp (host, "$TEZOS_SIGNER_TCP_PORT")), key) *)
|
||||
(* | "tcp" :: key :: [] -> *)
|
||||
(* return (Socket (Tcp ("$TEZOS_SIGNER_TCP_HOST", "$TEZOS_SIGNER_TCP_PORT")), key) *)
|
||||
| "https" :: host :: port :: key :: [] ->
|
||||
return (Https (host, int_of_string port),
|
||||
Signature.Public_key_hash.of_b58check_exn key)
|
||||
(* Temporary FIXME *)
|
||||
(* | "https" :: host :: key :: [] -> *)
|
||||
(* return (Https (host, "$TEZOS_SIGNER_HTTPS_PORT"), key) *)
|
||||
(* | "https" :: key :: [] -> *)
|
||||
(* return (Https ("$TEZOS_SIGNER_HTTPS_HOST", "$TEZOS_SIGNER_HTTPS_PORT"), key) *)
|
||||
| location ->
|
||||
failwith
|
||||
"@[<v 2>Remote Schema : wrong locator %s.@,@[<hov 0>%a@]@]"
|
||||
(Secret_key_locator.to_string (Secret_key_locator.create ~scheme ~location))
|
||||
Format.pp_print_text description
|
||||
|
||||
let locator_of_path = function
|
||||
| Socket (Unix path), key -> [ "unix" ; path ; Signature.Public_key_hash.to_b58check key ]
|
||||
| Socket (Tcp (host, port)), key -> [ "tcp" ; host ; string_of_int port ; Signature.Public_key_hash.to_b58check key ]
|
||||
| Https (host, port), key -> [ "https" ; host ; string_of_int port ; Signature.Public_key_hash.to_b58check key ]
|
||||
|
||||
let pk_locator_of_human_input _cctxt path =
|
||||
path_of_human_input path >>=? fun pk ->
|
||||
let location = locator_of_path pk in
|
||||
return (Public_key_locator.create ~scheme ~location)
|
||||
|
||||
let sk_to_locator sk =
|
||||
let location = locator_of_path sk in
|
||||
Lwt.return (Secret_key_locator.create ~scheme ~location)
|
||||
|
||||
let sk_locator_of_human_input _cctxt input =
|
||||
path_of_human_input input >>=? fun (path, key) ->
|
||||
request_public_key path key >>=? fun pk ->
|
||||
Hashtbl.replace pks (path, key) pk ;
|
||||
sk_to_locator (path,key) >>= fun locator ->
|
||||
return locator
|
||||
|
||||
let sk_of_locator loc =
|
||||
path_of_human_input (Secret_key_locator.location loc)
|
||||
|
||||
let pk_of_locator loc =
|
||||
path_of_human_input (Public_key_locator.location loc) >>=? fun (path, key) ->
|
||||
request_public_key path key >>=? fun pk ->
|
||||
Hashtbl.replace pks (path, key) pk ;
|
||||
return ((path, key), pk)
|
||||
|
||||
let pk_to_locator (path, _) =
|
||||
let location = locator_of_path path in
|
||||
Lwt.return (Public_key_locator.create ~scheme ~location)
|
||||
|
||||
let neuterize ((path, key) as sk) =
|
||||
match Hashtbl.find_opt pks sk with
|
||||
| Some pk -> Lwt.return (sk, pk)
|
||||
| None -> begin
|
||||
request_public_key path key >>= function
|
||||
| Error _ -> Lwt.fail_with "Remote : Cannot obtain public key from remote signer"
|
||||
| Ok pk -> begin
|
||||
Hashtbl.replace pks sk pk ;
|
||||
Lwt.return (sk, pk)
|
||||
end
|
||||
end
|
||||
|
||||
let public_key (_, x) = return x
|
||||
let public_key_hash (_, x) = return (Signature.Public_key.hash x)
|
||||
|
||||
let sign ?watermark (path, key) msg =
|
||||
let msg =
|
||||
match watermark with
|
||||
| None -> msg
|
||||
| Some watermark ->
|
||||
MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in
|
||||
sign path key msg
|
||||
|
||||
end
|
||||
|
||||
include Remote_signer
|
105
src/lib_signer_backends/socket.ml
Normal file
105
src/lib_signer_backends/socket.ml
Normal file
@ -0,0 +1,105 @@
|
||||
(**************************************************************************)
|
||||
(* *)
|
||||
(* Copyright (c) 2014 - 2018. *)
|
||||
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||||
(* *)
|
||||
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
open Client_keys
|
||||
open Signer_messages
|
||||
|
||||
let sign ?watermark path pkh msg =
|
||||
let msg =
|
||||
match watermark with
|
||||
| None -> msg
|
||||
| Some watermark ->
|
||||
MBytes.concat "" [ Signature.bytes_of_watermark watermark ; msg ] in
|
||||
let req = { Sign.Request.pkh ; data = msg } in
|
||||
Lwt_utils_unix.Socket.connect path >>=? fun conn ->
|
||||
Lwt_utils_unix.Socket.send
|
||||
conn Request.encoding (Request.Sign req) >>=? fun () ->
|
||||
let encoding = result_encoding Sign.Response.encoding in
|
||||
Lwt_utils_unix.Socket.recv conn encoding >>=? fun res ->
|
||||
Lwt_unix.close conn >>= fun () ->
|
||||
Lwt.return res
|
||||
|
||||
let public_key path pkh =
|
||||
Lwt_utils_unix.Socket.connect path >>=? fun conn ->
|
||||
Lwt_utils_unix.Socket.send
|
||||
conn Request.encoding (Request.Public_key pkh) >>=? fun () ->
|
||||
let encoding = result_encoding Public_key.Response.encoding in
|
||||
Lwt_utils_unix.Socket.recv conn encoding >>=? fun res ->
|
||||
Lwt_unix.close conn >>= fun () ->
|
||||
Lwt.return res
|
||||
|
||||
module Unix = struct
|
||||
|
||||
let scheme = "unix"
|
||||
|
||||
let title = "..."
|
||||
|
||||
let description = "..."
|
||||
|
||||
let parse uri =
|
||||
match Uri.get_query_param uri "key" with
|
||||
| None -> invalid_arg "... FIXME ... B"
|
||||
| Some key ->
|
||||
Lwt.return (Signature.Public_key_hash.of_b58check key) >>=? fun key ->
|
||||
return (Lwt_utils_unix.Socket.Unix (Uri.path uri), key)
|
||||
|
||||
let public_key uri =
|
||||
parse (uri : pk_uri :> Uri.t) >>=? fun (path, pkh) ->
|
||||
public_key path pkh
|
||||
|
||||
let neuterize uri =
|
||||
return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t))
|
||||
|
||||
let public_key_hash uri =
|
||||
public_key uri >>=? fun pk ->
|
||||
return (Signature.Public_key.hash pk)
|
||||
|
||||
let sign ?watermark uri msg =
|
||||
parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) ->
|
||||
sign ?watermark path pkh msg
|
||||
|
||||
end
|
||||
|
||||
module Tcp = struct
|
||||
|
||||
let scheme = "tcp"
|
||||
|
||||
let title = "..."
|
||||
|
||||
let description = "..."
|
||||
|
||||
(* let init _cctxt = return () *)
|
||||
|
||||
let parse uri =
|
||||
match Uri.get_query_param uri "key" with
|
||||
| None -> invalid_arg "... FIXME ... C"
|
||||
| Some key ->
|
||||
Lwt.return (Signature.Public_key_hash.of_b58check key) >>=? fun key ->
|
||||
match Uri.host uri, Uri.port uri with
|
||||
| None, _ | _, None ->
|
||||
invalid_arg "... FIXME ... C2"
|
||||
| Some path, Some port ->
|
||||
return (Lwt_utils_unix.Socket.Tcp (path, port), key)
|
||||
|
||||
let public_key uri =
|
||||
parse (uri : pk_uri :> Uri.t) >>=? fun (path, pkh) ->
|
||||
public_key path pkh
|
||||
|
||||
let neuterize uri =
|
||||
return (Client_keys.make_pk_uri (uri : sk_uri :> Uri.t))
|
||||
|
||||
let public_key_hash uri =
|
||||
public_key uri >>=? fun pk ->
|
||||
return (Signature.Public_key.hash pk)
|
||||
|
||||
let sign ?watermark uri msg =
|
||||
parse (uri : sk_uri :> Uri.t) >>=? fun (path, pkh) ->
|
||||
sign ?watermark path pkh msg
|
||||
|
||||
end
|
11
src/lib_signer_backends/socket.mli
Normal file
11
src/lib_signer_backends/socket.mli
Normal file
@ -0,0 +1,11 @@
|
||||
(**************************************************************************)
|
||||
(* *)
|
||||
(* Copyright (c) 2014 - 2018. *)
|
||||
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||||
(* *)
|
||||
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
module Unix : Client_keys.SIGNER
|
||||
module Tcp : Client_keys.SIGNER
|
@ -22,11 +22,7 @@ let description =
|
||||
The format for importing public keys is the raw Base58-encoded \
|
||||
key (starting with 'edpk')."
|
||||
|
||||
type secret_key = Signature.Secret_key.t
|
||||
type public_key = Signature.Public_key.t
|
||||
|
||||
let init _wallet = return ()
|
||||
|
||||
(*
|
||||
let sk_locator_of_human_input _cctxt = function
|
||||
| sk :: _ ->
|
||||
return (Secret_key_locator.create ~scheme ~location:[sk])
|
||||
@ -39,30 +35,31 @@ let pk_locator_of_human_input _cctxt = function
|
||||
| [] -> failwith "Missing public key argument"
|
||||
| pk :: _ -> return (Public_key_locator.create ~scheme ~location:[pk])
|
||||
|
||||
let sk_of_locator = function
|
||||
|(Sk_locator { location = ( location :: _ ) }) ->
|
||||
Lwt.return (Signature.Secret_key.of_b58check location)
|
||||
|(Sk_locator { location = _ }) ->
|
||||
failwith "Wrong type of location"
|
||||
|
||||
|
||||
let pk_of_locator = function
|
||||
|(Pk_locator { location = ( location :: _ ) }) ->
|
||||
Lwt.return (Signature.Public_key.of_b58check location)
|
||||
|(Pk_locator { location = _ }) ->
|
||||
failwith "Wrong type of location"
|
||||
|
||||
let sk_to_locator sk =
|
||||
Secret_key_locator.create
|
||||
~scheme ~location:[Signature.Secret_key.to_b58check sk] |>
|
||||
*)
|
||||
let secret_key sk_uri =
|
||||
Lwt.return
|
||||
(Signature.Secret_key.of_b58check (Uri.path (sk_uri : sk_uri :> Uri.t)))
|
||||
|
||||
let pk_to_locator pk =
|
||||
Public_key_locator.create
|
||||
~scheme ~location:[Signature.Public_key.to_b58check pk] |>
|
||||
let make_sk sk =
|
||||
Client_keys.make_sk_uri
|
||||
(Uri.make ~scheme ~path:(Signature.Secret_key.to_b58check sk) ())
|
||||
|
||||
let public_key pk_uri =
|
||||
Lwt.return
|
||||
(Signature.Public_key.of_b58check (Uri.path (pk_uri : pk_uri :> Uri.t)))
|
||||
|
||||
let neuterize x = Lwt.return (Signature.Secret_key.to_public_key x)
|
||||
let public_key x = return x
|
||||
let public_key_hash x = return (Signature.Public_key.hash x)
|
||||
let sign ?watermark t buf = return (Signature.sign ?watermark t buf)
|
||||
let make_pk pk =
|
||||
Client_keys.make_pk_uri
|
||||
(Uri.make ~scheme ~path:(Signature.Public_key.to_b58check pk) ())
|
||||
|
||||
let neuterize sk_uri =
|
||||
secret_key sk_uri >>=? fun sk ->
|
||||
return (make_pk (Signature.Secret_key.to_public_key sk))
|
||||
|
||||
let public_key_hash pk_uri =
|
||||
public_key pk_uri >>=? fun pk ->
|
||||
return (Signature.Public_key.hash pk)
|
||||
|
||||
let sign ?watermark sk_uri buf =
|
||||
secret_key sk_uri >>=? fun sk ->
|
||||
return (Signature.sign ?watermark sk buf)
|
||||
|
@ -8,3 +8,6 @@
|
||||
(**************************************************************************)
|
||||
|
||||
include Client_keys.SIGNER
|
||||
|
||||
val make_pk: Signature.public_key -> Client_keys.pk_uri
|
||||
val make_sk: Signature.secret_key -> Client_keys.sk_uri
|
||||
|
@ -103,7 +103,7 @@ let inject_endorsement (cctxt : #Proto_alpha.full)
|
||||
~slots
|
||||
() >>=? fun bytes ->
|
||||
Client_keys.append
|
||||
cctxt src_sk ~watermark:Endorsement bytes >>=? fun signed_bytes ->
|
||||
src_sk ~watermark:Endorsement bytes >>=? fun signed_bytes ->
|
||||
Shell_services.inject_operation
|
||||
cctxt ?async ~chain_id:bi.chain_id signed_bytes >>=? fun oph ->
|
||||
iter_s
|
||||
|
@ -31,8 +31,7 @@ let forge_block_header
|
||||
{ priority ; seed_nonce_hash ; proof_of_work_nonce } in
|
||||
if Baking.check_header_proof_of_work_stamp shell protocol_data stamp_threshold then
|
||||
let unsigned_header = Block_header.forge_unsigned shell protocol_data in
|
||||
Client_keys.append cctxt
|
||||
delegate_sk ~watermark:Block_header unsigned_header
|
||||
Client_keys.append delegate_sk ~watermark:Block_header unsigned_header
|
||||
else
|
||||
loop () in
|
||||
loop ()
|
||||
|
@ -114,9 +114,10 @@ let vote_protocol_parameters =
|
||||
|
||||
let activate_alpha ?(vote = false) () =
|
||||
let fitness = Fitness_repr.from_int64 0L in
|
||||
let dictator_sk = Client_keys.Secret_key_locator.create
|
||||
~scheme:"unencrypted"
|
||||
~location:["edsk31vznjHSSpGExDMHYASz45VZqXN4DPxvsa4hAyY8dHM28cZzp6"] in
|
||||
let dictator_sk =
|
||||
Tezos_signer_backends.Unencrypted.make_sk
|
||||
(Signature.Secret_key.of_b58check_exn
|
||||
"edsk31vznjHSSpGExDMHYASz45VZqXN4DPxvsa4hAyY8dHM28cZzp6") in
|
||||
let protocol_parameters =
|
||||
if vote then vote_protocol_parameters else protocol_parameters in
|
||||
Tezos_client_genesis.Client_proto_main.bake
|
||||
@ -248,9 +249,8 @@ module Account = struct
|
||||
~(account:t)
|
||||
~destination
|
||||
~amount () =
|
||||
let src_sk = Client_keys.Secret_key_locator.create
|
||||
~scheme:"unencrypted"
|
||||
~location:[Signature.Secret_key.to_b58check account.sk] in
|
||||
let src_sk =
|
||||
Tezos_signer_backends.Unencrypted.make_sk account.sk in
|
||||
Client_proto_context.transfer
|
||||
(new wrap_full (no_write_context !rpc_config ~block))
|
||||
block
|
||||
@ -272,9 +272,8 @@ module Account = struct
|
||||
let delegatable, delegate = match delegate with
|
||||
| None -> false, None
|
||||
| Some delegate -> true, Some delegate in
|
||||
let src_sk = Client_keys.Secret_key_locator.create
|
||||
~scheme:"unencrypted"
|
||||
~location:[Signature.Secret_key.to_b58check src.sk] in
|
||||
let src_sk =
|
||||
Tezos_signer_backends.Unencrypted.make_sk src.sk in
|
||||
Client_proto_context.originate_account
|
||||
~source:src.contract
|
||||
~src_pk:src.pk
|
||||
@ -506,9 +505,8 @@ module Baking = struct
|
||||
Some (Nonce.hash seed_nonce)
|
||||
else
|
||||
None in
|
||||
let src_sk = Client_keys.Secret_key_locator.create
|
||||
~scheme:"unencrypted"
|
||||
~location:[Signature.Secret_key.to_b58check contract.sk] in
|
||||
let src_sk =
|
||||
Tezos_signer_backends.Unencrypted.make_sk contract.sk in
|
||||
Client_baking_forge.forge_block
|
||||
ctxt
|
||||
block
|
||||
|
@ -84,7 +84,7 @@ module Account : sig
|
||||
?block:Block_services.block ->
|
||||
?fee: Tez.t ->
|
||||
contract:Contract.t ->
|
||||
manager_sk:Client_keys.Secret_key_locator.t ->
|
||||
manager_sk:Client_keys.sk_uri ->
|
||||
src_pk:public_key ->
|
||||
public_key_hash option ->
|
||||
Operation_hash.t tzresult Lwt.t
|
||||
|
@ -55,7 +55,7 @@ let transfer cctxt
|
||||
~destination ?parameters ~fee () >>=? fun bytes ->
|
||||
Block_services.predecessor cctxt block >>=? fun predecessor ->
|
||||
Client_keys.sign
|
||||
cctxt src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
let signed_bytes = Signature.concat bytes signature in
|
||||
let oph = Operation_hash.hash_bytes [ signed_bytes ] in
|
||||
Alpha_services.Helpers.apply_operation cctxt block
|
||||
@ -74,7 +74,7 @@ let reveal cctxt
|
||||
cctxt block
|
||||
~branch ~source ~sourcePubKey:src_pk ~counter ~fee () >>=? fun bytes ->
|
||||
Client_keys.sign
|
||||
cctxt src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
let signed_bytes = Signature.concat bytes signature in
|
||||
let oph = Operation_hash.hash_bytes [ signed_bytes ] in
|
||||
Shell_services.inject_operation
|
||||
@ -123,7 +123,7 @@ let originate_account ?branch
|
||||
~counter ~balance ~spendable:true
|
||||
?delegatable ?delegatePubKey:delegate ~fee () >>=? fun bytes ->
|
||||
Client_keys.sign
|
||||
cctxt src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
originate cctxt ~block ~chain_id ~signature bytes
|
||||
|
||||
let delegate_contract cctxt
|
||||
@ -138,7 +138,7 @@ let delegate_contract cctxt
|
||||
~branch ~source ?sourcePubKey:src_pk ~counter ~fee delegate_opt
|
||||
>>=? fun bytes ->
|
||||
Client_keys.sign
|
||||
cctxt manager_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
manager_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
let signed_bytes = Signature.concat bytes signature in
|
||||
let oph = Operation_hash.hash_bytes [ signed_bytes ] in
|
||||
Shell_services.inject_operation
|
||||
@ -241,7 +241,7 @@ let originate_contract
|
||||
~delegatable ?delegatePubKey:delegate
|
||||
~script:{ code ; storage } ~fee () >>=? fun bytes ->
|
||||
Client_keys.sign
|
||||
cctxt src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
src_sk ~watermark:Generic_operation bytes >>=? fun signature ->
|
||||
originate cctxt ~block ~signature bytes
|
||||
|
||||
let wait_for_operation_inclusion
|
||||
@ -345,15 +345,17 @@ let claim_commitment (cctxt : #Proto_alpha.full)
|
||||
Shell_services.inject_operation
|
||||
cctxt ~chain_id:bi.chain_id bytes >>=? fun oph ->
|
||||
operation_submitted_message cctxt oph >>=? fun () ->
|
||||
let pk_uri = Tezos_signer_backends.Unencrypted.make_pk pk in
|
||||
let sk_uri = Tezos_signer_backends.Unencrypted.make_sk sk in
|
||||
begin
|
||||
match confirmations with
|
||||
| None ->
|
||||
Client_keys.register_key cctxt ?force (pkh, pk, sk) name >>=? fun () ->
|
||||
Client_keys.register_key cctxt ?force (pkh, pk_uri, sk_uri) name >>=? fun () ->
|
||||
return ()
|
||||
| Some confirmations ->
|
||||
cctxt#message "Waiting for the operation to be included..." >>= fun () ->
|
||||
wait_for_operation_inclusion ~confirmations cctxt oph >>=? fun () ->
|
||||
Client_keys.register_key cctxt ?force (pkh, pk, sk) name >>=? fun () ->
|
||||
Client_keys.register_key cctxt ?force (pkh, pk_uri, sk_uri) name >>=? fun () ->
|
||||
Alpha_services.Contract.balance
|
||||
cctxt (`Head 0) (Contract.implicit_contract pkh) >>=? fun balance ->
|
||||
cctxt#message "Account %s (%a) created with %s%a."
|
||||
|
@ -119,7 +119,7 @@ let trace
|
||||
|
||||
let hash_and_sign (data : Michelson_v1_parser.parsed) (typ : Michelson_v1_parser.parsed) sk block cctxt =
|
||||
Alpha_services.Helpers.hash_data cctxt block (data.expanded, typ.expanded) >>=? fun hash ->
|
||||
Client_keys.sign cctxt sk (MBytes.of_string hash) >>=? fun signature ->
|
||||
Client_keys.sign sk (MBytes.of_string hash) >>=? fun signature ->
|
||||
let `Hex signature = Signature.to_hex signature in
|
||||
return (hash, signature)
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
tezos-shell-services
|
||||
tezos-client-base
|
||||
tezos-rpc
|
||||
tezos-signer-backends
|
||||
bip39))
|
||||
(library_flags (:standard -linkall))
|
||||
(flags (:standard -w -9+27-30-32-40@8
|
||||
|
@ -21,7 +21,7 @@ let bake cctxt ?(timestamp = Time.now ()) block command sk =
|
||||
let blk =
|
||||
Data_encoding.Binary.to_bytes_exn Block_header.encoding
|
||||
{ shell = shell_header ; protocol_data } in
|
||||
Client_keys.append cctxt sk blk >>=? fun signed_blk ->
|
||||
Client_keys.append sk blk >>=? fun signed_blk ->
|
||||
Shell_services.inject_block cctxt signed_blk []
|
||||
|
||||
let int64_parameter =
|
||||
|
Loading…
Reference in New Issue
Block a user