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