github.com/theQRL/go-zond@v0.2.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  
   167  	return res, nil
   168  }
   169  
   170  func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
   171  	var signature hexutil.Bytes
   172  	var signAddress = common.NewMixedcaseAddress(account.Address)
   173  	if err := api.client.Call(&signature, "account_signData",
   174  		accounts.MimetypeTextPlain,
   175  		&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
   176  		hexutil.Encode(text)); err != nil {
   177  		return nil, err
   178  	}
   179  	return signature, nil
   180  }
   181  
   182  // signTransactionResult represents the signinig result returned by clef.
   183  type signTransactionResult struct {
   184  	Raw hexutil.Bytes      `json:"raw"`
   185  	Tx  *types.Transaction `json:"tx"`
   186  }
   187  
   188  // SignTx sends the transaction to the external signer.
   189  // If chainID is nil, or tx.ChainID is zero, the chain ID will be assigned
   190  // by the external signer. The chain ID of the transaction overrides the
   191  // chainID parameter.
   192  func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
   193  	data := hexutil.Bytes(tx.Data())
   194  	var to *common.MixedcaseAddress
   195  	if tx.To() != nil {
   196  		t := common.NewMixedcaseAddress(*tx.To())
   197  		to = &t
   198  	}
   199  	args := &apitypes.SendTxArgs{
   200  		Data:  &data,
   201  		Nonce: hexutil.Uint64(tx.Nonce()),
   202  		Value: hexutil.Big(*tx.Value()),
   203  		Gas:   hexutil.Uint64(tx.Gas()),
   204  		To:    to,
   205  		From:  common.NewMixedcaseAddress(account.Address),
   206  	}
   207  	switch tx.Type() {
   208  	case types.DynamicFeeTxType:
   209  		args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
   210  		args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
   211  	default:
   212  		return nil, fmt.Errorf("unsupported tx type %d", tx.Type())
   213  	}
   214  	// We should request the default chain id that we're operating with
   215  	// (the chain we're executing on)
   216  	if chainID != nil && chainID.Sign() != 0 {
   217  		args.ChainID = (*hexutil.Big)(chainID)
   218  	}
   219  	// However, if the user asked for a particular chain id, then we should
   220  	// use that instead.
   221  	if tx.ChainId().Sign() != 0 {
   222  		args.ChainID = (*hexutil.Big)(tx.ChainId())
   223  	}
   224  	accessList := tx.AccessList()
   225  	args.AccessList = &accessList
   226  	var res signTransactionResult
   227  	if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
   228  		return nil, err
   229  	}
   230  	return res.Tx, nil
   231  }
   232  
   233  func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
   234  	return []byte{}, errors.New("password-operations not supported on external signers")
   235  }
   236  
   237  func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
   238  	return nil, errors.New("password-operations not supported on external signers")
   239  }
   240  func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
   241  	return nil, errors.New("password-operations not supported on external signers")
   242  }
   243  
   244  func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
   245  	var res []common.Address
   246  	if err := api.client.Call(&res, "account_list"); err != nil {
   247  		return nil, err
   248  	}
   249  	return res, nil
   250  }
   251  
   252  func (api *ExternalSigner) pingVersion() (string, error) {
   253  	var v string
   254  	if err := api.client.Call(&v, "account_version"); err != nil {
   255  		return "", err
   256  	}
   257  	return v, nil
   258  }