github.com/theQRL/go-zond@v0.1.1/accounts/external/backend.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package external 18 19 import ( 20 "errors" 21 "fmt" 22 "math/big" 23 "sync" 24 25 "github.com/theQRL/go-zond" 26 "github.com/theQRL/go-zond/accounts" 27 "github.com/theQRL/go-zond/common" 28 "github.com/theQRL/go-zond/common/hexutil" 29 "github.com/theQRL/go-zond/core/types" 30 "github.com/theQRL/go-zond/event" 31 "github.com/theQRL/go-zond/log" 32 "github.com/theQRL/go-zond/rpc" 33 "github.com/theQRL/go-zond/signer/core/apitypes" 34 ) 35 36 type ExternalBackend struct { 37 signers []accounts.Wallet 38 } 39 40 func (eb *ExternalBackend) Wallets() []accounts.Wallet { 41 return eb.signers 42 } 43 44 func NewExternalBackend(endpoint string) (*ExternalBackend, error) { 45 signer, err := NewExternalSigner(endpoint) 46 if err != nil { 47 return nil, err 48 } 49 return &ExternalBackend{ 50 signers: []accounts.Wallet{signer}, 51 }, nil 52 } 53 54 func (eb *ExternalBackend) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { 55 return event.NewSubscription(func(quit <-chan struct{}) error { 56 <-quit 57 return nil 58 }) 59 } 60 61 // ExternalSigner provides an API to interact with an external signer (clef) 62 // It proxies request to the external signer while forwarding relevant 63 // request headers 64 type ExternalSigner struct { 65 client *rpc.Client 66 endpoint string 67 status string 68 cacheMu sync.RWMutex 69 cache []accounts.Account 70 } 71 72 func NewExternalSigner(endpoint string) (*ExternalSigner, error) { 73 client, err := rpc.Dial(endpoint) 74 if err != nil { 75 return nil, err 76 } 77 extsigner := &ExternalSigner{ 78 client: client, 79 endpoint: endpoint, 80 } 81 // Check if reachable 82 version, err := extsigner.pingVersion() 83 if err != nil { 84 return nil, err 85 } 86 extsigner.status = fmt.Sprintf("ok [version=%v]", version) 87 return extsigner, nil 88 } 89 90 func (api *ExternalSigner) URL() accounts.URL { 91 return accounts.URL{ 92 Scheme: "extapi", 93 Path: api.endpoint, 94 } 95 } 96 97 func (api *ExternalSigner) Status() (string, error) { 98 return api.status, nil 99 } 100 101 func (api *ExternalSigner) Open(passphrase string) error { 102 return errors.New("operation not supported on external signers") 103 } 104 105 func (api *ExternalSigner) Close() error { 106 return errors.New("operation not supported on external signers") 107 } 108 109 func (api *ExternalSigner) Accounts() []accounts.Account { 110 var accnts []accounts.Account 111 res, err := api.listAccounts() 112 if err != nil { 113 log.Error("account listing failed", "error", err) 114 return accnts 115 } 116 for _, addr := range res { 117 accnts = append(accnts, accounts.Account{ 118 URL: accounts.URL{ 119 Scheme: "extapi", 120 Path: api.endpoint, 121 }, 122 Address: addr, 123 }) 124 } 125 api.cacheMu.Lock() 126 api.cache = accnts 127 api.cacheMu.Unlock() 128 return accnts 129 } 130 131 func (api *ExternalSigner) Contains(account accounts.Account) bool { 132 api.cacheMu.RLock() 133 defer api.cacheMu.RUnlock() 134 if api.cache == nil { 135 // If we haven't already fetched the accounts, it's time to do so now 136 api.cacheMu.RUnlock() 137 api.Accounts() 138 api.cacheMu.RLock() 139 } 140 for _, a := range api.cache { 141 if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) { 142 return true 143 } 144 } 145 return false 146 } 147 148 func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { 149 return accounts.Account{}, errors.New("operation not supported on external signers") 150 } 151 152 func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain zond.ChainStateReader) { 153 log.Error("operation SelfDerive not supported on external signers") 154 } 155 156 // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed 157 func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { 158 var res hexutil.Bytes 159 var signAddress = common.NewMixedcaseAddress(account.Address) 160 if err := api.client.Call(&res, "account_signData", 161 mimeType, 162 &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined 163 hexutil.Encode(data)); err != nil { 164 return nil, err 165 } 166 // If V is on 27/28-form, convert to 0/1 for Clique 167 if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) { 168 res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use 169 } 170 return res, nil 171 } 172 173 func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) { 174 var signature hexutil.Bytes 175 var signAddress = common.NewMixedcaseAddress(account.Address) 176 if err := api.client.Call(&signature, "account_signData", 177 accounts.MimetypeTextPlain, 178 &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined 179 hexutil.Encode(text)); err != nil { 180 return nil, err 181 } 182 if signature[64] == 27 || signature[64] == 28 { 183 // If clef is used as a backend, it may already have transformed 184 // the signature to ethereum-type signature. 185 signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1 186 } 187 return signature, nil 188 } 189 190 // signTransactionResult represents the signinig result returned by clef. 191 type signTransactionResult struct { 192 Raw hexutil.Bytes `json:"raw"` 193 Tx *types.Transaction `json:"tx"` 194 } 195 196 // SignTx sends the transaction to the external signer. 197 // If chainID is nil, or tx.ChainID is zero, the chain ID will be assigned 198 // by the external signer. For non-legacy transactions, the chain ID of the 199 // transaction overrides the chainID parameter. 200 func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 201 data := hexutil.Bytes(tx.Data()) 202 var to *common.MixedcaseAddress 203 if tx.To() != nil { 204 t := common.NewMixedcaseAddress(*tx.To()) 205 to = &t 206 } 207 args := &apitypes.SendTxArgs{ 208 Data: &data, 209 Nonce: hexutil.Uint64(tx.Nonce()), 210 Value: hexutil.Big(*tx.Value()), 211 Gas: hexutil.Uint64(tx.Gas()), 212 To: to, 213 From: common.NewMixedcaseAddress(account.Address), 214 } 215 switch tx.Type() { 216 case types.LegacyTxType, types.AccessListTxType: 217 args.GasPrice = (*hexutil.Big)(tx.GasPrice()) 218 case types.DynamicFeeTxType: 219 args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap()) 220 args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap()) 221 default: 222 return nil, fmt.Errorf("unsupported tx type %d", tx.Type()) 223 } 224 // We should request the default chain id that we're operating with 225 // (the chain we're executing on) 226 if chainID != nil && chainID.Sign() != 0 { 227 args.ChainID = (*hexutil.Big)(chainID) 228 } 229 if tx.Type() != types.LegacyTxType { 230 // However, if the user asked for a particular chain id, then we should 231 // use that instead. 232 if tx.ChainId().Sign() != 0 { 233 args.ChainID = (*hexutil.Big)(tx.ChainId()) 234 } 235 accessList := tx.AccessList() 236 args.AccessList = &accessList 237 } 238 var res signTransactionResult 239 if err := api.client.Call(&res, "account_signTransaction", args); err != nil { 240 return nil, err 241 } 242 return res.Tx, nil 243 } 244 245 func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { 246 return []byte{}, errors.New("password-operations not supported on external signers") 247 } 248 249 func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 250 return nil, errors.New("password-operations not supported on external signers") 251 } 252 func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { 253 return nil, errors.New("password-operations not supported on external signers") 254 } 255 256 func (api *ExternalSigner) listAccounts() ([]common.Address, error) { 257 var res []common.Address 258 if err := api.client.Call(&res, "account_list"); err != nil { 259 return nil, err 260 } 261 return res, nil 262 } 263 264 func (api *ExternalSigner) pingVersion() (string, error) { 265 var v string 266 if err := api.client.Call(&v, "account_version"); err != nil { 267 return "", err 268 } 269 return v, nil 270 }