-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
235 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
// Copyright 2019 The go-ethereum Authors | ||
// This file is part of the go-ethereum library. | ||
// | ||
// The go-ethereum library is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU Lesser General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// The go-ethereum library is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU Lesser General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Lesser General Public License | ||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package external | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
"sync" | ||
|
||
"github.com/sero-cash/go-sero/accounts" | ||
"github.com/sero-cash/go-sero/common" | ||
"github.com/sero-cash/go-sero/common/hexutil" | ||
"github.com/sero-cash/go-sero/core/types" | ||
"github.com/sero-cash/go-sero/event" | ||
"github.com/sero-cash/go-sero/internal/ethapi" | ||
"github.com/sero-cash/go-sero/log" | ||
"github.com/sero-cash/go-sero/rpc" | ||
"github.com/sero-cash/go-sero/signer/core" | ||
) | ||
|
||
type ExternalBackend struct { | ||
signers []accounts.Wallet | ||
} | ||
|
||
func (eb *ExternalBackend) Wallets() []accounts.Wallet { | ||
return eb.signers | ||
} | ||
|
||
func NewExternalBackend(endpoint string) (*ExternalBackend, error) { | ||
signer, err := NewExternalSigner(endpoint) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &ExternalBackend{ | ||
signers: []accounts.Wallet{signer}, | ||
}, nil | ||
} | ||
|
||
func (eb *ExternalBackend) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { | ||
return event.NewSubscription(func(quit <-chan struct{}) error { | ||
<-quit | ||
return nil | ||
}) | ||
} | ||
|
||
// ExternalSigner provides an API to interact with an external signer (clef) | ||
// It proxies request to the external signer while forwarding relevant | ||
// request headers | ||
type ExternalSigner struct { | ||
client *rpc.Client | ||
endpoint string | ||
status string | ||
cacheMu sync.RWMutex | ||
cache []accounts.Account | ||
} | ||
|
||
func NewExternalSigner(endpoint string) (*ExternalSigner, error) { | ||
client, err := rpc.Dial(endpoint) | ||
if err != nil { | ||
return nil, err | ||
} | ||
extsigner := &ExternalSigner{ | ||
client: client, | ||
endpoint: endpoint, | ||
} | ||
// Check if reachable | ||
version, err := extsigner.pingVersion() | ||
if err != nil { | ||
return nil, err | ||
} | ||
extsigner.status = fmt.Sprintf("ok [version=%v]", version) | ||
return extsigner, nil | ||
} | ||
|
||
func (api *ExternalSigner) URL() accounts.URL { | ||
return accounts.URL{ | ||
Scheme: "extapi", | ||
Path: api.endpoint, | ||
} | ||
} | ||
|
||
func (api *ExternalSigner) Status() (string, error) { | ||
return api.status, nil | ||
} | ||
|
||
func (api *ExternalSigner) Open(passphrase string) error { | ||
return fmt.Errorf("operation not supported on external signers") | ||
} | ||
|
||
func (api *ExternalSigner) Close() error { | ||
return fmt.Errorf("operation not supported on external signers") | ||
} | ||
|
||
func (api *ExternalSigner) Accounts() []accounts.Account { | ||
var accnts []accounts.Account | ||
res, err := api.listAccounts() | ||
if err != nil { | ||
log.Error("account listing failed", "error", err) | ||
return accnts | ||
} | ||
for _, addr := range res { | ||
accnts = append(accnts, accounts.Account{ | ||
URL: accounts.URL{ | ||
Scheme: "extapi", | ||
Path: api.endpoint, | ||
}, | ||
Address: addr, | ||
}) | ||
} | ||
api.cacheMu.Lock() | ||
api.cache = accnts | ||
api.cacheMu.Unlock() | ||
return accnts | ||
} | ||
|
||
func (api *ExternalSigner) Contains(account accounts.Account) bool { | ||
api.cacheMu.RLock() | ||
defer api.cacheMu.RUnlock() | ||
for _, a := range api.cache { | ||
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { | ||
return accounts.Account{}, fmt.Errorf("operation not supported on external signers") | ||
} | ||
|
||
func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) { | ||
log.Error("operation SelfDerive not supported on external signers") | ||
} | ||
|
||
func (api *ExternalSigner) signHash(account accounts.Account, hash []byte) ([]byte, error) { | ||
return []byte{}, fmt.Errorf("operation not supported on external signers") | ||
} | ||
|
||
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed | ||
func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { | ||
var res hexutil.Bytes | ||
var signAddress = common.NewMixedcaseAddress(account.Address) | ||
if err := api.client.Call(&res, "account_signData", | ||
mimeType, | ||
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined | ||
hexutil.Encode(data)); err != nil { | ||
return nil, err | ||
} | ||
// If V is on 27/28-form, convert to to 0/1 for Clique | ||
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) { | ||
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use | ||
} | ||
return res, nil | ||
} | ||
|
||
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) { | ||
var res hexutil.Bytes | ||
var signAddress = common.NewMixedcaseAddress(account.Address) | ||
if err := api.client.Call(&res, "account_signData", | ||
accounts.MimetypeTextPlain, | ||
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined | ||
hexutil.Encode(text)); err != nil { | ||
return nil, err | ||
} | ||
return res, nil | ||
} | ||
|
||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { | ||
res := ethapi.SignTransactionResult{} | ||
data := hexutil.Bytes(tx.Data()) | ||
var to *common.MixedcaseAddress | ||
if tx.To() != nil { | ||
t := common.NewMixedcaseAddress(*tx.To()) | ||
to = &t | ||
} | ||
args := &core.SendTxArgs{ | ||
Data: &data, | ||
Nonce: hexutil.Uint64(tx.Nonce()), | ||
Value: hexutil.Big(*tx.Value()), | ||
Gas: hexutil.Uint64(tx.Gas()), | ||
GasPrice: hexutil.Big(*tx.GasPrice()), | ||
To: to, | ||
From: common.NewMixedcaseAddress(account.Address), | ||
} | ||
if err := api.client.Call(&res, "account_signTransaction", args); err != nil { | ||
return nil, err | ||
} | ||
return res.Tx, nil | ||
} | ||
|
||
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { | ||
return []byte{}, fmt.Errorf("passphrase-operations not supported on external signers") | ||
} | ||
|
||
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { | ||
return nil, fmt.Errorf("passphrase-operations not supported on external signers") | ||
} | ||
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { | ||
return nil, fmt.Errorf("passphrase-operations not supported on external signers") | ||
} | ||
|
||
func (api *ExternalSigner) listAccounts() ([]common.Address, error) { | ||
var res []common.Address | ||
if err := api.client.Call(&res, "account_list"); err != nil { | ||
return nil, err | ||
} | ||
return res, nil | ||
} | ||
|
||
func (api *ExternalSigner) pingVersion() (string, error) { | ||
var v string | ||
if err := api.client.Call(&v, "account_version"); err != nil { | ||
return "", err | ||
} | ||
return v, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters