Skip to content

Commit fd7badc

Browse files
author
Alex | Interchain Labs
authored
chore: add address equivalence test (#203)
* add equivalent test * ftmr * lint-fix * clean-up * ok
1 parent 578b346 commit fd7badc

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

utils/utils_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
package utils
22

33
import (
4+
"bytes"
45
"fmt"
56
"testing"
67

8+
"github.com/ethereum/go-ethereum/common"
79
"github.com/stretchr/testify/require"
810

11+
cryptocodec "github.com/cosmos/evm/crypto/codec"
912
"github.com/cosmos/evm/crypto/ethsecp256k1"
13+
"github.com/cosmos/evm/crypto/hd"
14+
"github.com/cosmos/evm/types"
1015

16+
"github.com/cosmos/cosmos-sdk/codec"
17+
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
18+
sdkhd "github.com/cosmos/cosmos-sdk/crypto/hd"
19+
"github.com/cosmos/cosmos-sdk/crypto/keyring"
1120
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
1221
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
1322
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
@@ -291,3 +300,137 @@ func TestGetIBCDenomAddress(t *testing.T) {
291300
})
292301
}
293302
}
303+
304+
// TestAccountEquivalence tests and demonstrates the equivalence of accounts
305+
func TestAccountEquivalence(t *testing.T) {
306+
registry := codectypes.NewInterfaceRegistry()
307+
cryptocodec.RegisterInterfaces(registry)
308+
cdc := codec.NewProtoCodec(registry)
309+
310+
uid := "inMemory"
311+
mnemonic := "aunt imitate maximum student guard unhappy guard rotate marine panel negative merit record priority zoo voice mixture boost describe fruit often occur expect teach"
312+
313+
// create a keyring with support for ethsecp and secp (default supported)
314+
kb, err := keyring.New("keybasename", keyring.BackendMemory, t.TempDir(), nil, cdc, hd.EthSecp256k1Option())
315+
require.NoError(t, err)
316+
317+
// get the proper signing algorithms
318+
keyringAlgos, _ := kb.SupportedAlgorithms()
319+
algoEvm, err := keyring.NewSigningAlgoFromString(string(hd.EthSecp256k1Type), keyringAlgos)
320+
require.NoError(t, err)
321+
legacyAlgo, err := keyring.NewSigningAlgoFromString(string(sdkhd.Secp256k1Type), keyringAlgos)
322+
require.NoError(t, err)
323+
324+
// legacy account using "regular" cosmos secp
325+
// and coin type 118
326+
legacyCosmosKey, err := kb.NewAccount(uid, mnemonic, keyring.DefaultBIP39Passphrase, sdk.FullFundraiserPath, legacyAlgo)
327+
require.NoError(t, err)
328+
329+
// account using ethsecp
330+
// and coin type 118
331+
cosmsosKey, err := kb.NewAccount(uid, mnemonic, keyring.DefaultBIP39Passphrase, sdk.FullFundraiserPath, algoEvm)
332+
require.NoError(t, err)
333+
334+
// account using ethsecp
335+
// and coin type 60
336+
evmKey, err := kb.NewAccount(uid, mnemonic, keyring.DefaultBIP39Passphrase, types.BIP44HDPath, algoEvm)
337+
require.NoError(t, err)
338+
339+
// verify that none of these three keys are equal
340+
require.NotEqual(t, legacyCosmosKey, cosmsosKey)
341+
require.NotEqual(t, legacyCosmosKey.String(), cosmsosKey.String())
342+
require.NotEqual(t, legacyCosmosKey.PubKey.String(), cosmsosKey.PubKey.String())
343+
344+
require.NotEqual(t, legacyCosmosKey, evmKey)
345+
require.NotEqual(t, legacyCosmosKey.String(), evmKey.String())
346+
require.NotEqual(t, legacyCosmosKey.PubKey.String(), evmKey.PubKey.String())
347+
348+
require.NotEqual(t, cosmsosKey, evmKey)
349+
require.NotEqual(t, cosmsosKey.String(), evmKey.String())
350+
require.NotEqual(t, cosmsosKey.PubKey.String(), evmKey.PubKey.String())
351+
352+
// calls:
353+
// sha := sha256.Sum256(pubKey.Key)
354+
// hasherRIPEMD160 := ripemd160.New()
355+
// hasherRIPEMD160.Write(sha[:])
356+
//
357+
// one way sha256 -> ripeMD160
358+
// this is the actual bech32 algorithm
359+
legacyAddress, err := legacyCosmosKey.GetAddress() //
360+
require.NoError(t, err)
361+
362+
legacyPubKey, err := legacyCosmosKey.GetPubKey()
363+
require.NoError(t, err)
364+
365+
// create an ethsecp key from the same exact pubkey bytes
366+
// this will mean that calling `Address()` will use the Keccack hash of the pubkey
367+
ethSecpPubkey := ethsecp256k1.PubKey{Key: legacyPubKey.Bytes()}
368+
369+
// calls:
370+
// pubBytes := FromECDSAPub(&p)
371+
// return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
372+
//
373+
// one way keccak hash
374+
// because the key implementation points to it to call the EVM methods
375+
ethSecpAddress := ethSecpPubkey.Address().Bytes()
376+
require.False(t, bytes.Equal(legacyAddress.Bytes(), ethSecpAddress))
377+
trueHexLegacy, err := HexAddressFromBech32String(sdk.AccAddress(ethSecpAddress).String())
378+
require.NoError(t, err)
379+
380+
// deriving a legacy bech32 from the legacy address
381+
legacyBech32Address := legacyAddress.String()
382+
383+
// this just converts the ripeMD(sha(pubkey)) from bech32 formatting style to hex
384+
gotHexLegacy, err := HexAddressFromBech32String(legacyBech32Address)
385+
require.NoError(t, err)
386+
require.NotEqual(t, trueHexLegacy.Hex(), gotHexLegacy.Hex())
387+
388+
fmt.Println("\nLegacy Ethereum address:\t\t", gotHexLegacy.Hex()) //
389+
fmt.Println("True Legacy Ethereum address:\t", trueHexLegacy.Hex())
390+
fmt.Println("Legacy Bech32 address:\t\t\t", legacyBech32Address)
391+
fmt.Println()
392+
393+
// calls:
394+
// pubBytes := FromECDSAPub(&p)
395+
// return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
396+
//
397+
// one way keccak hash
398+
// because the key implementation points to it to call the EVM methods
399+
cosmosAddress, err := cosmsosKey.GetAddress() //
400+
require.NoError(t, err)
401+
require.NotEqual(t, legacyAddress, cosmosAddress)
402+
require.False(t, legacyAddress.Equals(cosmosAddress))
403+
404+
// calls:
405+
// pubBytes := FromECDSAPub(&p)
406+
// return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
407+
//
408+
// one way keccak hash
409+
evmAddress, err := evmKey.GetAddress()
410+
require.NoError(t, err)
411+
require.NotEqual(t, cosmosAddress, evmAddress)
412+
require.False(t, cosmosAddress.Equals(evmAddress))
413+
414+
// we have verified that two privkeys generated from the same mnemonic (on different HD paths) are different
415+
// now, let's derive the 0x and bech32 addresses of our EVM key
416+
t.Run("verify 0x and cosmos formatted address string is the same for an EVM key", func(t *testing.T) {
417+
addr := evmAddress
418+
require.NoError(t, err)
419+
_, err = kb.KeyByAddress(addr)
420+
require.NoError(t, err)
421+
422+
bech32 := addr.String()
423+
// Decode from hex to bytes
424+
425+
// Convert to Ethereum address
426+
address := common.BytesToAddress(addr)
427+
428+
fmt.Println("\nEthereum address:", address.Hex())
429+
fmt.Println("Bech32 address:", bech32)
430+
431+
require.Equal(t, bech32, Bech32StringFromHexAddress(address.Hex()))
432+
gotAddr, err := HexAddressFromBech32String(bech32)
433+
require.NoError(t, err)
434+
require.Equal(t, address.Hex(), gotAddr.Hex())
435+
})
436+
}

0 commit comments

Comments
 (0)