Skip to content
This repository was archived by the owner on Nov 30, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
60 changes: 45 additions & 15 deletions rpc/personal_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,26 @@ 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.
// PersonalEthAPI is the personal_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PersonalEthAPI struct {
logger log.Logger
cliCtx sdkcontext.CLIContext
Expand All @@ -34,7 +38,7 @@ type PersonalEthAPI struct {
keybaseLock sync.Mutex
}

// NewPersonalEthAPI creates an instance of the public ETH Web3 API.
// NewPersonalEthAPI creates an instance of the public Personal Eth API.
func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext, ethAPI *PublicEthAPI, nonceLock *AddrLocker, keys []emintcrypto.PrivKeySecp256k1) *PersonalEthAPI {
api := &PersonalEthAPI{
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "json-rpc"),
Expand Down Expand Up @@ -75,17 +79,43 @@ 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
}

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())

info, err := e.cliCtx.Keybase.Get(privKeyName)
if err != nil {
return common.Address{}, err
}

return common.Address{}, nil
// append key and info to be able to lock and list the account
e.keys = append(e.keys, privKey)
e.keyInfos = append(e.keyInfos, info)

e.logger.Info("key successfully imported", "name", privKeyName, "address", addr.String())

return addr, nil
}

// ListAccounts will return a list of addresses for accounts this node manages.
Expand Down Expand Up @@ -128,6 +158,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 +190,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 +231,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