Raw File
client_proto_utils_commands.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2020 Metastate AG <hello@metastate.ch>                      *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

open Client_proto_utils

let group = {Tezos_clic.name = "utilities"; title = "Utility Commands"}

let unsigned_block_header_param =
  let open Tezos_clic in
  param
    ~name:"unsigned block header"
    ~desc:"A hex or JSON encoded unsigned block header"
  @@ parameter (fun (cctxt : #Client_context.full) s ->
         let open Lwt_result_syntax in
         let bytes_opt = `Hex s |> Hex.to_bytes in
         let enc = Protocol.Alpha_context.Block_header.unsigned_encoding in
         Option.bind bytes_opt (Data_encoding.Binary.of_bytes_opt enc)
         |> function
         | Some s -> return s
         | None -> (
             let open Data_encoding.Json in
             let error =
               "Cannot decode unsigned block header: is it valid JSON or \
                hexadecimal?"
             in
             from_string s |> function
             | Error _ -> cctxt#error "%s" error
             | Ok json -> (
                 try destruct enc json |> return
                 with _ -> cctxt#error "%s" error)))

let commands () =
  let open Tezos_clic in
  let string_param ~name ~desc =
    param ~name ~desc Client_proto_args.string_parameter
  in
  let block_arg =
    default_arg
      ~long:"branch"
      ~short:'b'
      ~placeholder:"hash|tag"
      ~doc:
        "Block hash used to create the no-op operation to sign (possible tags \
         are 'head' and 'genesis'). Defaults to 'genesis'. Note that the the \
         genesis block hash is network-dependent."
      ~default:(Block_services.to_string `Genesis)
      (Client_config.block_parameter ())
  in
  [
    command
      ~group
      ~desc:
        "Sign a message and display it using the failing_noop operation. This \
         operation is not executable in the protocol. Please note that \
         signing/checking an arbitrary message in itself is not sufficient to \
         verify a key ownership"
      (args1 block_arg)
      (prefixes ["sign"; "message"]
      @@ string_param ~name:"message" ~desc:"message to sign"
      @@ prefixes ["for"]
      @@ Client_keys.Secret_key.source_param
           ~name:"src"
           ~desc:"name of the signer contract"
      @@ stop)
      (fun block_head message src_sk cctxt ->
        let open Lwt_result_syntax in
        let* block =
          Shell_services.Blocks.hash
            cctxt
            ~chain:cctxt#chain
            ~block:block_head
            ()
        in
        let* signature = sign_message cctxt ~src_sk ~block ~message in
        let*! () = cctxt#message "Signature: %a" Signature.pp signature in
        return_unit);
    command
      ~group
      ~desc:
        "Check the signature of an arbitrary message using the failing_noop \
         operation. Please note that signing/checking an arbitrary message in \
         itself is not sufficient to verify a key ownership."
      (args2
         block_arg
         (switch ~doc:"Use only exit codes" ~short:'q' ~long:"quiet" ()))
      (prefixes ["check"; "that"; "message"]
      @@ string_param ~name:"message" ~desc:"signed message"
      @@ prefixes ["was"; "signed"; "by"]
      @@ Client_keys.Public_key.source_param
           ~name:"signer"
           ~desc:"name of the signer contract"
      @@ prefixes ["to"; "produce"]
      @@ param
           ~name:"signature"
           ~desc:"the signature to check"
           Client_proto_args.signature_parameter
      @@ stop)
      (fun (block_head, quiet)
           message
           (key_locator, _)
           signature
           (cctxt : #Protocol_client_context.full) ->
        let open Lwt_result_syntax in
        let* block =
          Shell_services.Blocks.hash
            cctxt
            ~chain:cctxt#chain
            ~block:block_head
            ()
        in
        let* check =
          check_message cctxt ~key_locator ~block ~quiet ~message ~signature
        in
        if check then
          if quiet then return_unit
          else
            let*! () = cctxt#message "Signature check successful" in
            return_unit
        else cctxt#error "invalid signature");
    command
      ~group
      ~desc:
        "Sign an arbitrary unsigned block header for a given delegate and \
         return the signed block."
      no_options
      (prefixes ["sign"; "block"]
      @@ unsigned_block_header_param @@ prefixes ["for"]
      @@ Client_keys.Public_key_hash.source_param
           ~name:"delegate"
           ~desc:"signing delegate"
      @@ stop)
      (fun ()
           unsigned_block_header
           delegate
           (cctxt : #Protocol_client_context.full) ->
        let open Lwt_result_syntax in
        let unsigned_header =
          Data_encoding.Binary.to_bytes_exn
            Protocol.Alpha_context.Block_header.unsigned_encoding
            unsigned_block_header
        in
        let* chain_id =
          Shell_services.Chain.chain_id cctxt ~chain:cctxt#chain ()
        in
        let* _, _, sk = Client_keys.get_key cctxt delegate in
        let* s =
          Client_keys.sign
            cctxt
            ~watermark:
              (Protocol.Alpha_context.Block_header.to_watermark
                 (Block_header chain_id))
            sk
            unsigned_header
        in
        let*! () = cctxt#message "%a" Hex.pp (Signature.to_hex s) in
        return_unit);
  ]
back to top