accounts: support for external signer API (#18079)

This commit is contained in:
Daniel Liu 2025-01-14 10:56:08 +08:00
parent f0ed12486a
commit fe14069bd7
6 changed files with 82 additions and 18 deletions

View file

@ -89,7 +89,19 @@ type Wallet interface {
// chain state reader. // chain state reader.
SelfDerive(base DerivationPath, chain ethereum.ChainStateReader) SelfDerive(base DerivationPath, chain ethereum.ChainStateReader)
// SignHash requests the wallet to sign the given hash. // SignData requests the wallet to sign the hash of the given data
// It looks up the account specified either solely via its address contained within,
// or optionally with the aid of any location metadata from the embedded URL field.
//
// If the wallet requires additional authentication to sign the request (e.g.
// a password to decrypt the account, or a PIN code o verify the transaction),
// an AuthNeededError instance will be returned, containing infos for the user
// about which fields or actions are needed. The user may retry by providing
// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
// the account in a keystore).
SignData(account Account, mimeType string, data []byte) ([]byte, error)
// SignText requests the wallet to sign the given hash.
// //
// It looks up the account specified either solely via its address contained within, // It looks up the account specified either solely via its address contained within,
// or optionally with the aid of any location metadata from the embedded URL field. // or optionally with the aid of any location metadata from the embedded URL field.
@ -100,7 +112,7 @@ type Wallet interface {
// about which fields or actions are needed. The user may retry by providing // about which fields or actions are needed. The user may retry by providing
// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
// the account in a keystore). // the account in a keystore).
SignHash(account Account, hash []byte) ([]byte, error) SignText(account Account, text []byte) ([]byte, error)
// SignTx requests the wallet to sign the given transaction. // SignTx requests the wallet to sign the given transaction.
// //
@ -115,12 +127,12 @@ type Wallet interface {
// the account in a keystore). // the account in a keystore).
SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
// SignHashWithPassphrase requests the wallet to sign the given hash with the // SignTextWithPassphrase requests the wallet to sign the given hash with the
// given passphrase as extra authentication information. // given passphrase as extra authentication information.
// //
// It looks up the account specified either solely via its address contained within, // It looks up the account specified either solely via its address contained within,
// or optionally with the aid of any location metadata from the embedded URL field. // or optionally with the aid of any location metadata from the embedded URL field.
SignHashWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error) SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
// SignTxWithPassphrase requests the wallet to sign the given transaction, with the // SignTxWithPassphrase requests the wallet to sign the given transaction, with the
// given passphrase as extra authentication information. // given passphrase as extra authentication information.

32
accounts/accounts_test.go Normal file
View file

@ -0,0 +1,32 @@
// 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 accounts
import (
"bytes"
"testing"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
)
func TestTextHash(t *testing.T) {
hash := TextHash([]byte("Hello Joe"))
want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
if !bytes.Equal(hash, want) {
t.Fatalf("wrong hash: %x", hash)
}
}

View file

@ -22,6 +22,7 @@ import (
ethereum "github.com/XinFinOrg/XDPoSChain" ethereum "github.com/XinFinOrg/XDPoSChain"
"github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
) )
// keystoreWallet implements the accounts.Wallet interface for the original // keystoreWallet implements the accounts.Wallet interface for the original
@ -78,11 +79,11 @@ func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (account
// there is no notion of hierarchical account derivation for plain keystore accounts. // there is no notion of hierarchical account derivation for plain keystore accounts.
func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {} func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {}
// SignHash implements accounts.Wallet, attempting to sign the given hash with // signHash attempts to sign the given hash with
// the given account. If the wallet does not wrap this particular account, an // the given account. If the wallet does not wrap this particular account, an
// error is returned to avoid account leakage (even though in theory we may be // error is returned to avoid account leakage (even though in theory we may be
// able to sign via our shared keystore backend). // able to sign via our shared keystore backend).
func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if !w.Contains(account) { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
@ -91,6 +92,15 @@ func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte
return w.keystore.SignHash(account, hash) return w.keystore.SignHash(account, hash)
} }
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
return w.signHash(account, crypto.Keccak256(data))
}
func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
return w.signHash(account, accounts.TextHash(text))
}
// SignTx implements accounts.Wallet, attempting to sign the given transaction // SignTx implements accounts.Wallet, attempting to sign the given transaction
// with the given account. If the wallet does not wrap this particular account, // with the given account. If the wallet does not wrap this particular account,
// an error is returned to avoid account leakage (even though in theory we may // an error is returned to avoid account leakage (even though in theory we may
@ -104,15 +114,15 @@ func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction,
return w.keystore.SignTx(account, tx, chainID) return w.keystore.SignTx(account, tx, chainID)
} }
// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the // SignTextWithPassphrase implements accounts.Wallet, attempting to sign the
// given hash with the given account using passphrase as extra authentication. // given hash with the given account using passphrase as extra authentication.
func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { func (w *keystoreWallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if !w.Contains(account) { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
return w.keystore.SignHashWithPassphrase(account, passphrase, hash) return w.keystore.SignHashWithPassphrase(account, passphrase, accounts.TextHash(text))
} }
// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given

View file

@ -29,6 +29,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/karalabe/hid" "github.com/karalabe/hid"
) )
@ -495,12 +496,21 @@ func (w *wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainSt
w.deriveChain = chain w.deriveChain = chain
} }
// SignHash implements accounts.Wallet, however signing arbitrary data is not // signHash implements accounts.Wallet, however signing arbitrary data is not
// supported for hardware wallets, so this method will always return an error. // supported for hardware wallets, so this method will always return an error.
func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported return nil, accounts.ErrNotSupported
} }
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
return w.signHash(account, crypto.Keccak256(data))
}
func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
return w.signHash(account, accounts.TextHash(text))
}
// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger // SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
// wallet to request a confirmation from the user. It returns either the signed // wallet to request a confirmation from the user. It returns either the signed
// transaction or a failure if the user denied the transaction. // transaction or a failure if the user denied the transaction.
@ -547,11 +557,11 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
return signed, nil return signed, nil
} }
// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary // SignTextWithPassphrase implements accounts.Wallet, however signing arbitrary
// data is not supported for Ledger wallets, so this method will always return // data is not supported for Ledger wallets, so this method will always return
// an error. // an error.
func (w *wallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
return w.SignHash(account, hash) return w.SignText(account, accounts.TextHash(text))
} }
// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given

View file

@ -282,7 +282,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
return block, false, err return block, false, err
} }
header := block.Header() header := block.Header()
sighash, err := wallet.SignHash(accounts.Account{Address: eb}, c.SigHash(header).Bytes()) sighash, err := wallet.SignText(accounts.Account{Address: eb}, c.SigHash(header).Bytes())
if err != nil || sighash == nil { if err != nil || sighash == nil {
log.Error("Can't get signature hash of m2", "sighash", sighash, "err", err) log.Error("Can't get signature hash of m2", "sighash", sighash, "err", err)
return block, false, err return block, false, err
@ -503,7 +503,7 @@ func (e *Ethereum) StartStaking(local bool) error {
log.Error("Etherbase account unavailable locally", "address", eb, "err", err) log.Error("Etherbase account unavailable locally", "address", eb, "err", err)
return fmt.Errorf("signer missing: %v", err) return fmt.Errorf("signer missing: %v", err)
} }
XDPoS.Authorize(eb, wallet.SignHash) XDPoS.Authorize(eb, wallet.SignText)
} }
if local { if local {
// If local (CPU) mining is started, we can disable the transaction rejection // If local (CPU) mining is started, we can disable the transaction rejection

View file

@ -543,7 +543,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
return nil, err return nil, err
} }
// Assemble sign the data with the wallet // Assemble sign the data with the wallet
signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data)) signature, err := wallet.SignTextWithPassphrase(account, passwd, signHash(data))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -3292,7 +3292,7 @@ func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes)
return nil, err return nil, err
} }
// Sign the requested hash with the wallet // Sign the requested hash with the wallet
signature, err := wallet.SignHash(account, signHash(data)) signature, err := wallet.SignText(account, signHash(data))
if err == nil { if err == nil {
signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
} }