github.com/Consensys/quorum@v21.1.0+incompatible/plugin/account/gateway.go (about)

     1  package account
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum/accounts"
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethereum/go-ethereum/crypto"
    15  	"github.com/ethereum/go-ethereum/log"
    16  	"github.com/jpmorganchase/quorum-account-plugin-sdk-go/proto"
    17  )
    18  
    19  type service struct {
    20  	client      proto.AccountServiceClient
    21  	mu          sync.Mutex
    22  	isStreaming bool
    23  }
    24  
    25  func (g *service) Status(ctx context.Context) (string, error) {
    26  	resp, err := g.client.Status(ctx, &proto.StatusRequest{})
    27  	if err != nil {
    28  		return "", err
    29  	}
    30  	if resp == nil {
    31  		return "", errors.New("empty response from plugin")
    32  	}
    33  	return resp.Status, err
    34  }
    35  
    36  func (g *service) Open(ctx context.Context, passphrase string) error {
    37  	_, err := g.client.Open(ctx, &proto.OpenRequest{Passphrase: passphrase})
    38  	return err
    39  }
    40  
    41  func (g *service) Close(ctx context.Context) error {
    42  	_, err := g.client.Close(ctx, &proto.CloseRequest{})
    43  	return err
    44  }
    45  
    46  func (g *service) Accounts(ctx context.Context) []accounts.Account {
    47  	resp, err := g.client.Accounts(ctx, &proto.AccountsRequest{})
    48  	if err != nil {
    49  		log.Error("unable to get accounts from plugin account store", "err", err)
    50  		return []accounts.Account{}
    51  	}
    52  	if resp == nil {
    53  		log.Error("empty response from plugin")
    54  		return []accounts.Account{}
    55  	}
    56  
    57  	return asAccounts(resp.Accounts)
    58  }
    59  
    60  func (g *service) Contains(ctx context.Context, account accounts.Account) bool {
    61  	resp, err := g.client.Contains(ctx, &proto.ContainsRequest{Address: account.Address.Bytes()})
    62  	if err != nil {
    63  		log.Error("unable to check contents of plugin account store", "err", err)
    64  	}
    65  	if resp == nil {
    66  		log.Error("empty response from plugin")
    67  		return false
    68  	}
    69  
    70  	return resp.IsContained
    71  }
    72  
    73  func (g *service) Sign(ctx context.Context, account accounts.Account, toSign []byte) ([]byte, error) {
    74  	resp, err := g.client.Sign(ctx, &proto.SignRequest{
    75  		Address: account.Address.Bytes(),
    76  		ToSign:  toSign,
    77  	})
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	if resp == nil {
    82  		return nil, errors.New("empty response from plugin")
    83  	}
    84  
    85  	return resp.Sig, nil
    86  }
    87  
    88  func (g *service) UnlockAndSign(ctx context.Context, account accounts.Account, toSign []byte, passphrase string) ([]byte, error) {
    89  	resp, err := g.client.UnlockAndSign(ctx, &proto.UnlockAndSignRequest{
    90  		Address:    account.Address.Bytes(),
    91  		ToSign:     toSign,
    92  		Passphrase: passphrase,
    93  	})
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	if resp == nil {
    98  		return nil, errors.New("empty response from plugin")
    99  	}
   100  
   101  	return resp.Sig, nil
   102  }
   103  
   104  func (g *service) TimedUnlock(ctx context.Context, account accounts.Account, password string, duration time.Duration) error {
   105  	_, err := g.client.TimedUnlock(ctx, &proto.TimedUnlockRequest{Address: account.Address.Bytes(), Password: password, Duration: duration.Nanoseconds()})
   106  	return err
   107  }
   108  
   109  func (g *service) Lock(ctx context.Context, account accounts.Account) error {
   110  	_, err := g.client.Lock(ctx, &proto.LockRequest{Address: account.Address.Bytes()})
   111  	return err
   112  }
   113  
   114  func (g *service) NewAccount(ctx context.Context, newAccountConfig interface{}) (accounts.Account, error) {
   115  	byt, err := json.Marshal(newAccountConfig)
   116  	if err != nil {
   117  		return accounts.Account{}, err
   118  	}
   119  
   120  	req := &proto.NewAccountRequest{
   121  		NewAccountConfig: byt,
   122  	}
   123  	resp, err := g.client.NewAccount(ctx, req)
   124  	if err != nil {
   125  		return accounts.Account{}, err
   126  	}
   127  
   128  	acct, err := asAccount(resp.Account)
   129  	if err != nil {
   130  		return accounts.Account{}, err
   131  	}
   132  
   133  	return acct, nil
   134  }
   135  
   136  func (g *service) ImportRawKey(ctx context.Context, rawKey string, newAccountConfig interface{}) (accounts.Account, error) {
   137  	byt, err := json.Marshal(newAccountConfig)
   138  	if err != nil {
   139  		return accounts.Account{}, err
   140  	}
   141  	// validate the rawKey
   142  	_, err = crypto.HexToECDSA(rawKey)
   143  	if err != nil {
   144  		return accounts.Account{}, err
   145  	}
   146  	req := &proto.ImportRawKeyRequest{
   147  		RawKey:           rawKey,
   148  		NewAccountConfig: byt,
   149  	}
   150  	resp, err := g.client.ImportRawKey(ctx, req)
   151  	if err != nil {
   152  		return accounts.Account{}, err
   153  	}
   154  
   155  	acct, err := asAccount(resp.Account)
   156  	if err != nil {
   157  		return accounts.Account{}, err
   158  	}
   159  
   160  	return acct, nil
   161  }
   162  
   163  func asAccounts(pAccts []*proto.Account) []accounts.Account {
   164  	accts := make([]accounts.Account, 0, len(pAccts))
   165  
   166  	for i, pAcct := range pAccts {
   167  		acct, err := asAccount(pAcct)
   168  		if err != nil {
   169  			log.Error("unable to parse account from plugin account store", "index", i, "err", err)
   170  			continue
   171  		}
   172  
   173  		accts = append(accts, acct)
   174  	}
   175  
   176  	return accts
   177  }
   178  
   179  func asAccount(pAcct *proto.Account) (accounts.Account, error) {
   180  	addr := strings.TrimSpace(common.Bytes2Hex(pAcct.Address))
   181  
   182  	if !common.IsHexAddress(addr) {
   183  		return accounts.Account{}, fmt.Errorf("invalid hex address: %v", addr)
   184  	}
   185  
   186  	url, err := ToUrl(pAcct.Url)
   187  	if err != nil {
   188  		return accounts.Account{}, err
   189  	}
   190  
   191  	acct := accounts.Account{
   192  		Address: common.HexToAddress(addr),
   193  		URL:     url,
   194  	}
   195  
   196  	return acct, nil
   197  }
   198  
   199  func ToUrl(strUrl string) (accounts.URL, error) {
   200  	if strUrl == "" {
   201  		return accounts.URL{}, nil
   202  	}
   203  
   204  	//to parse a string url as an accounts.URL it must first be in json format
   205  	toParse := fmt.Sprintf("\"%v\"", strUrl)
   206  
   207  	var url accounts.URL
   208  	if err := url.UnmarshalJSON([]byte(toParse)); err != nil {
   209  		return accounts.URL{}, err
   210  	}
   211  
   212  	return url, nil
   213  }