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
import (
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/cosmos/cosmos-sdk/client/context"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/ethereum/go-ethereum/rpc"
)
@ -21,7 +19,7 @@ const (
)
// 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)
backend := NewEthermintBackend(cliCtx)
return []rpc.API{
@ -34,7 +32,7 @@ func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []r
{
Namespace: EthNamespace,
Version: apiVersion,
Service: NewPublicEthAPI(cliCtx, backend, nonceLock, key),
Service: NewPublicEthAPI(cliCtx, backend, nonceLock, keys),
Public: true,
},
{

View File

@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"strings"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
@ -16,7 +17,6 @@ import (
"github.com/cosmos/ethermint/app"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/ethereum/go-ethereum/rpc"
"github.com/spf13/cobra"
@ -56,8 +56,9 @@ func EmintServeCmd(cdc *codec.Codec) *cobra.Command {
func registerRoutes(rs *lcd.RestServer) {
s := rpc.NewServer()
accountName := viper.GetString(flagUnlockKey)
accountNames := strings.Split(accountName, ",")
var emintKey emintcrypto.PrivKeySecp256k1
var emintKeys []emintcrypto.PrivKeySecp256k1
if len(accountName) > 0 {
var err error
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 {
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
whitelist := make(map[string]bool)
@ -105,7 +106,7 @@ func registerRoutes(rs *lcd.RestServer) {
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(
sdk.KeyringServiceName(),
viper.GetString(flags.FlagKeyringBackend),
@ -116,16 +117,21 @@ func unlockKeyFromNameAndPassphrase(accountName, passphrase string) (emintKey em
return
}
// With keyring keybase, password is not required as it is pulled from the OS prompt
privKey, err := keybase.ExportPrivateKeyObject(accountName, passphrase)
if err != nil {
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
privKey, err := keybase.ExportPrivateKeyObject(acc, passphrase)
if err != nil {
return nil, err
}
var ok bool
emintKey, ok = privKey.(emintcrypto.PrivKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid private key type: %T", privKey))
var ok bool
emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid private key type: %T", privKey))
}
emintKeys = append(emintKeys, emintKey)
}
return

View File

@ -45,19 +45,19 @@ import (
type PublicEthAPI struct {
cliCtx context.CLIContext
backend Backend
key emintcrypto.PrivKeySecp256k1
keys []emintcrypto.PrivKeySecp256k1
nonceLock *AddrLocker
keybaseLock sync.Mutex
}
// NewPublicEthAPI creates an instance of the public ETH Web3 API.
func NewPublicEthAPI(cliCtx context.CLIContext, backend Backend, nonceLock *AddrLocker,
key emintcrypto.PrivKeySecp256k1) *PublicEthAPI {
key []emintcrypto.PrivKeySecp256k1) *PublicEthAPI {
return &PublicEthAPI{
cliCtx: cliCtx,
backend: backend,
key: key,
keys: key,
nonceLock: nonceLock,
}
}
@ -282,15 +282,28 @@ func (e *PublicEthAPI) ExportAccount(address common.Address, blockNumber BlockNu
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.
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
// 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
}
// Sign the requested hash with the wallet
signature, err := e.key.Sign(data)
signature, err := key.Sign(data)
if err == nil {
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.
func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, error) {
// 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
}
@ -326,7 +341,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
}
// 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
}
@ -429,9 +444,11 @@ func (e *PublicEthAPI) doCall(
// Set sender address or use a default if none specified
var addr common.Address
if args.From == nil {
if e.key != nil {
addr = common.BytesToAddress(e.key.PubKey().Address().Bytes())
key, exist := checkKeyInKeyring(e.keys, *args.From)
if exist {
addr = common.BytesToAddress(key.PubKey().Address().Bytes())
}
// No error handled here intentionally to match geth behaviour
} else {