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