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 }