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  }