From 004f51a311edab4b8971a85afdf3429919424043 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Fri, 29 Jan 2021 23:48:26 -0300 Subject: [PATCH] backport #8479 --- CHANGELOG.md | 6 ++++ x/bank/types/metadata.go | 39 +++++++++++++++++++++++-- x/bank/types/metadata_test.go | 55 ++++++++++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 973ea6e054ce..f9190ec3d8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,12 @@ Ref: https://keepachangelog.com/en/1.0.0/ # Changelog +## Unreleased + +### Improvements + +* (x/bank) [\#8479](https://github.com/cosmos/cosmos-sdk/pull/8479) Aditional client denom metadata validation for `base` and `display` denoms. + ## [v0.41.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.0) - 2021-01-26 ### State Machine Breaking diff --git a/x/bank/types/metadata.go b/x/bank/types/metadata.go index 5106964e994e..ce7794053d93 100644 --- a/x/bank/types/metadata.go +++ b/x/bank/types/metadata.go @@ -1,13 +1,19 @@ package types import ( + "errors" "fmt" "strings" sdk "github.com/cosmos/cosmos-sdk/types" ) -// Validate performs a basic validation of the coin metadata fields +// Validate performs a basic validation of the coin metadata fields. It checks: +// - Base and Display denominations are valid coin denominations +// - Base and Display denominations are present in the DenomUnit slice +// - Base denomination has exponent 0 +// - Denomination units are sorted in ascending order +// - Denomination units not duplicated func (m Metadata) Validate() error { if err := sdk.ValidateDenom(m.Base); err != nil { return fmt.Errorf("invalid metadata base denom: %w", err) @@ -17,12 +23,37 @@ func (m Metadata) Validate() error { return fmt.Errorf("invalid metadata display denom: %w", err) } + var ( + hasDisplay bool + currentExponent uint32 // check that the exponents are increasing + ) + seenUnits := make(map[string]bool) - for _, denomUnit := range m.DenomUnits { + + for i, denomUnit := range m.DenomUnits { + // The first denomination unit MUST be the base + if i == 0 { + // validate denomination and exponent + if denomUnit.Denom != m.Base { + return fmt.Errorf("metadata's first denomination unit must be the one with base denom '%s'", m.Base) + } + if denomUnit.Exponent != 0 { + return fmt.Errorf("the exponent for base denomination unit %s must be 0", m.Base) + } + } else if currentExponent >= denomUnit.Exponent { + return errors.New("denom units should be sorted asc by exponent") + } + + currentExponent = denomUnit.Exponent + if seenUnits[denomUnit.Denom] { return fmt.Errorf("duplicate denomination unit %s", denomUnit.Denom) } + if denomUnit.Denom == m.Display { + hasDisplay = true + } + if err := denomUnit.Validate(); err != nil { return err } @@ -30,6 +61,10 @@ func (m Metadata) Validate() error { seenUnits[denomUnit.Denom] = true } + if !hasDisplay { + return fmt.Errorf("metadata must contain a denomination unit with display denom '%s'", m.Display) + } + return nil } diff --git a/x/bank/types/metadata_test.go b/x/bank/types/metadata_test.go index 29f4d220f95f..64241e6098e0 100644 --- a/x/bank/types/metadata_test.go +++ b/x/bank/types/metadata_test.go @@ -51,7 +51,7 @@ func TestMetadataValidate(t *testing.T) { Description: "The native staking token of the Cosmos Hub.", DenomUnits: []*types.DenomUnit{ {"uatom", uint32(0), []string{"microatom"}}, - {"uatom", uint32(0), []string{"microatom"}}, + {"uatom", uint32(1), []string{"microatom"}}, }, Base: "uatom", Display: "atom", @@ -94,6 +94,59 @@ func TestMetadataValidate(t *testing.T) { }, true, }, + { + "no base denom unit", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "base denom exponent not zero", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(1), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "no display denom unit", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, + { + "denom units not sorted", + types.Metadata{ + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"atom", uint32(6), nil}, + {"matom", uint32(3), []string{"milliatom"}}, + }, + Base: "uatom", + Display: "atom", + }, + true, + }, } for _, tc := range testCases {