Client: add an option --remote-signer
When the option is provided, all keys with the scheme `remote` are now implicitly handled by the provided remote signer.
This commit is contained in:
parent
57663a95e9
commit
fb85568d98
@ -12,6 +12,7 @@
|
|||||||
type error += Invalid_block_argument of string
|
type error += Invalid_block_argument of string
|
||||||
type error += Invalid_protocol_argument of string
|
type error += Invalid_protocol_argument of string
|
||||||
type error += Invalid_port_arg of string
|
type error += Invalid_port_arg of string
|
||||||
|
type error += Invalid_remote_signer_argument of string
|
||||||
let () =
|
let () =
|
||||||
register_error_kind
|
register_error_kind
|
||||||
`Branch
|
`Branch
|
||||||
@ -45,11 +46,22 @@ let () =
|
|||||||
Format.fprintf ppf "Value %s is not a valid TCP port." s)
|
Format.fprintf ppf "Value %s is not a valid TCP port." s)
|
||||||
Data_encoding.(obj1 (req "value" string))
|
Data_encoding.(obj1 (req "value" string))
|
||||||
(function Invalid_port_arg s -> Some s | _ -> None)
|
(function Invalid_port_arg s -> Some s | _ -> None)
|
||||||
(fun s -> Invalid_port_arg s)
|
(fun s -> Invalid_port_arg s) ;
|
||||||
|
register_error_kind
|
||||||
|
`Branch
|
||||||
|
~id: "invalid_remote_signer_argument"
|
||||||
|
~title: "Unexpected URI of remote signer"
|
||||||
|
~description: "The remote signer argument could not be parsed"
|
||||||
|
~pp:
|
||||||
|
(fun ppf s ->
|
||||||
|
Format.fprintf ppf "Value '%s' is not a valid URI." s)
|
||||||
|
Data_encoding.(obj1 (req "value" string))
|
||||||
|
(function Invalid_remote_signer_argument s -> Some s | _ -> None)
|
||||||
|
(fun s -> Invalid_remote_signer_argument s)
|
||||||
|
|
||||||
|
let home = try Sys.getenv "HOME" with Not_found -> "/root"
|
||||||
|
|
||||||
let default_base_dir =
|
let default_base_dir =
|
||||||
let home = try Sys.getenv "HOME" with Not_found -> "/root" in
|
|
||||||
Filename.concat home ".tezos-client"
|
Filename.concat home ".tezos-client"
|
||||||
|
|
||||||
let default_block = `Head 0
|
let default_block = `Head 0
|
||||||
@ -64,6 +76,7 @@ module Cfg_file = struct
|
|||||||
node_port: int ;
|
node_port: int ;
|
||||||
tls: bool ;
|
tls: bool ;
|
||||||
web_port: int ;
|
web_port: int ;
|
||||||
|
remote_signer: Uri.t option ;
|
||||||
}
|
}
|
||||||
|
|
||||||
let default = {
|
let default = {
|
||||||
@ -72,27 +85,29 @@ module Cfg_file = struct
|
|||||||
node_port = 8732 ;
|
node_port = 8732 ;
|
||||||
tls = false ;
|
tls = false ;
|
||||||
web_port = 8080 ;
|
web_port = 8080 ;
|
||||||
|
remote_signer = None ;
|
||||||
}
|
}
|
||||||
|
|
||||||
open Data_encoding
|
open Data_encoding
|
||||||
|
|
||||||
let encoding =
|
let encoding =
|
||||||
conv
|
conv
|
||||||
(fun { base_dir ; node_addr ; node_port ; tls ; web_port } ->
|
(fun { base_dir ; node_addr ; node_port ; tls ; web_port ; remote_signer } ->
|
||||||
(base_dir, Some node_addr, Some node_port,
|
(base_dir, Some node_addr, Some node_port,
|
||||||
Some tls, Some web_port))
|
Some tls, Some web_port, remote_signer))
|
||||||
(fun (base_dir, node_addr, node_port, tls, web_port) ->
|
(fun (base_dir, node_addr, node_port, tls, web_port, remote_signer) ->
|
||||||
let node_addr = Option.unopt ~default:default.node_addr node_addr in
|
let node_addr = Option.unopt ~default:default.node_addr node_addr in
|
||||||
let node_port = Option.unopt ~default:default.node_port node_port in
|
let node_port = Option.unopt ~default:default.node_port node_port in
|
||||||
let tls = Option.unopt ~default:default.tls tls in
|
let tls = Option.unopt ~default:default.tls tls in
|
||||||
let web_port = Option.unopt ~default:default.web_port web_port in
|
let web_port = Option.unopt ~default:default.web_port web_port in
|
||||||
{ base_dir ; node_addr ; node_port ; tls ; web_port })
|
{ base_dir ; node_addr ; node_port ; tls ; web_port ; remote_signer })
|
||||||
(obj5
|
(obj6
|
||||||
(req "base_dir" string)
|
(req "base_dir" string)
|
||||||
(opt "node_addr" string)
|
(opt "node_addr" string)
|
||||||
(opt "node_port" int16)
|
(opt "node_port" int16)
|
||||||
(opt "tls" bool)
|
(opt "tls" bool)
|
||||||
(opt "web_port" int16))
|
(opt "web_port" int16)
|
||||||
|
(opt "remote_signer" RPC_client.uri_encoding))
|
||||||
|
|
||||||
let from_json json =
|
let from_json json =
|
||||||
Data_encoding.Json.destruct encoding json
|
Data_encoding.Json.destruct encoding json
|
||||||
@ -218,6 +233,17 @@ let tls_switch () =
|
|||||||
~short:'S'
|
~short:'S'
|
||||||
~doc:"use TLS to connect to node."
|
~doc:"use TLS to connect to node."
|
||||||
()
|
()
|
||||||
|
let remote_signer_arg () =
|
||||||
|
arg
|
||||||
|
~long:"remote-signer"
|
||||||
|
~short:'R'
|
||||||
|
~placeholder:"uri"
|
||||||
|
~doc:"URI of the remote signer"
|
||||||
|
(parameter
|
||||||
|
(fun _ x ->
|
||||||
|
(* TODO check scheme = 'unix/tcp/https' *)
|
||||||
|
try return (Uri.of_string x)
|
||||||
|
with _ -> fail (Invalid_remote_signer_argument x)))
|
||||||
|
|
||||||
let read_config_file config_file =
|
let read_config_file config_file =
|
||||||
Lwt_utils_unix.Json.read_file config_file >>=? fun cfg_json ->
|
Lwt_utils_unix.Json.read_file config_file >>=? fun cfg_json ->
|
||||||
@ -292,7 +318,8 @@ let commands config_file cfg =
|
|||||||
]
|
]
|
||||||
|
|
||||||
let global_options () =
|
let global_options () =
|
||||||
args9 (base_dir_arg ())
|
args10
|
||||||
|
(base_dir_arg ())
|
||||||
(config_file_arg ())
|
(config_file_arg ())
|
||||||
(timings_switch ())
|
(timings_switch ())
|
||||||
(block_arg ())
|
(block_arg ())
|
||||||
@ -301,6 +328,7 @@ let global_options () =
|
|||||||
(addr_arg ())
|
(addr_arg ())
|
||||||
(port_arg ())
|
(port_arg ())
|
||||||
(tls_switch ())
|
(tls_switch ())
|
||||||
|
(remote_signer_arg ())
|
||||||
|
|
||||||
let parse_config_args (ctx : #Client_context.full) argv =
|
let parse_config_args (ctx : #Client_context.full) argv =
|
||||||
parse_global_options
|
parse_global_options
|
||||||
@ -315,7 +343,8 @@ let parse_config_args (ctx : #Client_context.full) argv =
|
|||||||
log_requests,
|
log_requests,
|
||||||
node_addr,
|
node_addr,
|
||||||
node_port,
|
node_port,
|
||||||
tls), remaining) ->
|
tls,
|
||||||
|
remote_signer), remaining) ->
|
||||||
begin match base_dir with
|
begin match base_dir with
|
||||||
| None ->
|
| None ->
|
||||||
let base_dir = default_base_dir in
|
let base_dir = default_base_dir in
|
||||||
@ -352,7 +381,8 @@ let parse_config_args (ctx : #Client_context.full) argv =
|
|||||||
let tls = cfg.tls || tls in
|
let tls = cfg.tls || tls in
|
||||||
let node_addr = Option.unopt ~default:cfg.node_addr node_addr in
|
let node_addr = Option.unopt ~default:cfg.node_addr node_addr in
|
||||||
let node_port = Option.unopt ~default:cfg.node_port node_port in
|
let node_port = Option.unopt ~default:cfg.node_port node_port in
|
||||||
let cfg = { cfg with tls ; node_port ; node_addr } in
|
let remote_signer = Option.first_some remote_signer cfg.remote_signer in
|
||||||
|
let cfg = { cfg with tls ; node_port ; node_addr ; remote_signer } in
|
||||||
if Sys.file_exists base_dir && not (Sys.is_directory base_dir) then begin
|
if Sys.file_exists base_dir && not (Sys.is_directory base_dir) then begin
|
||||||
Format.eprintf "%s is not a directory.@." base_dir ;
|
Format.eprintf "%s is not a directory.@." base_dir ;
|
||||||
exit 1 ;
|
exit 1 ;
|
||||||
|
@ -122,6 +122,12 @@ let main select_commands =
|
|||||||
~block:parsed_args.block
|
~block:parsed_args.block
|
||||||
~base_dir:parsed_config_file.base_dir
|
~base_dir:parsed_config_file.base_dir
|
||||||
~rpc_config:rpc_config in
|
~rpc_config:rpc_config in
|
||||||
|
Option.iter parsed_config_file.remote_signer ~f: begin fun signer ->
|
||||||
|
Client_keys.register_signer
|
||||||
|
(module Tezos_signer_backends.Remote.Make(struct
|
||||||
|
let default = signer
|
||||||
|
end))
|
||||||
|
end ;
|
||||||
begin match autocomplete with
|
begin match autocomplete with
|
||||||
| Some (prev_arg, cur_arg, script) ->
|
| Some (prev_arg, cur_arg, script) ->
|
||||||
Clic.autocompletion
|
Clic.autocompletion
|
||||||
|
@ -15,8 +15,8 @@ let group =
|
|||||||
|
|
||||||
let encrypted_switch () =
|
let encrypted_switch () =
|
||||||
if List.exists
|
if List.exists
|
||||||
(fun (_, (module Signer : Client_keys.SIGNER)) ->
|
(fun (scheme, _) ->
|
||||||
Signer.scheme = Tezos_signer_backends.Unencrypted.scheme)
|
scheme = Tezos_signer_backends.Unencrypted.scheme)
|
||||||
(Client_keys.registered_signers ()) then
|
(Client_keys.registered_signers ()) then
|
||||||
Clic.switch
|
Clic.switch
|
||||||
~long:"encrypted"
|
~long:"encrypted"
|
||||||
|
@ -93,3 +93,4 @@ val generic_call :
|
|||||||
[< RPC_service.meth ] ->
|
[< RPC_service.meth ] ->
|
||||||
Uri.t -> (content, content) RPC_context.rest_result Lwt.t
|
Uri.t -> (content, content) RPC_context.rest_result Lwt.t
|
||||||
|
|
||||||
|
val uri_encoding: Uri.t Data_encoding.t
|
||||||
|
83
src/lib_signer_backends/remote.ml
Normal file
83
src/lib_signer_backends/remote.ml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
(**************************************************************************)
|
||||||
|
(* *)
|
||||||
|
(* 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 = "remote"
|
||||||
|
|
||||||
|
module Make(S : sig val default : Uri.t end) = struct
|
||||||
|
|
||||||
|
let scheme = scheme
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
let get_remote () =
|
||||||
|
match Uri.scheme S.default with
|
||||||
|
| Some "unix" -> (module Socket.Unix : SIGNER)
|
||||||
|
| Some "tcp" -> (module Socket.Tcp : SIGNER)
|
||||||
|
| Some "https" -> (module Https : SIGNER)
|
||||||
|
| _ -> assert false
|
||||||
|
|
||||||
|
module Remote = (val get_remote () : SIGNER)
|
||||||
|
let key =
|
||||||
|
match Uri.scheme S.default with
|
||||||
|
| Some "unix" | Some "tcp" ->
|
||||||
|
(fun uri ->
|
||||||
|
let key = Uri.path uri in
|
||||||
|
Uri.add_query_param S.default ("key", [key]))
|
||||||
|
| Some "https" ->
|
||||||
|
(fun uri ->
|
||||||
|
let key = Uri.path uri in
|
||||||
|
match Uri.path S.default with
|
||||||
|
| "" -> Uri.with_path S.default key
|
||||||
|
| path -> Uri.with_path S.default (path ^ "/" ^ key))
|
||||||
|
| _ -> assert false
|
||||||
|
|
||||||
|
let public_key pk_uri =
|
||||||
|
Remote.public_key
|
||||||
|
(Client_keys.make_pk_uri (key (pk_uri : pk_uri :> Uri.t)))
|
||||||
|
|
||||||
|
let public_key_hash pk_uri =
|
||||||
|
Remote.public_key_hash
|
||||||
|
(Client_keys.make_pk_uri (key (pk_uri : pk_uri :> Uri.t)))
|
||||||
|
|
||||||
|
let neuterize sk_uri =
|
||||||
|
Remote.neuterize
|
||||||
|
(Client_keys.make_sk_uri (key (sk_uri : sk_uri :> Uri.t)))
|
||||||
|
|
||||||
|
let sign ?watermark sk_uri msg =
|
||||||
|
Remote.sign
|
||||||
|
?watermark
|
||||||
|
(Client_keys.make_sk_uri (key (sk_uri : sk_uri :> Uri.t)))
|
||||||
|
msg
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
let make_sk sk =
|
||||||
|
Client_keys.make_sk_uri
|
||||||
|
(Uri.make ~scheme ~path:(Signature.Secret_key.to_b58check sk) ())
|
||||||
|
|
||||||
|
let make_pk pk =
|
||||||
|
Client_keys.make_pk_uri
|
||||||
|
(Uri.make ~scheme ~path:(Signature.Public_key.to_b58check pk) ())
|
13
src/lib_signer_backends/remote.mli
Normal file
13
src/lib_signer_backends/remote.mli
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
(**************************************************************************)
|
||||||
|
(* *)
|
||||||
|
(* Copyright (c) 2014 - 2017. *)
|
||||||
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||||||
|
(* *)
|
||||||
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
||||||
|
(* *)
|
||||||
|
(**************************************************************************)
|
||||||
|
|
||||||
|
module Make(S : sig val default : Uri.t end) : Client_keys.SIGNER
|
||||||
|
|
||||||
|
val make_pk: Signature.public_key -> Client_keys.pk_uri
|
||||||
|
val make_sk: Signature.secret_key -> Client_keys.sk_uri
|
@ -16,6 +16,11 @@ open Client_proto_programs
|
|||||||
open Client_keys
|
open Client_keys
|
||||||
open Client_proto_args
|
open Client_proto_args
|
||||||
|
|
||||||
|
let encrypted_switch =
|
||||||
|
Clic.switch
|
||||||
|
~long:"encrypted"
|
||||||
|
~doc:("Encrypt the key on-disk") ()
|
||||||
|
|
||||||
let report_michelson_errors ?(no_print_source=false) ~msg (cctxt : #Client_context.printer) = function
|
let report_michelson_errors ?(no_print_source=false) ~msg (cctxt : #Client_context.printer) = function
|
||||||
| Error errs ->
|
| Error errs ->
|
||||||
cctxt#warning "%a"
|
cctxt#warning "%a"
|
||||||
@ -264,7 +269,7 @@ let commands () =
|
|||||||
(args3
|
(args3
|
||||||
(Secret_key.force_switch ())
|
(Secret_key.force_switch ())
|
||||||
(Client_proto_args.no_confirmation)
|
(Client_proto_args.no_confirmation)
|
||||||
(Client_keys_commands.encrypted_switch ()))
|
encrypted_switch)
|
||||||
(prefixes [ "activate" ; "account" ]
|
(prefixes [ "activate" ; "account" ]
|
||||||
@@ Secret_key.fresh_alias_param
|
@@ Secret_key.fresh_alias_param
|
||||||
@@ prefixes [ "with" ]
|
@@ prefixes [ "with" ]
|
||||||
|
Loading…
Reference in New Issue
Block a user