Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

rpc: implement personal_importRawKey #552

Merged
merged 17 commits into from
Sep 30, 2020
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

## Unreleased

### Features

* (rpc) [\#552](https://github.com/ChainSafe/ethermint/pull/552) Implement Eth Personal namespace `personal_importRawKey`.

## [v0.2.0] - 2020-09-24

### State Machine Breaking
Expand Down
4 changes: 3 additions & 1 deletion crypto/algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import (
)

const (
// EthSecp256k1Type string constant for the EthSecp256k1 algorithm
EthSecp256k1Type = "eth_secp256k1"
// EthSecp256k1 defines the ECDSA secp256k1 used on Ethereum
EthSecp256k1 = keys.SigningAlgo("eth_secp256k1")
EthSecp256k1 = keys.SigningAlgo(EthSecp256k1Type)
)

// SupportedAlgorithms defines the list of signing algorithms used on Ethermint:
Expand Down
46 changes: 33 additions & 13 deletions rpc/personal_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ import (
"sync"
"time"

"github.com/spf13/viper"

"github.com/tendermint/tendermint/libs/log"

sdkcontext "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
sdk "github.com/cosmos/cosmos-sdk/types"
emintcrypto "github.com/cosmos/ethermint/crypto"
params "github.com/cosmos/ethermint/rpc/args"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/log"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"

emintcrypto "github.com/cosmos/ethermint/crypto"
params "github.com/cosmos/ethermint/rpc/args"
)

// PersonalEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
Expand Down Expand Up @@ -75,17 +79,33 @@ func (e *PersonalEthAPI) getKeybaseInfo() ([]keys.Info, error) {
return e.cliCtx.Keybase.List()
}

// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
// encrypting it with the passphrase.
// Currently, this is not implemented since the feature is not supported by the keys.
// ImportRawKey armors and encrypts a given raw hex encoded ECDSA key and stores it into the key directory.
// The name of the key will have the format "personal_<length-keys>", where <length-keys> is the total number of
// keys stored on the keyring.
// NOTE: The key will be both armored and encrypted using the same passphrase.
func (e *PersonalEthAPI) ImportRawKey(privkey, password string) (common.Address, error) {
e.logger.Debug("personal_importRawKey", "error", "not implemented")
_, err := crypto.HexToECDSA(privkey)
e.logger.Debug("personal_importRawKey")
priv, err := crypto.HexToECDSA(privkey)
if err != nil {
return common.Address{}, err
}

return common.Address{}, nil
privKey := emintcrypto.PrivKeySecp256k1(crypto.FromECDSA(priv))

armor := mintkey.EncryptArmorPrivKey(privKey, password, emintcrypto.EthSecp256k1Type)

// ignore error as we only care about the length of the list
list, _ := e.cliCtx.Keybase.List()
privKeyName := fmt.Sprintf("personal_%d", len(list))

if err := e.cliCtx.Keybase.ImportPrivKey(privKeyName, armor, password); err != nil {
return common.Address{}, err
}

addr := common.BytesToAddress(privKey.PubKey().Address().Bytes())
e.logger.Info("key successfully imported", "name", privKeyName, "address", addr.String())

fedekunze marked this conversation as resolved.
Show resolved Hide resolved
return addr, nil
}

// ListAccounts will return a list of addresses for accounts this node manages.
Expand Down Expand Up @@ -128,6 +148,8 @@ func (e *PersonalEthAPI) LockAccount(address common.Address) bool {
return true
}

e.logger.Debug("account unlocked", "address", address)

return false
}

Expand Down Expand Up @@ -158,7 +180,6 @@ func (e *PersonalEthAPI) NewAccount(password string) (common.Address, error) {
return common.Address{}, fmt.Errorf("invalid private key type: %T", privKey)
}
e.ethAPI.keys = append(e.ethAPI.keys, emintKey)
e.logger.Debug("personal_newAccount", "address", fmt.Sprintf("0x%x", emintKey.PubKey().Address().Bytes()))

addr := common.BytesToAddress(info.GetPubKey().Address().Bytes())
e.logger.Info("Your new key was generated", "address", addr)
Expand Down Expand Up @@ -200,8 +221,7 @@ func (e *PersonalEthAPI) UnlockAccount(ctx context.Context, addr common.Address,

e.keys = append(e.keys, emintKey)
e.ethAPI.keys = append(e.ethAPI.keys, emintKey)
e.logger.Debug("personal_unlockAccount", "address", fmt.Sprintf("0x%x", emintKey.PubKey().Address().Bytes()))

e.logger.Debug("account unlocked", "address", addr)
return true, nil
}

Expand Down
19 changes: 19 additions & 0 deletions tests/personal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethcrypto "github.com/ethereum/go-ethereum/crypto"

"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -42,6 +43,24 @@ func TestPersonal_Sign(t *testing.T) {
// TODO: check that signature is same as with geth, requires importing a key
}

func TestPersonal_ImportRawKey(t *testing.T) {
privkey, err := ethcrypto.GenerateKey()
require.NoError(t, err)

// parse priv key to hex
hexPriv := common.Bytes2Hex(ethcrypto.FromECDSA(privkey))
rpcRes := call(t, "personal_importRawKey", []string{hexPriv, "password"})

var res hexutil.Bytes
err = json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)

addr := ethcrypto.PubkeyToAddress(privkey.PublicKey)
resAddr := common.BytesToAddress(res)

require.Equal(t, addr.String(), resAddr.String())
}

func TestPersonal_EcRecover(t *testing.T) {
data := hexutil.Bytes{0x88}
rpcRes := call(t, "personal_sign", []interface{}{data, hexutil.Bytes(from), ""})
Expand Down