Skip to content

Commit 2bbff66

Browse files
srdtrkaljo242vladjdk
authored
feat(encoding): include an address codec implementation (#665)
* feat: address codec with signed commit * docs: update godoc * imp: use bech32 codec for bytes to string * style * imp: move * lint: fix * test: fix * imp: rewire app.go * test: replace bech32 codec fully * test: evmd simul test passing * lint: golanci * imp: more usage * imp: move --------- Co-authored-by: Alex | Interchain Labs <[email protected]> Co-authored-by: Vlad J <[email protected]>
1 parent ac4e9f6 commit 2bbff66

File tree

21 files changed

+347
-68
lines changed

21 files changed

+347
-68
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252
### FEATURES
5353

54+
- [\#665](https://github.com/cosmos/evm/pull/665) Add EvmCodec address codec implementation
5455
- [\#346](https://github.com/cosmos/evm/pull/346) Add eth_createAccessList method and implementation
5556
- [\#337](https://github.com/cosmos/evm/pull/337) Support state overrides in eth_call.
5657
- [\#502](https://github.com/cosmos/evm/pull/502) Add block time in derived logs.

encoding/address/address_codec.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package address
2+
3+
import (
4+
"strings"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
8+
"github.com/cosmos/evm/utils"
9+
10+
"cosmossdk.io/core/address"
11+
12+
sdk "github.com/cosmos/cosmos-sdk/types"
13+
"github.com/cosmos/cosmos-sdk/types/bech32"
14+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
15+
)
16+
17+
var _ address.Codec = (*evmCodec)(nil)
18+
19+
// evmCodec defines an address codec for EVM compatible cosmos modules
20+
type evmCodec struct {
21+
bech32Prefix string
22+
}
23+
24+
// NewEvmCodec returns a new EvmCodec with the given bech32 prefix
25+
func NewEvmCodec(prefix string) address.Codec {
26+
return evmCodec{prefix}
27+
}
28+
29+
// StringToBytes decodes text to bytes using either hex or bech32 encoding
30+
func (bc evmCodec) StringToBytes(text string) ([]byte, error) {
31+
if len(strings.TrimSpace(text)) == 0 {
32+
return []byte{}, sdkerrors.ErrInvalidAddress.Wrap("empty address string is not allowed")
33+
}
34+
35+
switch {
36+
case common.IsHexAddress(text):
37+
return common.HexToAddress(text).Bytes(), nil
38+
case utils.IsBech32Address(text):
39+
hrp, bz, err := bech32.DecodeAndConvert(text)
40+
if err != nil {
41+
return nil, err
42+
}
43+
if hrp != bc.bech32Prefix {
44+
return nil, sdkerrors.ErrLogic.Wrapf("hrp does not match bech32 prefix: expected '%s' got '%s'", bc.bech32Prefix, hrp)
45+
}
46+
if err := sdk.VerifyAddressFormat(bz); err != nil {
47+
return nil, err
48+
}
49+
return bz, nil
50+
default:
51+
return nil, sdkerrors.ErrUnknownAddress.Wrapf("unknown address format: %s", text)
52+
}
53+
}
54+
55+
// BytesToString decodes bytes to text
56+
func (bc evmCodec) BytesToString(bz []byte) (string, error) {
57+
text, err := bech32.ConvertAndEncode(bc.bech32Prefix, bz)
58+
if err != nil {
59+
return "", err
60+
}
61+
62+
return text, nil
63+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package address_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
"github.com/stretchr/testify/require"
8+
9+
evmaddress "github.com/cosmos/evm/encoding/address"
10+
11+
"cosmossdk.io/core/address"
12+
13+
sdk "github.com/cosmos/cosmos-sdk/types"
14+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
15+
)
16+
17+
const (
18+
hex = "0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02E"
19+
bech32 = "cosmos10jmp6sgh4cc6zt3e8gw05wavvejgr5pwsjskvv"
20+
)
21+
22+
func TestStringToBytes(t *testing.T) {
23+
config := sdk.GetConfig()
24+
config.SetBech32PrefixForAccount("cosmos", "cosmospub")
25+
addrBz := common.HexToAddress(hex).Bytes()
26+
27+
testCases := []struct {
28+
name string
29+
cdcPrefix string
30+
input string
31+
expBz []byte
32+
expErr error
33+
}{
34+
{
35+
"success: valid bech32 address",
36+
"cosmos",
37+
bech32,
38+
addrBz,
39+
nil,
40+
},
41+
{
42+
"success: valid hex address",
43+
"cosmos",
44+
hex,
45+
addrBz,
46+
nil,
47+
},
48+
{
49+
"failure: invalid bech32 address (wrong prefix)",
50+
"evmos",
51+
bech32,
52+
nil,
53+
sdkerrors.ErrLogic.Wrapf("hrp does not match bech32 prefix: expected '%s' got '%s'", "evmos", "cosmos"),
54+
},
55+
{
56+
"failure: invalid bech32 address (too long)",
57+
"cosmos",
58+
"cosmos10jmp6sgh4cc6zt3e8gw05wavvejgr5pwsjskvvv", // extra char at the end
59+
nil,
60+
sdkerrors.ErrUnknownAddress,
61+
},
62+
{
63+
"failure: invalid bech32 address (invalid format)",
64+
"cosmos",
65+
"cosmos10jmp6sgh4cc6zt3e8gw05wavvejgr5pwsjskv", // missing last char
66+
nil,
67+
sdkerrors.ErrUnknownAddress,
68+
},
69+
{
70+
"failure: invalid hex address (odd length)",
71+
"cosmos",
72+
"0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02", // missing last char
73+
nil,
74+
sdkerrors.ErrUnknownAddress,
75+
},
76+
{
77+
"failure: invalid hex address (even length)",
78+
"cosmos",
79+
"0x7cB61D4117AE31a12E393a1Cfa3BaC666481D0", // missing last 2 char
80+
nil,
81+
sdkerrors.ErrUnknownAddress,
82+
},
83+
{
84+
"failure: invalid hex address (too long)",
85+
"cosmos",
86+
"0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02E00", // extra 2 char at the end
87+
nil,
88+
sdkerrors.ErrUnknownAddress,
89+
},
90+
{
91+
"failure: empty string",
92+
"cosmos",
93+
"",
94+
nil,
95+
sdkerrors.ErrInvalidAddress.Wrap("empty address string is not allowed"),
96+
},
97+
}
98+
99+
for _, tc := range testCases {
100+
t.Run(tc.name, func(t *testing.T) {
101+
cdc := evmaddress.NewEvmCodec(tc.cdcPrefix)
102+
bz, err := cdc.StringToBytes(tc.input)
103+
if tc.expErr == nil {
104+
require.NoError(t, err)
105+
require.Equal(t, tc.expBz, bz)
106+
} else {
107+
require.ErrorContains(t, err, tc.expErr.Error())
108+
}
109+
})
110+
}
111+
}
112+
113+
func TestBytesToString(t *testing.T) {
114+
// Keep the same fixtures as your StringToBytes test
115+
config := sdk.GetConfig()
116+
config.SetBech32PrefixForAccount("cosmos", "cosmospub")
117+
118+
addrBz := common.HexToAddress(hex).Bytes() // 20 bytes
119+
120+
// Helper codec (used only where we want to derive bytes from the bech32 string)
121+
var cdc address.Codec
122+
123+
type tc struct {
124+
name string
125+
input func() []byte
126+
expRes string
127+
expErr error
128+
}
129+
130+
testCases := []tc{
131+
{
132+
name: "success: from 20-byte input (hex-derived)",
133+
input: func() []byte {
134+
cdc = evmaddress.NewEvmCodec("cosmos")
135+
return addrBz
136+
},
137+
expRes: bech32,
138+
expErr: nil,
139+
},
140+
{
141+
name: "success: from bech32-derived bytes",
142+
input: func() []byte {
143+
cdc = evmaddress.NewEvmCodec("cosmos")
144+
bz, err := cdc.StringToBytes(bech32)
145+
require.NoError(t, err)
146+
require.Len(t, bz, 20)
147+
return bz
148+
},
149+
expRes: bech32,
150+
expErr: nil,
151+
},
152+
}
153+
154+
for _, tc := range testCases {
155+
t.Run(tc.name, func(t *testing.T) {
156+
got, err := cdc.BytesToString(tc.input())
157+
if tc.expErr == nil {
158+
require.NoError(t, err)
159+
require.Equal(t, tc.expRes, got)
160+
} else {
161+
require.ErrorContains(t, err, tc.expErr.Error())
162+
}
163+
})
164+
}
165+
}

encoding/config.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package encoding
33
import (
44
"google.golang.org/protobuf/reflect/protoreflect"
55

6+
evmaddress "github.com/cosmos/evm/encoding/address"
67
enccodec "github.com/cosmos/evm/encoding/codec"
78
"github.com/cosmos/evm/ethereum/eip712"
89
erc20types "github.com/cosmos/evm/x/erc20/types"
@@ -13,7 +14,6 @@ import (
1314

1415
"github.com/cosmos/cosmos-sdk/client"
1516
amino "github.com/cosmos/cosmos-sdk/codec"
16-
"github.com/cosmos/cosmos-sdk/codec/address"
1717
"github.com/cosmos/cosmos-sdk/codec/types"
1818
sdk "github.com/cosmos/cosmos-sdk/types"
1919
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
@@ -33,12 +33,8 @@ type Config struct {
3333
func MakeConfig(evmChainID uint64) Config {
3434
cdc := amino.NewLegacyAmino()
3535
signingOptions := signing.Options{
36-
AddressCodec: address.Bech32Codec{
37-
Bech32Prefix: sdk.GetConfig().GetBech32AccountAddrPrefix(),
38-
},
39-
ValidatorAddressCodec: address.Bech32Codec{
40-
Bech32Prefix: sdk.GetConfig().GetBech32ValidatorAddrPrefix(),
41-
},
36+
AddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
37+
ValidatorAddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
4238
CustomGetSigners: map[protoreflect.FullName]signing.GetSignersFunc{
4339
evmtypes.MsgEthereumTxCustomGetSigner.MsgType: evmtypes.MsgEthereumTxCustomGetSigner.Fn,
4440
erc20types.MsgConvertERC20CustomGetSigner.MsgType: erc20types.MsgConvertERC20CustomGetSigner.Fn,

ethereum/eip712/preprocess_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/stretchr/testify/require"
1010

1111
"github.com/cosmos/evm/encoding"
12+
evmaddress "github.com/cosmos/evm/encoding/address"
1213
"github.com/cosmos/evm/ethereum/eip712"
1314
"github.com/cosmos/evm/testutil/constants"
1415
utiltx "github.com/cosmos/evm/testutil/tx"
@@ -18,7 +19,6 @@ import (
1819
"cosmossdk.io/math"
1920

2021
"github.com/cosmos/cosmos-sdk/client"
21-
"github.com/cosmos/cosmos-sdk/codec/address"
2222
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
2323
"github.com/cosmos/cosmos-sdk/crypto/keyring"
2424
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -107,9 +107,7 @@ func TestLedgerPreprocessing(t *testing.T) {
107107
// Verify tx fields are unchanged
108108
tx := tc.txBuilder.GetTx()
109109

110-
addrCodec := address.Bech32Codec{
111-
Bech32Prefix: sdk.GetConfig().GetBech32AccountAddrPrefix(),
112-
}
110+
addrCodec := evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix())
113111

114112
txFeePayer, err := addrCodec.BytesToString(tx.FeePayer())
115113
require.NoError(t, err)

evmd/app.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
evmante "github.com/cosmos/evm/ante"
2424
evmconfig "github.com/cosmos/evm/config"
2525
evmosencoding "github.com/cosmos/evm/encoding"
26+
evmaddress "github.com/cosmos/evm/encoding/address"
2627
evmmempool "github.com/cosmos/evm/mempool"
2728
srvflags "github.com/cosmos/evm/server/flags"
2829
cosmosevmtypes "github.com/cosmos/evm/types"
@@ -93,7 +94,6 @@ import (
9394
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
9495
"github.com/cosmos/cosmos-sdk/version"
9596
"github.com/cosmos/cosmos-sdk/x/auth"
96-
authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec"
9797
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
9898
"github.com/cosmos/cosmos-sdk/x/auth/posthandler"
9999
authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation"
@@ -292,7 +292,7 @@ func NewExampleApp(
292292
app.AccountKeeper = authkeeper.NewAccountKeeper(
293293
appCodec, runtime.NewKVStoreService(keys[authtypes.StoreKey]),
294294
authtypes.ProtoBaseAccount, evmconfig.GetMaccPerms(),
295-
authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
295+
evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
296296
sdk.GetConfig().GetBech32AccountAddrPrefix(),
297297
authAddr,
298298
)
@@ -327,8 +327,8 @@ func NewExampleApp(
327327
app.AccountKeeper,
328328
app.BankKeeper,
329329
authAddr,
330-
authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
331-
authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
330+
evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
331+
evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
332332
)
333333

334334
app.MintKeeper = mintkeeper.NewKeeper(
@@ -1134,8 +1134,8 @@ func (app *EVMD) AutoCliOpts() autocli.AppOptions {
11341134
return autocli.AppOptions{
11351135
Modules: modules,
11361136
ModuleOptions: runtimeservices.ExtractAutoCLIOptions(app.ModuleManager.Modules),
1137-
AddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
1138-
ValidatorAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
1139-
ConsensusAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
1137+
AddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
1138+
ValidatorAddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
1139+
ConsensusAddressCodec: evmaddress.NewEvmCodec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
11401140
}
11411141
}

0 commit comments

Comments
 (0)