allow multikey unlock (#356)

* allow multikey unlock

* fix lint

* Update rpc/apis.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Daniel Choi 2020-07-06 10:03:34 -07:00 committed by GitHub
parent 5768706917
commit b76c36fcdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 28 deletions

View File

@ -3,10 +3,8 @@
package rpc package rpc
import ( import (
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -21,7 +19,7 @@ const (
) )
// GetRPCAPIs returns the list of all APIs // GetRPCAPIs returns the list of all APIs
func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []rpc.API { func GetRPCAPIs(cliCtx context.CLIContext, keys []emintcrypto.PrivKeySecp256k1) []rpc.API {
nonceLock := new(AddrLocker) nonceLock := new(AddrLocker)
backend := NewEthermintBackend(cliCtx) backend := NewEthermintBackend(cliCtx)
return []rpc.API{ return []rpc.API{
@ -34,7 +32,7 @@ func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []r
{ {
Namespace: EthNamespace, Namespace: EthNamespace,
Version: apiVersion, Version: apiVersion,
Service: NewPublicEthAPI(cliCtx, backend, nonceLock, key), Service: NewPublicEthAPI(cliCtx, backend, nonceLock, keys),
Public: true, Public: true,
}, },
{ {

View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"strings"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/flags"
@ -16,7 +17,6 @@ import (
"github.com/cosmos/ethermint/app" "github.com/cosmos/ethermint/app"
emintcrypto "github.com/cosmos/ethermint/crypto" emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -56,8 +56,9 @@ func EmintServeCmd(cdc *codec.Codec) *cobra.Command {
func registerRoutes(rs *lcd.RestServer) { func registerRoutes(rs *lcd.RestServer) {
s := rpc.NewServer() s := rpc.NewServer()
accountName := viper.GetString(flagUnlockKey) accountName := viper.GetString(flagUnlockKey)
accountNames := strings.Split(accountName, ",")
var emintKey emintcrypto.PrivKeySecp256k1 var emintKeys []emintcrypto.PrivKeySecp256k1
if len(accountName) > 0 { if len(accountName) > 0 {
var err error var err error
inBuf := bufio.NewReader(os.Stdin) inBuf := bufio.NewReader(os.Stdin)
@ -76,13 +77,13 @@ func registerRoutes(rs *lcd.RestServer) {
} }
} }
emintKey, err = unlockKeyFromNameAndPassphrase(accountName, passphrase) emintKeys, err = unlockKeyFromNameAndPassphrase(accountNames, passphrase)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
apis := GetRPCAPIs(rs.CliCtx, emintKey) apis := GetRPCAPIs(rs.CliCtx, emintKeys)
// TODO: Allow cli to configure modules https://github.com/ChainSafe/ethermint/issues/74 // TODO: Allow cli to configure modules https://github.com/ChainSafe/ethermint/issues/74
whitelist := make(map[string]bool) whitelist := make(map[string]bool)
@ -105,7 +106,7 @@ func registerRoutes(rs *lcd.RestServer) {
app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux) app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux)
} }
func unlockKeyFromNameAndPassphrase(accountName, passphrase string) (emintKey emintcrypto.PrivKeySecp256k1, err error) { func unlockKeyFromNameAndPassphrase(accountNames []string, passphrase string) (emintKeys []emintcrypto.PrivKeySecp256k1, err error) {
keybase, err := keyring.NewKeyring( keybase, err := keyring.NewKeyring(
sdk.KeyringServiceName(), sdk.KeyringServiceName(),
viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagKeyringBackend),
@ -116,17 +117,22 @@ func unlockKeyFromNameAndPassphrase(accountName, passphrase string) (emintKey em
return return
} }
// try the for loop with array []string accountNames
// run through the bottom code inside the for loop
for _, acc := range accountNames {
// With keyring keybase, password is not required as it is pulled from the OS prompt // With keyring keybase, password is not required as it is pulled from the OS prompt
privKey, err := keybase.ExportPrivateKeyObject(accountName, passphrase) privKey, err := keybase.ExportPrivateKeyObject(acc, passphrase)
if err != nil { if err != nil {
return return nil, err
} }
var ok bool var ok bool
emintKey, ok = privKey.(emintcrypto.PrivKeySecp256k1) emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1)
if !ok { if !ok {
panic(fmt.Sprintf("invalid private key type: %T", privKey)) panic(fmt.Sprintf("invalid private key type: %T", privKey))
} }
emintKeys = append(emintKeys, emintKey)
}
return return
} }

View File

@ -45,19 +45,19 @@ import (
type PublicEthAPI struct { type PublicEthAPI struct {
cliCtx context.CLIContext cliCtx context.CLIContext
backend Backend backend Backend
key emintcrypto.PrivKeySecp256k1 keys []emintcrypto.PrivKeySecp256k1
nonceLock *AddrLocker nonceLock *AddrLocker
keybaseLock sync.Mutex keybaseLock sync.Mutex
} }
// NewPublicEthAPI creates an instance of the public ETH Web3 API. // NewPublicEthAPI creates an instance of the public ETH Web3 API.
func NewPublicEthAPI(cliCtx context.CLIContext, backend Backend, nonceLock *AddrLocker, func NewPublicEthAPI(cliCtx context.CLIContext, backend Backend, nonceLock *AddrLocker,
key emintcrypto.PrivKeySecp256k1) *PublicEthAPI { key []emintcrypto.PrivKeySecp256k1) *PublicEthAPI {
return &PublicEthAPI{ return &PublicEthAPI{
cliCtx: cliCtx, cliCtx: cliCtx,
backend: backend, backend: backend,
key: key, keys: key,
nonceLock: nonceLock, nonceLock: nonceLock,
} }
} }
@ -282,15 +282,28 @@ func (e *PublicEthAPI) ExportAccount(address common.Address, blockNumber BlockNu
return string(res), nil return string(res), nil
} }
func checkKeyInKeyring(keys []emintcrypto.PrivKeySecp256k1, address common.Address) (key emintcrypto.PrivKeySecp256k1, exist bool) {
if len(keys) > 0 {
for _, key := range keys {
if bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) {
return key, true
}
}
}
return nil, false
}
// Sign signs the provided data using the private key of address via Geth's signature standard. // Sign signs the provided data using the private key of address via Geth's signature standard.
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
// TODO: Change this functionality to find an unlocked account by address // TODO: Change this functionality to find an unlocked account by address
if e.key == nil || !bytes.Equal(e.key.PubKey().Address().Bytes(), address.Bytes()) {
key, exist := checkKeyInKeyring(e.keys, address)
if !exist {
return nil, keystore.ErrLocked return nil, keystore.ErrLocked
} }
// Sign the requested hash with the wallet // Sign the requested hash with the wallet
signature, err := e.key.Sign(data) signature, err := key.Sign(data)
if err == nil { if err == nil {
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
} }
@ -301,7 +314,9 @@ func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil
// SendTransaction sends an Ethereum transaction. // SendTransaction sends an Ethereum transaction.
func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, error) { func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, error) {
// TODO: Change this functionality to find an unlocked account by address // TODO: Change this functionality to find an unlocked account by address
if e.key == nil || !bytes.Equal(e.key.PubKey().Address().Bytes(), args.From.Bytes()) {
key, exist := checkKeyInKeyring(e.keys, args.From)
if !exist {
return common.Hash{}, keystore.ErrLocked return common.Hash{}, keystore.ErrLocked
} }
@ -326,7 +341,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
} }
// Sign transaction // Sign transaction
if err := tx.Sign(intChainID, e.key.ToECDSA()); err != nil { if err := tx.Sign(intChainID, key.ToECDSA()); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -429,9 +444,11 @@ func (e *PublicEthAPI) doCall(
// Set sender address or use a default if none specified // Set sender address or use a default if none specified
var addr common.Address var addr common.Address
if args.From == nil { if args.From == nil {
if e.key != nil { key, exist := checkKeyInKeyring(e.keys, *args.From)
addr = common.BytesToAddress(e.key.PubKey().Address().Bytes()) if exist {
addr = common.BytesToAddress(key.PubKey().Address().Bytes())
} }
// No error handled here intentionally to match geth behaviour // No error handled here intentionally to match geth behaviour
} else { } else {