-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: enforce checksum format for asset address in ZRC20 (#3278)
* add checksum check * changelog * add migration script * add logs * Update pkg/crypto/evm_address.go Co-authored-by: Dmitry S <[email protected]> * remove duplicated logs --------- Co-authored-by: Dmitry S <[email protected]>
- Loading branch information
Showing
12 changed files
with
371 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package crypto | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
|
||
"github.com/zeta-chain/node/pkg/constant" | ||
) | ||
|
||
// IsEmptyAddress returns true if the address is empty | ||
func IsEmptyAddress(address common.Address) bool { | ||
return address == (common.Address{}) || address.Hex() == constant.EVMZeroAddress | ||
} | ||
|
||
// IsEVMAddress returns true if the string is an EVM address | ||
// independently of the checksum format | ||
func IsEVMAddress(address string) bool { | ||
return len(address) == 42 && strings.HasPrefix(address, "0x") && common.IsHexAddress(address) | ||
} | ||
|
||
// IsChecksumAddress returns true if the EVM address string is a valid checksum address | ||
// See https://eips.ethereum.org/EIPS/eip-55 | ||
func IsChecksumAddress(address string) bool { | ||
return address == common.HexToAddress(address).Hex() | ||
} | ||
|
||
// ToChecksumAddress returns the checksum address of the given EVM address | ||
func ToChecksumAddress(address string) string { | ||
return common.HexToAddress(address).Hex() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package crypto | ||
|
||
import ( | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/require" | ||
"github.com/zeta-chain/node/pkg/constant" | ||
"testing" | ||
) | ||
|
||
func TestIsEmptyAddress(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
address common.Address | ||
want bool | ||
}{ | ||
{ | ||
name: "empty address", | ||
address: common.Address{}, | ||
want: true, | ||
}, | ||
{ | ||
name: "zero address", | ||
address: common.HexToAddress(constant.EVMZeroAddress), | ||
want: true, | ||
}, | ||
{ | ||
name: "non empty address", | ||
address: common.HexToAddress("0x1"), | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
require.EqualValues(t, tt.want, IsEmptyAddress(tt.address)) | ||
}) | ||
} | ||
} | ||
|
||
func TestIsEVMAddress(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
address string | ||
want bool | ||
}{ | ||
{ | ||
name: "EVM address", | ||
address: "0x5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
want: true, | ||
}, | ||
{ | ||
name: "EVM address with invalid checksum", | ||
address: "0x5a4f260a7D716c859A2736151CB38b9c58C32c64", | ||
want: true, | ||
}, | ||
{ | ||
name: "EVM address all lowercase", | ||
address: "0x5a4f260a7d716c859a2736151cb38b9c58c32c64", | ||
want: true, | ||
}, | ||
{ | ||
name: "EVM address all uppercase", | ||
address: "0x5A4F260A7D716C859A2736151CB38B9C58C32C64", | ||
want: true, | ||
}, | ||
{ | ||
name: "invalid EVM address", | ||
address: "5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
}, | ||
{ | ||
name: "empty address", | ||
address: "", | ||
}, | ||
{ | ||
name: "non EVM address", | ||
address: "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
require.EqualValues(t, tt.want, IsEVMAddress(tt.address)) | ||
}) | ||
} | ||
} | ||
|
||
func TestIsChecksumAddress(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
address string | ||
want bool | ||
}{ | ||
{ | ||
name: "checksum address", | ||
address: "0x5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
want: true, | ||
}, | ||
{ | ||
name: "invalid checksum address", | ||
address: "0x5a4f260a7D716c859A2736151CB38b9c58C32c64", | ||
}, | ||
{ | ||
name: "all lowercase", | ||
address: "0x5a4f260a7d716c859a2736151cb38b9c58c32c64", | ||
}, | ||
{ | ||
name: "all uppercase", | ||
address: "0x5A4F260A7D716C859A2736151CB38B9C58C32C64", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
require.EqualValues(t, tt.want, IsChecksumAddress(tt.address)) | ||
}) | ||
} | ||
} | ||
|
||
func TestToChecksumAddress(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
address string | ||
want string | ||
}{ | ||
{ | ||
name: "checksum address", | ||
address: "0x5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
want: "0x5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
}, | ||
{ | ||
name: "all lowercase", | ||
address: "0x5a4f260a7d716c859a2736151cb38b9c58c32c64", | ||
want: "0x5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
}, | ||
{ | ||
name: "all uppercase", | ||
address: "0x5A4F260A7D716C859A2736151CB38B9C58C32C64", | ||
want: "0x5a4f260A7D716c859A2736151cB38b9c58C32c64", | ||
}, | ||
{ | ||
name: "empty address returns null address", | ||
address: "", | ||
want: "0x0000000000000000000000000000000000000000", | ||
}, | ||
{ | ||
name: "non evm address returns null address", | ||
address: "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", | ||
want: "0x0000000000000000000000000000000000000000", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
require.EqualValues(t, tt.want, ToChecksumAddress(tt.address)) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package keeper | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
v3 "github.com/zeta-chain/node/x/fungible/migrations/v3" | ||
) | ||
|
||
// Migrator is a struct for handling in-place store migrations. | ||
type Migrator struct { | ||
fungibleKeeper Keeper | ||
} | ||
|
||
// NewMigrator returns a new Migrator. | ||
func NewMigrator(keeper Keeper) Migrator { | ||
return Migrator{ | ||
fungibleKeeper: keeper, | ||
} | ||
} | ||
|
||
// Migrate2to3 migrates the store from consensus version 2 to 3 | ||
func (m Migrator) Migrate2to3(ctx sdk.Context) error { | ||
return v3.MigrateStore(ctx, m.fungibleKeeper) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package v3 | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
"github.com/zeta-chain/node/pkg/crypto" | ||
"github.com/zeta-chain/node/x/fungible/types" | ||
) | ||
|
||
type fungibleKeeper interface { | ||
GetAllForeignCoins(ctx sdk.Context) (list []types.ForeignCoins) | ||
SetForeignCoins(ctx sdk.Context, foreignCoins types.ForeignCoins) | ||
} | ||
|
||
// MigrateStore migrates the x/fungible module state from the consensus version 2 to 3 | ||
// It updates all existing address in ForeignCoin to use checksum format if the address is EVM type | ||
func MigrateStore(ctx sdk.Context, fungibleKeeper fungibleKeeper) error { | ||
fcs := fungibleKeeper.GetAllForeignCoins(ctx) | ||
for _, fc := range fcs { | ||
if fc.Asset != "" && crypto.IsEVMAddress(fc.Asset) && !crypto.IsChecksumAddress(fc.Asset) { | ||
checksumAddress := crypto.ToChecksumAddress(fc.Asset) | ||
ctx.Logger().Info("Patching zrc20 asset", "zrc20", fc.Symbol, "old", fc.Asset, "new", checksumAddress) | ||
|
||
fc.Asset = checksumAddress | ||
fungibleKeeper.SetForeignCoins(ctx, fc) | ||
} | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.