From d6783707dc1a65b7ee71f5bd0f9c339d80f96fb3 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 8 Sep 2020 17:58:19 +0200 Subject: [PATCH] types: unmarshal account from hex address (#504) * types: unmarshal account from hex address: * changelog --- CHANGELOG.md | 1 + types/account.go | 41 ++++++++++++++++++++++++++++++++++++++++- types/account_test.go | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf1354378..4a59b99ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (types) [\#504](https://github.com/ChainSafe/ethermint/pull/504) Unmarshal a JSON `EthAccount` using an Ethereum hex address in addition to Bech32. * (types) [\#503](https://github.com/ChainSafe/ethermint/pull/503) Add `--coin-denom` flag to testnet command that sets the given coin denomination to SDK and Ethermint parameters. * (types) [\#502](https://github.com/ChainSafe/ethermint/pull/502) `EthAccount` now also exposes the Ethereum hex address in `string` format to clients. * (types) [\#494](https://github.com/ChainSafe/ethermint/pull/494) Update `EthAccount` public key JSON type to `string`. diff --git a/types/account.go b/types/account.go index b5b480099..6ff23bbe3 100644 --- a/types/account.go +++ b/types/account.go @@ -1,12 +1,14 @@ package types import ( + "bytes" "encoding/json" "fmt" "gopkg.in/yaml.v2" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -114,9 +116,15 @@ func (acc EthAccount) MarshalYAML() (interface{}, error) { // MarshalJSON returns the JSON representation of an EthAccount. func (acc EthAccount) MarshalJSON() ([]byte, error) { + var ethAddress = "" + + if acc.BaseAccount != nil && acc.Address != nil { + ethAddress = acc.EthAddress().String() + } + alias := ethermintAccountPretty{ Address: acc.Address, - EthAddress: acc.EthAddress().String(), + EthAddress: ethAddress, Coins: acc.Coins, AccountNumber: acc.AccountNumber, Sequence: acc.Sequence, @@ -146,6 +154,37 @@ func (acc *EthAccount) UnmarshalJSON(bz []byte) error { return err } + switch { + case !alias.Address.Empty() && alias.EthAddress != "": + // Both addresses provided. Verify correctness + ethAddress := ethcmn.HexToAddress(alias.EthAddress) + ethAddressFromAccAddress := ethcmn.BytesToAddress(alias.Address.Bytes()) + + if !bytes.Equal(ethAddress.Bytes(), alias.Address.Bytes()) { + err = sdkerrors.Wrapf( + sdkerrors.ErrInvalidAddress, + "expected %s, got %s", + ethAddressFromAccAddress.String(), ethAddress.String(), + ) + } + + case !alias.Address.Empty() && alias.EthAddress == "": + // unmarshal sdk.AccAddress only. Do nothing here + case alias.Address.Empty() && alias.EthAddress != "": + // retrieve sdk.AccAddress from ethereum address + ethAddress := ethcmn.HexToAddress(alias.EthAddress) + alias.Address = sdk.AccAddress(ethAddress.Bytes()) + case alias.Address.Empty() && alias.EthAddress == "": + err = sdkerrors.Wrapf( + sdkerrors.ErrInvalidAddress, + "account must contain address in Ethereum Hex or Cosmos Bech32 format", + ) + } + + if err != nil { + return err + } + acc.BaseAccount = &authtypes.BaseAccount{ Coins: alias.Coins, Address: alias.Address, diff --git a/types/account_test.go b/types/account_test.go index 976e9a464..20c9b9ec0 100644 --- a/types/account_test.go +++ b/types/account_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + emintcrypto "github.com/cosmos/ethermint/crypto" ) @@ -106,9 +106,43 @@ func TestEthermintAccount_MarshalJSON(t *testing.T) { bz, err := ethAcc.MarshalJSON() require.NoError(t, err) + require.Contains(t, string(bz), ethAcc.EthAddress().String()) res := new(EthAccount) err = res.UnmarshalJSON(bz) require.NoError(t, err) require.Equal(t, ethAcc, res) + + bech32pubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubkey) + require.NoError(t, err) + + // test that the sdk.AccAddress is populated from the hex address + jsonAcc := fmt.Sprintf( + `{"address":"","eth_address":"%s","coins":[{"denom":"aphoton","amount":"1"}],"public_key":"%s","account_number":10,"sequence":50,"code_hash":"0102"}`, + ethAcc.EthAddress().String(), bech32pubkey, + ) + + res = new(EthAccount) + err = res.UnmarshalJSON([]byte(jsonAcc)) + require.NoError(t, err) + require.Equal(t, addr.String(), res.Address.String()) + + jsonAcc = fmt.Sprintf( + `{"address":"","eth_address":"","coins":[{"denom":"aphoton","amount":"1"}],"public_key":"%s","account_number":10,"sequence":50,"code_hash":"0102"}`, + bech32pubkey, + ) + + res = new(EthAccount) + err = res.UnmarshalJSON([]byte(jsonAcc)) + require.Error(t, err, "should fail if both address are empty") + + // test that the sdk.AccAddress is populated from the hex address + jsonAcc = fmt.Sprintf( + `{"address": "%s","eth_address":"0x0000000000000000000000000000000000000000","coins":[{"denom":"aphoton","amount":"1"}],"public_key":"%s","account_number":10,"sequence":50,"code_hash":"0102"}`, + ethAcc.Address.String(), bech32pubkey, + ) + + res = new(EthAccount) + err = res.UnmarshalJSON([]byte(jsonAcc)) + require.Error(t, err, "should fail if addresses mismatch") }