2016-09-08 21:13:10 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
2017-11-14 03:36:14 +04:00
|
|
|
(* Copyright (c) 2014 - 2017. *)
|
2016-09-08 21:13:10 +04:00
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
2016-11-14 18:55:24 +04:00
|
|
|
module Ed25519 = Environment.Ed25519
|
|
|
|
|
2017-04-05 03:02:10 +04:00
|
|
|
module ContractEntity = struct
|
|
|
|
type t = Contract.t
|
|
|
|
let encoding = Contract.encoding
|
|
|
|
let of_source _ s =
|
|
|
|
match Contract.of_b58check s with
|
|
|
|
| Error _ as err ->
|
2017-10-27 20:53:07 +04:00
|
|
|
Lwt.return (Environment.wrap_error err)
|
2017-04-05 03:02:10 +04:00
|
|
|
|> trace (failure "bad contract notation")
|
|
|
|
| Ok s -> return s
|
|
|
|
let to_source _ s = return (Contract.to_b58check s)
|
|
|
|
let name = "contract"
|
|
|
|
end
|
|
|
|
|
|
|
|
module RawContractAlias = Client_aliases.Alias (ContractEntity)
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
module ContractAlias = struct
|
2017-04-05 03:02:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let find cctxt s =
|
2017-04-05 03:02:10 +04:00
|
|
|
RawContractAlias.find_opt cctxt s >>=? function
|
|
|
|
| Some v -> return (s, v)
|
2016-09-08 21:13:10 +04:00
|
|
|
| None ->
|
2017-04-05 03:02:10 +04:00
|
|
|
Client_keys.Public_key_hash.find_opt cctxt s >>=? function
|
2016-09-08 21:13:10 +04:00
|
|
|
| Some v ->
|
2017-04-05 03:02:10 +04:00
|
|
|
return (s, Contract.default_contract v)
|
2016-09-08 21:13:10 +04:00
|
|
|
| None ->
|
2017-04-07 23:21:20 +04:00
|
|
|
failwith "no contract or key named %s" s
|
2017-04-05 03:02:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let find_key cctxt name =
|
2017-04-05 03:02:10 +04:00
|
|
|
Client_keys.Public_key_hash.find cctxt name >>=? fun v ->
|
|
|
|
return (name, Contract.default_contract v)
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let rev_find cctxt c =
|
2016-09-08 21:13:10 +04:00
|
|
|
match Contract.is_default c with
|
|
|
|
| Some hash -> begin
|
2017-04-05 03:02:10 +04:00
|
|
|
Client_keys.Public_key_hash.rev_find cctxt hash >>=? function
|
|
|
|
| Some name -> return (Some ("key:" ^ name))
|
|
|
|
| None -> return None
|
2016-09-08 21:13:10 +04:00
|
|
|
end
|
2016-12-03 16:05:02 +04:00
|
|
|
| None -> RawContractAlias.rev_find cctxt c
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let get_contract cctxt s =
|
2016-09-08 21:13:10 +04:00
|
|
|
match Utils.split ~limit:1 ':' s with
|
|
|
|
| [ "key" ; key ]->
|
2016-12-03 16:05:02 +04:00
|
|
|
find_key cctxt key
|
|
|
|
| _ -> find cctxt s
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-09-27 11:55:20 +04:00
|
|
|
let autocomplete cctxt =
|
|
|
|
Client_keys.Public_key_hash.autocomplete cctxt >>=? fun keys ->
|
|
|
|
RawContractAlias.autocomplete cctxt >>=? fun contracts ->
|
|
|
|
return (List.map ((^) "key:") keys @ contracts)
|
|
|
|
|
2016-11-22 17:23:40 +04:00
|
|
|
let alias_param ?(name = "name") ?(desc = "existing contract alias") next =
|
|
|
|
let desc =
|
|
|
|
desc ^ "\n"
|
2017-11-27 20:23:21 +04:00
|
|
|
^ "can be a contract alias or a key alias (autodetected in this order)\n\
|
2016-11-22 17:23:40 +04:00
|
|
|
use 'key:name' to force the later" in
|
2017-09-27 11:55:20 +04:00
|
|
|
Cli_entries.(
|
|
|
|
param ~name ~desc
|
|
|
|
(parameter ~autocomplete:autocomplete
|
|
|
|
(fun cctxt p -> get_contract cctxt p))
|
|
|
|
next)
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2016-11-22 17:23:40 +04:00
|
|
|
let destination_param ?(name = "dst") ?(desc = "destination contract") next =
|
|
|
|
let desc =
|
|
|
|
desc ^ "\n"
|
2016-11-22 20:33:17 +04:00
|
|
|
^ "can be an alias, a key alias, or a literal (autodetected in this order)\n\
|
|
|
|
use 'text:literal', 'alias:name', 'key:name' to force" in
|
2017-09-27 11:55:20 +04:00
|
|
|
Cli_entries.(
|
|
|
|
param ~name ~desc
|
|
|
|
(parameter
|
|
|
|
~autocomplete:(fun cctxt ->
|
|
|
|
autocomplete cctxt >>=? fun list1 ->
|
|
|
|
Client_keys.Public_key_hash.autocomplete cctxt >>=? fun list2 ->
|
|
|
|
return (list1 @ list2))
|
|
|
|
(fun cctxt s ->
|
|
|
|
begin
|
|
|
|
match Utils.split ~limit:1 ':' s with
|
|
|
|
| [ "alias" ; alias ]->
|
|
|
|
find cctxt alias
|
|
|
|
| [ "key" ; text ] ->
|
|
|
|
Client_keys.Public_key_hash.find cctxt text >>=? fun v ->
|
|
|
|
return (s, Contract.default_contract v)
|
|
|
|
| _ ->
|
|
|
|
find cctxt s >>= function
|
|
|
|
| Ok v -> return v
|
|
|
|
| Error k_errs ->
|
|
|
|
ContractEntity.of_source cctxt s >>= function
|
|
|
|
| Ok v -> return (s, v)
|
2017-11-27 20:23:21 +04:00
|
|
|
| Error c_errs ->
|
|
|
|
Lwt.return (Error (k_errs @ c_errs))
|
2017-09-27 11:55:20 +04:00
|
|
|
end)))
|
2016-11-22 17:23:40 +04:00
|
|
|
next
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-11-13 19:34:00 +04:00
|
|
|
let name cctxt contract =
|
|
|
|
rev_find cctxt contract >>=? function
|
|
|
|
| None -> return (Contract.to_b58check contract)
|
|
|
|
| Some name -> return name
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2017-03-15 04:27:34 +04:00
|
|
|
module Contract_tags = Client_tags.Tags (struct
|
|
|
|
let name = "contract"
|
|
|
|
end)
|
|
|
|
|
2017-03-15 04:20:25 +04:00
|
|
|
let list_contracts cctxt =
|
|
|
|
(* List contracts *)
|
2017-04-05 03:02:10 +04:00
|
|
|
RawContractAlias.load cctxt >>=? fun raw_contracts ->
|
|
|
|
Lwt_list.map_s
|
|
|
|
(fun (n, v) -> Lwt.return ("", n, v))
|
2017-03-15 04:20:25 +04:00
|
|
|
raw_contracts >>= fun contracts ->
|
2017-04-05 03:02:10 +04:00
|
|
|
Client_keys.Public_key_hash.load cctxt >>=? fun keys ->
|
2017-03-15 04:20:25 +04:00
|
|
|
(* List accounts (default contracts of identities) *)
|
2017-04-05 03:02:10 +04:00
|
|
|
map_s (fun (n, v) ->
|
|
|
|
RawContractAlias.mem cctxt n >>=? fun mem ->
|
2017-03-15 04:20:25 +04:00
|
|
|
let p = if mem then "key:" else "" in
|
|
|
|
let v' = Contract.default_contract v in
|
2017-04-05 03:02:10 +04:00
|
|
|
return (p, n, v'))
|
|
|
|
keys >>=? fun accounts ->
|
|
|
|
return (contracts @ accounts)
|
2017-03-15 04:20:25 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let get_manager cctxt block source =
|
2016-09-08 21:13:10 +04:00
|
|
|
match Contract.is_default source with
|
|
|
|
| Some hash -> return hash
|
2016-12-03 16:05:02 +04:00
|
|
|
| None -> Client_proto_rpcs.Context.Contract.manager cctxt block source
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let get_delegate cctxt block source =
|
2016-09-08 21:13:10 +04:00
|
|
|
match Contract.is_default source with
|
|
|
|
| Some hash -> return hash
|
|
|
|
| None ->
|
2017-04-05 03:02:10 +04:00
|
|
|
Client_proto_rpcs.Context.Contract.delegate cctxt
|
|
|
|
block source >>=? function
|
|
|
|
| Some delegate ->
|
|
|
|
return delegate
|
|
|
|
| None ->
|
|
|
|
Client_proto_rpcs.Context.Contract.manager cctxt block source
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
let may_check_key sourcePubKey sourcePubKeyHash =
|
|
|
|
match sourcePubKey with
|
|
|
|
| Some sourcePubKey ->
|
2017-04-05 03:02:10 +04:00
|
|
|
fail_unless
|
|
|
|
(Ed25519.Public_key_hash.equal
|
|
|
|
(Ed25519.Public_key.hash sourcePubKey) sourcePubKeyHash)
|
|
|
|
(failure "Invalid public key in `client_proto_endorsement`")
|
|
|
|
| None ->
|
|
|
|
return ()
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let check_public_key cctxt block ?src_pk src_pk_hash =
|
|
|
|
Client_proto_rpcs.Context.Key.get cctxt block src_pk_hash >>= function
|
2016-09-08 21:13:10 +04:00
|
|
|
| Error errors ->
|
|
|
|
begin
|
|
|
|
match src_pk with
|
|
|
|
| None ->
|
|
|
|
let exn = Client_proto_rpcs.string_of_errors errors in
|
|
|
|
failwith "Unknown public key\n%s" exn
|
|
|
|
| Some key ->
|
|
|
|
may_check_key src_pk src_pk_hash >>=? fun () ->
|
|
|
|
return (Some key)
|
|
|
|
end
|
|
|
|
| Ok _ -> return None
|
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
let group =
|
|
|
|
{ Cli_entries.name = "contracts" ;
|
|
|
|
title = "Commands for managing the record of known contracts" }
|
|
|
|
|
2016-09-08 21:13:10 +04:00
|
|
|
let commands () =
|
|
|
|
let open Cli_entries in
|
2017-03-15 04:17:20 +04:00
|
|
|
let open Client_commands in
|
2016-09-08 21:13:10 +04:00
|
|
|
[
|
2017-04-05 03:02:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
command ~group ~desc: "add a contract to the wallet"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2016-09-08 21:13:10 +04:00
|
|
|
(prefixes [ "remember" ; "contract" ]
|
|
|
|
@@ RawContractAlias.fresh_alias_param
|
|
|
|
@@ RawContractAlias.source_param
|
|
|
|
@@ stop)
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () name hash cctxt ->
|
2017-04-05 03:02:10 +04:00
|
|
|
RawContractAlias.add cctxt name hash) ;
|
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
command ~group ~desc: "remove a contract from the wallet"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2016-09-08 21:13:10 +04:00
|
|
|
(prefixes [ "forget" ; "contract" ]
|
|
|
|
@@ RawContractAlias.alias_param
|
|
|
|
@@ stop)
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () (name, _) cctxt -> RawContractAlias.del cctxt name) ;
|
2017-04-05 03:02:10 +04:00
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
command ~group ~desc: "lists all known contracts"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2016-09-08 21:13:10 +04:00
|
|
|
(fixed [ "list" ; "known" ; "contracts" ])
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () cctxt ->
|
2017-04-05 03:02:10 +04:00
|
|
|
list_contracts cctxt >>=? fun contracts ->
|
|
|
|
iter_s
|
|
|
|
(fun (prefix, alias, contract) ->
|
|
|
|
cctxt.message "%s%s: %s" prefix alias
|
|
|
|
(Contract.to_b58check contract) >>= fun () ->
|
|
|
|
return ())
|
|
|
|
contracts) ;
|
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
command ~group ~desc: "forget all known contracts"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2016-09-08 21:13:10 +04:00
|
|
|
(fixed [ "forget" ; "all" ; "contracts" ])
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () cctxt ->
|
2017-04-05 03:02:10 +04:00
|
|
|
fail_unless
|
|
|
|
cctxt.config.force
|
|
|
|
(failure "this can only used with option -force true") >>=? fun () ->
|
|
|
|
RawContractAlias.save cctxt []) ;
|
|
|
|
|
2016-12-03 16:05:02 +04:00
|
|
|
command ~group ~desc: "display a contract from the wallet"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2016-09-08 21:13:10 +04:00
|
|
|
(prefixes [ "show" ; "known" ; "contract" ]
|
|
|
|
@@ RawContractAlias.alias_param
|
|
|
|
@@ stop)
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () (_, contract) cctxt ->
|
2017-04-05 01:35:41 +04:00
|
|
|
cctxt.message "%a\n%!" Contract.pp contract >>= fun () ->
|
|
|
|
return ()) ;
|
2017-04-05 03:02:10 +04:00
|
|
|
|
2017-03-15 04:27:34 +04:00
|
|
|
command ~group ~desc: "tag a contract in the wallet"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2017-03-15 04:27:34 +04:00
|
|
|
(prefixes [ "tag" ; "contract" ]
|
|
|
|
@@ RawContractAlias.alias_param
|
|
|
|
@@ prefixes [ "with" ]
|
|
|
|
@@ Contract_tags.tag_param
|
|
|
|
@@ stop)
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () (alias, _contract) new_tags cctxt ->
|
2017-04-05 03:02:10 +04:00
|
|
|
Contract_tags.find_opt cctxt alias >>=? fun tags ->
|
|
|
|
let new_tags =
|
|
|
|
match tags with
|
2017-03-15 04:27:34 +04:00
|
|
|
| None -> new_tags
|
|
|
|
| Some tags -> Utils.merge_list2 tags new_tags in
|
2017-04-05 03:02:10 +04:00
|
|
|
Contract_tags.update cctxt alias new_tags) ;
|
|
|
|
|
2017-03-15 04:27:34 +04:00
|
|
|
command ~group ~desc: "remove tag(s) from a contract in the wallet"
|
2017-09-19 13:31:35 +04:00
|
|
|
no_options
|
2017-03-15 04:27:34 +04:00
|
|
|
(prefixes [ "untag" ; "contract" ]
|
|
|
|
@@ RawContractAlias.alias_param
|
|
|
|
@@ prefixes [ "with" ]
|
|
|
|
@@ Contract_tags.tag_param
|
|
|
|
@@ stop)
|
2017-09-19 13:31:35 +04:00
|
|
|
(fun () (alias, _contract) new_tags cctxt ->
|
2017-04-05 03:02:10 +04:00
|
|
|
Contract_tags.find_opt cctxt alias >>=? fun tags ->
|
|
|
|
let new_tags =
|
|
|
|
match tags with
|
2017-03-15 04:27:34 +04:00
|
|
|
| None -> []
|
|
|
|
| Some tags ->
|
|
|
|
Utils.merge_filter_list2
|
|
|
|
~f:(fun x1 x2 -> match x1, x2 with
|
|
|
|
| None, None -> assert false
|
|
|
|
| None, Some _ -> None
|
|
|
|
| Some t1, Some t2 when t1 = t2 -> None
|
|
|
|
| Some t1, _ -> Some t1) tags new_tags in
|
2017-04-05 03:02:10 +04:00
|
|
|
Contract_tags.update cctxt alias new_tags) ;
|
|
|
|
|
2016-09-08 21:13:10 +04:00
|
|
|
]
|