mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
parent
69e9f41b4c
commit
b413f71763
8 changed files with 9 additions and 580 deletions
|
|
@ -587,11 +587,6 @@ var (
|
|||
Usage: "Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
EnablePersonal = &cli.BoolFlag{
|
||||
Name: "rpc.enabledeprecatedpersonal",
|
||||
Usage: "Enables the (deprecated) personal namespace",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
BatchRequestLimit = &cli.IntFlag{
|
||||
Name: "rpc-batch-request-limit",
|
||||
Usage: "Maximum number of requests in a batch",
|
||||
|
|
@ -1284,7 +1279,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
|||
}
|
||||
|
||||
if ctx.IsSet(EnablePersonal.Name) {
|
||||
cfg.EnablePersonal = true
|
||||
log.Warn(fmt.Sprintf("Option --%s is deprecated. The 'personal' RPC namespace has been removed.", EnablePersonal.Name))
|
||||
}
|
||||
|
||||
switch {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ var (
|
|||
Value: ethconfig.Defaults.LightPeers,
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
// Deprecated Oct 2024
|
||||
EnablePersonal = &cli.BoolFlag{
|
||||
Name: "rpc.enabledeprecatedpersonal",
|
||||
Usage: "This used to enable the 'personal' namespace.",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
)
|
||||
|
||||
// showDeprecated displays deprecated flags that will be soon removed from the codebase.
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ package console
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/usbwallet"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/internal/jsre"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
|
|
@ -49,212 +47,6 @@ func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *br
|
|||
}
|
||||
}
|
||||
|
||||
func getJeth(vm *goja.Runtime) *goja.Object {
|
||||
jeth := vm.Get("jeth")
|
||||
if jeth == nil {
|
||||
panic(vm.ToValue("jeth object does not exist"))
|
||||
}
|
||||
return jeth.ToObject(vm)
|
||||
}
|
||||
|
||||
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
|
||||
// non-echoing password prompt to acquire the passphrase and executes the original
|
||||
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
|
||||
func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) {
|
||||
var (
|
||||
password string
|
||||
confirm string
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
// No password was specified, prompt the user for it
|
||||
case len(call.Arguments) == 0:
|
||||
if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if password != confirm {
|
||||
return nil, errors.New("passwords don't match")
|
||||
}
|
||||
// A single string password was specified, use that
|
||||
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
|
||||
password = call.Argument(0).ToString().String()
|
||||
default:
|
||||
return nil, errors.New("expected 0 or 1 string argument")
|
||||
}
|
||||
// Password acquired, execute the call and return
|
||||
newAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("newAccount"))
|
||||
if !callable {
|
||||
return nil, errors.New("jeth.newAccount is not callable")
|
||||
}
|
||||
ret, err := newAccount(goja.Null(), call.VM.ToValue(password))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// OpenWallet is a wrapper around personal.openWallet which can interpret and
|
||||
// react to certain error messages, such as the Trezor PIN matrix request.
|
||||
func (b *bridge) OpenWallet(call jsre.Call) (goja.Value, error) {
|
||||
// Make sure we have a wallet specified to open
|
||||
if call.Argument(0).ToObject(call.VM).ClassName() != "String" {
|
||||
return nil, errors.New("first argument must be the wallet URL to open")
|
||||
}
|
||||
wallet := call.Argument(0)
|
||||
|
||||
var passwd goja.Value
|
||||
if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
|
||||
passwd = call.VM.ToValue("")
|
||||
} else {
|
||||
passwd = call.Argument(1)
|
||||
}
|
||||
// Open the wallet and return if successful in itself
|
||||
openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
|
||||
if !callable {
|
||||
return nil, errors.New("jeth.openWallet is not callable")
|
||||
}
|
||||
val, err := openWallet(goja.Null(), wallet, passwd)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Wallet open failed, report error unless it's a PIN or PUK entry
|
||||
switch {
|
||||
case strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()):
|
||||
val, err = b.readPinAndReopenWallet(call)
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
val, err = b.readPassphraseAndReopenWallet(call)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
// Unknown error occurred, drop to the user
|
||||
return nil, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (b *bridge) readPassphraseAndReopenWallet(call jsre.Call) (goja.Value, error) {
|
||||
wallet := call.Argument(0)
|
||||
input, err := b.prompter.PromptPassword("Please enter your passphrase: ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
|
||||
if !callable {
|
||||
return nil, errors.New("jeth.openWallet is not callable")
|
||||
}
|
||||
return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
|
||||
}
|
||||
|
||||
func (b *bridge) readPinAndReopenWallet(call jsre.Call) (goja.Value, error) {
|
||||
wallet := call.Argument(0)
|
||||
// Trezor PIN matrix input requested, display the matrix to the user and fetch the data
|
||||
fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
|
||||
fmt.Fprintf(b.printer, "7 | 8 | 9\n")
|
||||
fmt.Fprintf(b.printer, "--+---+--\n")
|
||||
fmt.Fprintf(b.printer, "4 | 5 | 6\n")
|
||||
fmt.Fprintf(b.printer, "--+---+--\n")
|
||||
fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
|
||||
|
||||
input, err := b.prompter.PromptPassword("Please enter current PIN: ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
|
||||
if !callable {
|
||||
return nil, errors.New("jeth.openWallet is not callable")
|
||||
}
|
||||
return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
|
||||
}
|
||||
|
||||
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
|
||||
// uses a non-echoing password prompt to acquire the passphrase and executes the
|
||||
// original RPC method (saved in jeth.unlockAccount) with it to actually execute
|
||||
// the RPC call.
|
||||
func (b *bridge) UnlockAccount(call jsre.Call) (goja.Value, error) {
|
||||
// Make sure we have an account specified to unlock.
|
||||
if call.Argument(0).ExportType().Kind() != reflect.String {
|
||||
return nil, errors.New("first argument must be the account to unlock")
|
||||
}
|
||||
account := call.Argument(0)
|
||||
|
||||
// If password is not given or is the null value, prompt the user for it.
|
||||
var passwd goja.Value
|
||||
if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
|
||||
fmt.Fprintf(b.printer, "Unlock account %s\n", account)
|
||||
input, err := b.prompter.PromptPassword("Passphrase: ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
passwd = call.VM.ToValue(input)
|
||||
} else {
|
||||
if call.Argument(1).ExportType().Kind() != reflect.String {
|
||||
return nil, errors.New("password must be a string")
|
||||
}
|
||||
passwd = call.Argument(1)
|
||||
}
|
||||
|
||||
// Third argument is the duration how long the account should be unlocked.
|
||||
duration := goja.Null()
|
||||
if !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) {
|
||||
if !isNumber(call.Argument(2)) {
|
||||
return nil, errors.New("unlock duration must be a number")
|
||||
}
|
||||
duration = call.Argument(2)
|
||||
}
|
||||
|
||||
// Send the request to the backend and return.
|
||||
unlockAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
|
||||
if !callable {
|
||||
return nil, errors.New("jeth.unlockAccount is not callable")
|
||||
}
|
||||
return unlockAccount(goja.Null(), account, passwd, duration)
|
||||
}
|
||||
|
||||
// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password
|
||||
// prompt to acquire the passphrase and executes the original RPC method (saved in
|
||||
// jeth.sign) with it to actually execute the RPC call.
|
||||
func (b *bridge) Sign(call jsre.Call) (goja.Value, error) {
|
||||
var (
|
||||
message = call.Argument(0)
|
||||
account = call.Argument(1)
|
||||
passwd = call.Argument(2)
|
||||
)
|
||||
|
||||
if message.ExportType().Kind() != reflect.String {
|
||||
return nil, errors.New("first argument must be the message to sign")
|
||||
}
|
||||
if account.ExportType().Kind() != reflect.String {
|
||||
return nil, errors.New("second argument must be the account to sign with")
|
||||
}
|
||||
|
||||
// if the password is not given or null ask the user and ensure password is a string
|
||||
if goja.IsUndefined(passwd) || goja.IsNull(passwd) {
|
||||
fmt.Fprintf(b.printer, "Give password for account %s\n", account)
|
||||
input, err := b.prompter.PromptPassword("Password: ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
passwd = call.VM.ToValue(input)
|
||||
} else if passwd.ExportType().Kind() != reflect.String {
|
||||
return nil, errors.New("third argument must be the password to unlock the account")
|
||||
}
|
||||
|
||||
// Send the request to the backend and return
|
||||
sign, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
|
||||
if !callable {
|
||||
return nil, errors.New("jeth.unlockAccount is not callable")
|
||||
}
|
||||
return sign(goja.Null(), message, account, passwd)
|
||||
}
|
||||
|
||||
// Sleep will block the console for the specified number of seconds.
|
||||
func (b *bridge) Sleep(call jsre.Call) (goja.Value, error) {
|
||||
if !isNumber(call.Argument(0)) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/internal/jsre"
|
||||
"github.com/XinFinOrg/XDPoSChain/internal/jsre/deps"
|
||||
"github.com/XinFinOrg/XDPoSChain/internal/web3ext"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
"github.com/dop251/goja"
|
||||
"github.com/mattn/go-colorable"
|
||||
|
|
@ -123,7 +122,6 @@ func (c *Console) init(preload []string) error {
|
|||
// Add bridge overrides for web3.js functionality.
|
||||
c.jsre.Do(func(vm *goja.Runtime) {
|
||||
c.initAdmin(vm, bridge)
|
||||
c.initPersonal(vm, bridge)
|
||||
})
|
||||
|
||||
// Preload JavaScript files.
|
||||
|
|
@ -221,30 +219,6 @@ func (c *Console) initAdmin(vm *goja.Runtime, bridge *bridge) {
|
|||
}
|
||||
}
|
||||
|
||||
// initPersonal redirects account-related API methods through the bridge.
|
||||
//
|
||||
// If the console is in interactive mode and the 'personal' API is available, override
|
||||
// the openWallet, unlockAccount, newAccount and sign methods since these require user
|
||||
// interaction. The original web3 callbacks are stored in 'jeth'. These will be called
|
||||
// by the bridge after the prompt and send the original web3 request to the backend.
|
||||
func (c *Console) initPersonal(vm *goja.Runtime, bridge *bridge) {
|
||||
personal := getObject(vm, "personal")
|
||||
if personal == nil || c.prompter == nil {
|
||||
return
|
||||
}
|
||||
log.Warn("Enabling deprecated personal namespace")
|
||||
jeth := vm.NewObject()
|
||||
vm.Set("jeth", jeth)
|
||||
jeth.Set("openWallet", personal.Get("openWallet"))
|
||||
jeth.Set("unlockAccount", personal.Get("unlockAccount"))
|
||||
jeth.Set("newAccount", personal.Get("newAccount"))
|
||||
jeth.Set("sign", personal.Get("sign"))
|
||||
personal.Set("openWallet", jsre.MakeCallback(vm, bridge.OpenWallet))
|
||||
personal.Set("unlockAccount", jsre.MakeCallback(vm, bridge.UnlockAccount))
|
||||
personal.Set("newAccount", jsre.MakeCallback(vm, bridge.NewAccount))
|
||||
personal.Set("sign", jsre.MakeCallback(vm, bridge.Sign))
|
||||
}
|
||||
|
||||
func (c *Console) clearHistory() {
|
||||
c.history = nil
|
||||
c.prompter.ClearHistory()
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
math "github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
|
|
@ -293,281 +292,6 @@ func (s *EthereumAccountAPI) Accounts() []common.Address {
|
|||
return s.am.Accounts()
|
||||
}
|
||||
|
||||
// PersonalAccountAPI provides an API to access accounts managed by this node.
|
||||
// It offers methods to create, (un)lock en list accounts. Some methods accept
|
||||
// passwords and are therefore considered private by default.
|
||||
type PersonalAccountAPI struct {
|
||||
am *accounts.Manager
|
||||
nonceLock *AddrLocker
|
||||
b Backend
|
||||
}
|
||||
|
||||
// NewPersonalAccountAPI create a new PersonalAccountAPI.
|
||||
func NewPersonalAccountAPI(b Backend, nonceLock *AddrLocker) *PersonalAccountAPI {
|
||||
return &PersonalAccountAPI{
|
||||
am: b.AccountManager(),
|
||||
nonceLock: nonceLock,
|
||||
b: b,
|
||||
}
|
||||
}
|
||||
|
||||
// ListAccounts will return a list of addresses for accounts this node manages.
|
||||
func (s *PersonalAccountAPI) ListAccounts() []common.Address {
|
||||
return s.am.Accounts()
|
||||
}
|
||||
|
||||
// rawWallet is a JSON representation of an accounts.Wallet interface, with its
|
||||
// data contents extracted into plain fields.
|
||||
type rawWallet struct {
|
||||
URL string `json:"url"`
|
||||
Status string `json:"status"`
|
||||
Failure string `json:"failure,omitempty"`
|
||||
Accounts []accounts.Account `json:"accounts,omitempty"`
|
||||
}
|
||||
|
||||
// ListWallets will return a list of wallets this node manages.
|
||||
func (s *PersonalAccountAPI) ListWallets() []rawWallet {
|
||||
wallets := make([]rawWallet, 0) // return [] instead of nil if empty
|
||||
for _, wallet := range s.am.Wallets() {
|
||||
status, failure := wallet.Status()
|
||||
|
||||
raw := rawWallet{
|
||||
URL: wallet.URL().String(),
|
||||
Status: status,
|
||||
Accounts: wallet.Accounts(),
|
||||
}
|
||||
if failure != nil {
|
||||
raw.Failure = failure.Error()
|
||||
}
|
||||
wallets = append(wallets, raw)
|
||||
}
|
||||
return wallets
|
||||
}
|
||||
|
||||
// OpenWallet initiates a hardware wallet opening procedure, establishing a USB
|
||||
// connection and attempting to authenticate via the provided passphrase. Note,
|
||||
// the method may return an extra challenge requiring a second open (e.g. the
|
||||
// Trezor PIN matrix challenge).
|
||||
func (s *PersonalAccountAPI) OpenWallet(url string, passphrase *string) error {
|
||||
wallet, err := s.am.Wallet(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pass := ""
|
||||
if passphrase != nil {
|
||||
pass = *passphrase
|
||||
}
|
||||
return wallet.Open(pass)
|
||||
}
|
||||
|
||||
// DeriveAccount requests a HD wallet to derive a new account, optionally pinning
|
||||
// it for later reuse.
|
||||
func (s *PersonalAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) {
|
||||
wallet, err := s.am.Wallet(url)
|
||||
if err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
derivPath, err := accounts.ParseDerivationPath(path)
|
||||
if err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
if pin == nil {
|
||||
pin = new(bool)
|
||||
}
|
||||
return wallet.Derive(derivPath, *pin)
|
||||
}
|
||||
|
||||
// NewAccount will create a new account and returns the address for the new account.
|
||||
func (s *PersonalAccountAPI) NewAccount(password string) (common.AddressEIP55, error) {
|
||||
acc, err := fetchKeystore(s.am).NewAccount(password)
|
||||
if err == nil {
|
||||
addrEIP55 := common.AddressEIP55(acc.Address)
|
||||
log.Info("Your new key was generated", "address", addrEIP55.String())
|
||||
log.Warn("Please backup your key file!", "path", acc.URL.Path)
|
||||
log.Warn("Please remember your password!")
|
||||
return addrEIP55, nil
|
||||
}
|
||||
return common.AddressEIP55{}, err
|
||||
}
|
||||
|
||||
// fetchKeystore retrives the encrypted keystore from the account manager.
|
||||
func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
|
||||
ks := am.Backends(keystore.KeyStoreType)
|
||||
if len(ks) == 0 {
|
||||
return nil
|
||||
}
|
||||
return ks[0].(*keystore.KeyStore)
|
||||
}
|
||||
|
||||
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
|
||||
// encrypting it with the passphrase.
|
||||
func (s *PersonalAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
|
||||
key, err := crypto.HexToECDSA(privkey)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
acc, err := fetchKeystore(s.am).ImportECDSA(key, password)
|
||||
return acc.Address, err
|
||||
}
|
||||
|
||||
// UnlockAccount will unlock the account associated with the given address with
|
||||
// the given password for duration seconds. If duration is nil it will use a
|
||||
// default of 300 seconds. It returns an indication if the account was unlocked.
|
||||
func (s *PersonalAccountAPI) UnlockAccount(addr common.Address, password string, duration *uint64) (bool, error) {
|
||||
const max = uint64(time.Duration(gomath.MaxInt64) / time.Second)
|
||||
var d time.Duration
|
||||
if duration == nil {
|
||||
d = 300 * time.Second
|
||||
} else if *duration > max {
|
||||
return false, errors.New("unlock duration too large")
|
||||
} else {
|
||||
d = time.Duration(*duration) * time.Second
|
||||
}
|
||||
err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// LockAccount will lock the account associated with the given address when it's unlocked.
|
||||
func (s *PersonalAccountAPI) LockAccount(addr common.Address) bool {
|
||||
return fetchKeystore(s.am).Lock(addr) == nil
|
||||
}
|
||||
|
||||
// signTransactions sets defaults and signs the given transaction
|
||||
// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
|
||||
// and release it after the transaction has been submitted to the tx pool
|
||||
func (s *PersonalAccountAPI) signTransaction(ctx context.Context, args *TransactionArgs, passwd string) (*types.Transaction, error) {
|
||||
// Look up the wallet containing the requested signer
|
||||
account := accounts.Account{Address: args.from()}
|
||||
wallet, err := s.am.Find(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
tx := args.toTransaction()
|
||||
|
||||
var chainID *big.Int
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
||||
chainID = config.ChainId
|
||||
}
|
||||
return wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
|
||||
}
|
||||
|
||||
// SendTransaction will create a transaction from the given arguments and
|
||||
// tries to sign it with the key associated with args.From. If the given
|
||||
// passwd isn't able to decrypt the key it fails.
|
||||
func (s *PersonalAccountAPI) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) {
|
||||
if args.Nonce == nil {
|
||||
// Hold the addresse's mutex around signing to prevent concurrent assignment of
|
||||
// the same nonce to multiple accounts.
|
||||
s.nonceLock.LockAddr(args.from())
|
||||
defer s.nonceLock.UnlockAddr(args.from())
|
||||
}
|
||||
signed, err := s.signTransaction(ctx, &args, passwd)
|
||||
if err != nil {
|
||||
log.Warn("Failed transaction send attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err)
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return SubmitTransaction(ctx, s.b, signed)
|
||||
}
|
||||
|
||||
// SignTransaction will create a transaction from the given arguments and
|
||||
// tries to sign it with the key associated with args.To. If the given passwd isn't
|
||||
// able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast
|
||||
// to other nodes
|
||||
func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args TransactionArgs, passwd string) (*SignTransactionResult, error) {
|
||||
// No need to obtain the noncelock mutex, since we won't be sending this
|
||||
// tx into the transaction pool, but right back to the user
|
||||
if args.From == nil {
|
||||
return nil, errors.New("sender not specified")
|
||||
}
|
||||
if args.Gas == nil {
|
||||
return nil, errors.New("gas not specified")
|
||||
}
|
||||
if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) {
|
||||
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
||||
}
|
||||
if args.Nonce == nil {
|
||||
return nil, errors.New("nonce not specified")
|
||||
}
|
||||
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
||||
tx := args.toTransaction()
|
||||
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signed, err := s.signTransaction(ctx, &args, passwd)
|
||||
if err != nil {
|
||||
log.Warn("Failed transaction sign attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err)
|
||||
return nil, err
|
||||
}
|
||||
data, err := signed.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SignTransactionResult{data, signed}, nil
|
||||
}
|
||||
|
||||
// Sign calculates an Ethereum ECDSA signature for:
|
||||
// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message))
|
||||
//
|
||||
// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
|
||||
// where the V value will be 27 or 28 for legacy reasons.
|
||||
//
|
||||
// The key used to calculate the signature is decrypted with the given password.
|
||||
//
|
||||
// https://github.com/XinFinOrg/XDPoSChain/wiki/Management-APIs#personal_sign
|
||||
func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
|
||||
// Look up the wallet containing the requested signer
|
||||
account := accounts.Account{Address: addr}
|
||||
|
||||
wallet, err := s.b.AccountManager().Find(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Assemble sign the data with the wallet
|
||||
signature, err := wallet.SignTextWithPassphrase(account, passwd, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
// EcRecover returns the address for the account that was used to create the signature.
|
||||
// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
|
||||
// the address of:
|
||||
// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message})
|
||||
// addr = ecrecover(hash, signature)
|
||||
//
|
||||
// Note, the signature must conform to the secp256k1 curve R, S and V values, where
|
||||
// the V value must be be 27 or 28 for legacy reasons.
|
||||
//
|
||||
// https://github.com/XinFinOrg/XDPoSChain/wiki/Management-APIs#personal_ecRecover
|
||||
func (s *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
|
||||
if len(sig) != crypto.SignatureLength {
|
||||
return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength)
|
||||
}
|
||||
if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 {
|
||||
return common.Address{}, errors.New("invalid Ethereum signature (V is not 27 or 28)")
|
||||
}
|
||||
sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
|
||||
|
||||
rpk, err := crypto.SigToPub(accounts.TextHash(data), sig)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
return crypto.PubkeyToAddress(*rpk), nil
|
||||
}
|
||||
|
||||
// SignAndSendTransaction was renamed to SendTransaction. This method is deprecated
|
||||
// and will be removed in the future. It primary goal is to give clients time to update.
|
||||
func (s *PersonalAccountAPI) SignAndSendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) {
|
||||
return s.SendTransaction(ctx, args, passwd)
|
||||
}
|
||||
|
||||
// BlockChainAPI provides an API to access Ethereum blockchain data.
|
||||
type BlockChainAPI struct {
|
||||
b Backend
|
||||
|
|
|
|||
|
|
@ -145,9 +145,6 @@ func GetAPIs(apiBackend Backend, chainReader consensus.ChainReader) []rpc.API {
|
|||
}, {
|
||||
Namespace: "eth",
|
||||
Service: NewEthereumAccountAPI(apiBackend.AccountManager()),
|
||||
}, {
|
||||
Namespace: "personal",
|
||||
Service: NewPersonalAccountAPI(apiBackend, nonceLock),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ var Modules = map[string]string{
|
|||
"eth": Eth_JS,
|
||||
"miner": Miner_JS,
|
||||
"net": Net_JS,
|
||||
"personal": Personal_JS,
|
||||
"rpc": RPC_JS,
|
||||
"shh": Shh_JS,
|
||||
"XDCx": XDCX_JS,
|
||||
|
|
@ -648,52 +647,6 @@ web3._extend({
|
|||
});
|
||||
`
|
||||
|
||||
const Personal_JS = `
|
||||
web3._extend({
|
||||
property: 'personal',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'importRawKey',
|
||||
call: 'personal_importRawKey',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sign',
|
||||
call: 'personal_sign',
|
||||
params: 3,
|
||||
inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'ecRecover',
|
||||
call: 'personal_ecRecover',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'openWallet',
|
||||
call: 'personal_openWallet',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'deriveAccount',
|
||||
call: 'personal_deriveAccount',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'personal_signTransaction',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null]
|
||||
}),
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'listWallets',
|
||||
getter: 'personal_listWallets'
|
||||
}),
|
||||
]
|
||||
})
|
||||
`
|
||||
|
||||
const RPC_JS = `
|
||||
web3._extend({
|
||||
property: 'rpc',
|
||||
|
|
|
|||
16
node/node.go
16
node/node.go
|
|
@ -378,25 +378,13 @@ func (n *Node) obtainJWTSecret(cliParam string) ([]byte, error) {
|
|||
// startup. It's not meant to be called at any time afterwards as it makes certain
|
||||
// assumptions about the state of the node.
|
||||
func (n *Node) startRPC() error {
|
||||
// Filter out personal api
|
||||
var apis []rpc.API
|
||||
for _, api := range n.rpcAPIs {
|
||||
if api.Namespace == "personal" {
|
||||
if n.config.EnablePersonal {
|
||||
log.Warn("Deprecated personal namespace activated")
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
apis = append(apis, api)
|
||||
}
|
||||
if err := n.startInProc(apis); err != nil {
|
||||
if err := n.startInProc(n.rpcAPIs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Configure IPC.
|
||||
if n.ipc.endpoint != "" {
|
||||
if err := n.ipc.start(apis); err != nil {
|
||||
if err := n.ipc.start(n.rpcAPIs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue