github.com/mavryk-network/mvgo@v1.19.9/signer/remote/client.go (about) 1 // Copyright (c) 2020-2022 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 package remote 5 6 import ( 7 "context" 8 "net/http" 9 10 "github.com/mavryk-network/mvgo/codec" 11 "github.com/mavryk-network/mvgo/mavryk" 12 "github.com/mavryk-network/mvgo/rpc" 13 "github.com/mavryk-network/mvgo/signer" 14 ) 15 16 var _ signer.Signer = (*RemoteSigner)(nil) 17 18 type RemoteSigner struct { 19 c *rpc.Client 20 addrs []mavryk.Address 21 auth mavryk.PrivateKey 22 } 23 24 // New creates a new remote signer client and initializes it with the remote url. 25 // Users may pass an optional http client with a custom configuration, otherwise 26 // the http.DefaultClient is used. 27 func New(url string, client *http.Client) (*RemoteSigner, error) { 28 c, err := rpc.NewClient(url, client) 29 if err != nil { 30 return nil, err 31 } 32 return &RemoteSigner{c: c}, nil 33 } 34 35 func (s *RemoteSigner) WithAddress(addr mavryk.Address) *RemoteSigner { 36 s.addrs = append(s.addrs, addr) 37 return s 38 } 39 40 func (s *RemoteSigner) WithAuthKey(sk mavryk.PrivateKey) *RemoteSigner { 41 s.auth = sk 42 return s 43 } 44 45 // AuthorizedKeys returns a list of addresses the remote signer accepts for 46 // authenticating requests. 47 func (s RemoteSigner) AuthorizedKeys(ctx context.Context) ([]mavryk.Address, error) { 48 type response struct { 49 Addrs []mavryk.Address `json:"authorized_keys"` 50 } 51 var resp response 52 err := s.c.Get(ctx, "/authorized_keys", &resp) 53 if err != nil { 54 return nil, err 55 } 56 return resp.Addrs, nil 57 } 58 59 // ListAddresses returns a list of addresses the remote signer can produce signatures for. 60 func (s RemoteSigner) ListAddresses(ctx context.Context) ([]mavryk.Address, error) { 61 return s.addrs, nil 62 } 63 64 // GetKey returns the public key associated with address. 65 func (s RemoteSigner) GetKey(ctx context.Context, address mavryk.Address) (mavryk.Key, error) { 66 type response struct { 67 Pk mavryk.Key `json:"public_key"` 68 } 69 var resp response 70 err := s.c.Get(ctx, "/keys/"+address.String(), &resp) 71 return resp.Pk, err 72 } 73 74 // SignMessage signs msg for address by wrapping it into a failing noop operation 75 // with zero branch hash. This prevents unintended signature of message bytes that 76 // represent a valid transaction. 77 // 78 // Note that most remote signers for Tezos do not support signing of operation kinds other 79 // than baking related operations. 80 func (s RemoteSigner) SignMessage(ctx context.Context, address mavryk.Address, msg string) (mavryk.Signature, error) { 81 op := codec.NewOp(). 82 WithBranch(mavryk.ZeroBlockHash). 83 WithContents(&codec.FailingNoop{ 84 Arbitrary: msg, 85 }) 86 return s.SignOperation(ctx, address, op) 87 } 88 89 // SignOperation signs operation op for address using the configured remote signer's 90 // REST API. For endorsements this call requires branch_id to be present. 91 // 92 // Note that most remote signers for Tezos do not support signing of operation kinds other 93 // than baking related operations. 94 func (s RemoteSigner) SignOperation(ctx context.Context, address mavryk.Address, op *codec.Op) (mavryk.Signature, error) { 95 type response struct { 96 Sig mavryk.Signature `json:"signature"` 97 } 98 var resp response 99 err := s.c.Post(ctx, "/keys/"+address.String(), mavryk.HexBytes(op.WatermarkedBytes()), &resp) 100 return resp.Sig, err 101 } 102 103 // SignOperation signs a block header for address using the configured remote signer's 104 // REST API. This call requires branch_id to be present. 105 func (s RemoteSigner) SignBlock(ctx context.Context, address mavryk.Address, head *codec.BlockHeader) (mavryk.Signature, error) { 106 type response struct { 107 Sig mavryk.Signature `json:"signature"` 108 } 109 var resp response 110 err := s.c.Post(ctx, "/keys/"+address.String(), mavryk.HexBytes(head.WatermarkedBytes()), &resp) 111 return resp.Sig, err 112 }