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  }