From d3529dd959693443b5564f9b0d473ffd23c35693 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Mon, 7 Sep 2020 15:04:50 +0200 Subject: [PATCH] types: update account pubkey JSON to string (#494) * types: update account pubkey JSON to string * changelog * Update app/ethermint.go * tests * update * fix secp256k1 public key formatting (#501) * use Compress and Decompress pubkey for secp256k1 keys * cleanup * update estimate gas test * comments Co-authored-by: noot <36753753+noot@users.noreply.github.com> --- CHANGELOG.md | 1 + crypto/secp256k1.go | 20 +++++++++++---- docs/basics/accounts.md | 2 +- tests/rpc_test.go | 2 +- types/account.go | 52 ++++++++++++++++++++++++-------------- types/account_test.go | 55 ++++++++++++++++++++++++++++++++++++++++- types/config_test.go | 4 +++ 7 files changed, 110 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f948790..25b5dba8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (types) [\#494](https://github.com/ChainSafe/ethermint/pull/494) Update `EthAccount` public key JSON type to `string`. * (app) [\#471](https://github.com/ChainSafe/ethermint/pull/471) Add `x/upgrade` module for managing software updates. * (`x/evm`) [\#458](https://github.com/ChainSafe/ethermint/pull/458) Define parameter for token denomination used for the EVM module. * (`x/evm`) [\#443](https://github.com/ChainSafe/ethermint/issues/443) Support custom Ethereum `ChainConfig` params. diff --git a/crypto/secp256k1.go b/crypto/secp256k1.go index c8ae6ad2..099e9197 100644 --- a/crypto/secp256k1.go +++ b/crypto/secp256k1.go @@ -33,7 +33,7 @@ func GenerateKey() (PrivKeySecp256k1, error) { // PubKey returns the ECDSA private key's public key. func (privkey PrivKeySecp256k1) PubKey() tmcrypto.PubKey { ecdsaPKey := privkey.ToECDSA() - return PubKeySecp256k1(ethcrypto.FromECDSAPub(&ecdsaPKey.PublicKey)) + return PubKeySecp256k1(ethcrypto.CompressPubkey(&ecdsaPKey.PublicKey)) } // Bytes returns the raw ECDSA private key bytes. @@ -58,8 +58,12 @@ func (privkey PrivKeySecp256k1) Equals(other tmcrypto.PrivKey) bool { } // ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type. +// The function will panic if the private key is invalid. func (privkey PrivKeySecp256k1) ToECDSA() *ecdsa.PrivateKey { - key, _ := ethcrypto.ToECDSA(privkey) + key, err := ethcrypto.ToECDSA(privkey) + if err != nil { + panic(err) + } return key } @@ -68,17 +72,23 @@ func (privkey PrivKeySecp256k1) ToECDSA() *ecdsa.PrivateKey { var _ tmcrypto.PubKey = (*PubKeySecp256k1)(nil) -// PubKeySecp256k1 defines a type alias for an ecdsa.PublicKey that implements -// Tendermint's PubKey interface. +// PubKeySecp256k1 defines a type alias for an ecdsa.PublicKey that implements Tendermint's PubKey +// interface. It represents the 33-byte compressed public key format. type PubKeySecp256k1 []byte // Address returns the address of the ECDSA public key. +// The function will panic if the public key is invalid. func (key PubKeySecp256k1) Address() tmcrypto.Address { - pubk, _ := ethcrypto.UnmarshalPubkey(key) + pubk, err := ethcrypto.DecompressPubkey(key) + if err != nil { + panic(err) + } + return tmcrypto.Address(ethcrypto.PubkeyToAddress(*pubk).Bytes()) } // Bytes returns the raw bytes of the ECDSA public key. +// The function panics if the key cannot be marshaled to bytes. func (key PubKeySecp256k1) Bytes() []byte { bz, err := CryptoCodec.MarshalBinaryBare(key) if err != nil { diff --git a/docs/basics/accounts.md b/docs/basics/accounts.md index ed553ea2..ef21a762 100644 --- a/docs/basics/accounts.md +++ b/docs/basics/accounts.md @@ -43,7 +43,7 @@ Cosmos `sdk.AccAddress`. - Address (Bech32): `eth1crwhac03z2pcgu88jfnqnwu66xlthlz2rhljah` - Address (Hex): `0xc0dd7ee1f112838470e7926609bb9ad1bebbfc4a` -- Public Key (Bech32): `ethpub1pfqnmk6pqnwwuw0h9hj58t2hyzwvqc3truhhp5tl5hfucezcfy2rs8470nkyzju2vmk645fzmw2wveaqcqek767kwa0es9rmxe9nmmjq84cpny3fvj6tpg` +- Compressed Public Key (Bech32): `ethpub1pfqnmk6pqnwwuw0h9hj58t2hyzwvqc3truhhp5tl5hfucezcfy2rs8470nkyzju2vmk645fzmw2wveaqcqek767kwa0es9rmxe9nmmjq84cpny3fvj6tpg` ## Next {hide} diff --git a/tests/rpc_test.go b/tests/rpc_test.go index bf775b92..6bfc2d58 100644 --- a/tests/rpc_test.go +++ b/tests/rpc_test.go @@ -758,7 +758,7 @@ func TestEth_EstimateGas(t *testing.T) { err := json.Unmarshal(rpcRes.Result, &gas) require.NoError(t, err, string(rpcRes.Result)) - require.Equal(t, "0x1051d", gas) + require.Equal(t, "0xfd40", gas) } func TestEth_EstimateGas_ContractDeployment(t *testing.T) { diff --git a/types/account.go b/types/account.go index 4f22eccd..8020ed1b 100644 --- a/types/account.go +++ b/types/account.go @@ -11,8 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" - ethcmn "github.com/ethereum/go-ethereum/common" ethcrypto "github.com/ethereum/go-ethereum/crypto" ) @@ -79,7 +77,7 @@ func (acc *EthAccount) SetBalance(amt sdk.Int) { type ethermintAccountPretty struct { Address sdk.AccAddress `json:"address" yaml:"address"` Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey []byte `json:"public_key" yaml:"public_key"` + PubKey string `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` CodeHash string `json:"code_hash" yaml:"code_hash"` @@ -95,8 +93,13 @@ func (acc EthAccount) MarshalYAML() (interface{}, error) { CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), } + var err error + if acc.PubKey != nil { - alias.PubKey = acc.PubKey.Bytes() + alias.PubKey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) + if err != nil { + return nil, err + } } bz, err := yaml.Marshal(alias) @@ -117,8 +120,13 @@ func (acc EthAccount) MarshalJSON() ([]byte, error) { CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), } + var err error + if acc.PubKey != nil { - alias.PubKey = acc.PubKey.Bytes() + alias.PubKey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) + if err != nil { + return nil, err + } } return json.Marshal(alias) @@ -126,26 +134,34 @@ func (acc EthAccount) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals raw JSON bytes into an EthAccount. func (acc *EthAccount) UnmarshalJSON(bz []byte) error { - acc.BaseAccount = &authtypes.BaseAccount{} - var alias ethermintAccountPretty + var ( + alias ethermintAccountPretty + err error + ) + if err := json.Unmarshal(bz, &alias); err != nil { return err } - if alias.PubKey != nil { - pubKey, err := tmamino.PubKeyFromBytes(alias.PubKey) + acc.BaseAccount = &authtypes.BaseAccount{ + Coins: alias.Coins, + Address: alias.Address, + AccountNumber: alias.AccountNumber, + Sequence: alias.Sequence, + } + acc.CodeHash = ethcmn.Hex2Bytes(alias.CodeHash) + + if alias.PubKey != "" { + acc.BaseAccount.PubKey, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) if err != nil { return err } - - acc.BaseAccount.PubKey = pubKey } - - acc.BaseAccount.Coins = alias.Coins - acc.BaseAccount.Address = alias.Address - acc.BaseAccount.AccountNumber = alias.AccountNumber - acc.BaseAccount.Sequence = alias.Sequence - acc.CodeHash = ethcmn.Hex2Bytes(alias.CodeHash) - return nil } + +// String implements the fmt.Stringer interface +func (acc EthAccount) String() string { + out, _ := yaml.Marshal(acc) + return string(out) +} diff --git a/types/account_test.go b/types/account_test.go index cb13b66e..98cad2bf 100644 --- a/types/account_test.go +++ b/types/account_test.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -35,7 +36,7 @@ func TestEthermintAccountJSON(t *testing.T) { require.Equal(t, string(bz1), string(bz)) var a EthAccount - require.NoError(t, json.Unmarshal(bz, &a)) + require.NoError(t, a.UnmarshalJSON(bz)) require.Equal(t, ethAcc.String(), a.String()) require.Equal(t, ethAcc.PubKey, a.PubKey) } @@ -58,3 +59,55 @@ func TestSecpPubKeyJSON(t *testing.T) { require.NoError(t, err) require.Equal(t, pubk, pubkey) } + +func TestEthermintAccount_String(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + balance := sdk.NewCoins(sdk.NewCoin(DenomDefault, sdk.OneInt())) + baseAcc := auth.NewBaseAccount(addr, balance, pubkey, 10, 50) + ethAcc := EthAccount{BaseAccount: baseAcc, CodeHash: []byte{1, 2}} + + config := sdk.GetConfig() + SetBech32Prefixes(config) + + bech32pubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubkey) + require.NoError(t, err) + + accountStr := fmt.Sprintf(`| + address: %s + coins: + - denom: aphoton + amount: "1" + public_key: %s + account_number: 10 + sequence: 50 + code_hash: "0102" +`, addr, bech32pubkey) + + require.Equal(t, accountStr, ethAcc.String()) + + i, err := ethAcc.MarshalYAML() + require.NoError(t, err) + + var ok bool + accountStr, ok = i.(string) + require.True(t, ok) + require.Contains(t, accountStr, addr.String()) + require.Contains(t, accountStr, bech32pubkey) +} + +func TestEthermintAccount_MarshalJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + balance := sdk.NewCoins(sdk.NewCoin(DenomDefault, sdk.OneInt())) + baseAcc := auth.NewBaseAccount(addr, balance, pubkey, 10, 50) + ethAcc := &EthAccount{BaseAccount: baseAcc, CodeHash: []byte{1, 2}} + + bz, err := ethAcc.MarshalJSON() + require.NoError(t, err) + + res := new(EthAccount) + err = res.UnmarshalJSON(bz) + require.NoError(t, err) + require.Equal(t, ethAcc, res) +} diff --git a/types/config_test.go b/types/config_test.go index 966b0bcf..0b985074 100644 --- a/types/config_test.go +++ b/types/config_test.go @@ -10,6 +10,8 @@ import ( func TestSetBech32Prefixes(t *testing.T) { config := sdk.GetConfig() + config = sdk.NewConfig() // reset config values + require.Equal(t, sdk.Bech32PrefixAccAddr, config.GetBech32AccountAddrPrefix()) require.Equal(t, sdk.Bech32PrefixAccPub, config.GetBech32AccountPubPrefix()) require.Equal(t, sdk.Bech32PrefixValAddr, config.GetBech32ValidatorAddrPrefix()) @@ -36,8 +38,10 @@ func TestSetBech32Prefixes(t *testing.T) { func TestSetCoinType(t *testing.T) { config := sdk.GetConfig() require.Equal(t, sdk.CoinType, int(config.GetCoinType())) + require.Equal(t, sdk.FullFundraiserPath, config.GetFullFundraiserPath()) SetBip44CoinType(config) require.Equal(t, Bip44CoinType, int(config.GetCoinType())) require.Equal(t, sdk.GetConfig().GetCoinType(), config.GetCoinType()) + require.Equal(t, sdk.GetConfig().GetFullFundraiserPath(), config.GetFullFundraiserPath()) }