From c2bcf4cdccd98b37d892f57e57e5287aa45221aa Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Mon, 13 Nov 2023 13:34:45 -0500 Subject: [PATCH] refactor: improve address validator structure and make seal explicit (#2022) --- app/keepers.go | 5 +-- go.mod | 16 +++++----- go.sum | 16 ++++++++++ x/nexus/keeper/address.go | 8 ++--- x/nexus/keeper/genesis_test.go | 8 +++-- x/nexus/keeper/grpc_query_test.go | 5 +-- x/nexus/keeper/keeper.go | 30 +++++++++--------- x/nexus/keeper/keeper_test.go | 12 ++++--- x/nexus/keeper/transfer_test.go | 2 +- x/nexus/types/address_validator.go | 51 +++++++++++++++--------------- 10 files changed, 88 insertions(+), 65 deletions(-) diff --git a/app/keepers.go b/app/keepers.go index 1ec63e03f..dddc98447 100644 --- a/app/keepers.go +++ b/app/keepers.go @@ -286,12 +286,13 @@ func initEvmKeeper(appCodec codec.Codec, keys map[string]*sdk.KVStoreKey, keeper func initNexusKeeper(appCodec codec.Codec, keys map[string]*sdk.KVStoreKey, keepers *keeperCache) *nexusKeeper.Keeper { // setting validator will finalize all by sealing it // no more validators can be added - addressValidator := nexusTypes.NewAddressValidator(). + addressValidators := nexusTypes.NewAddressValidators(). AddAddressValidator(evmTypes.ModuleName, evmKeeper.NewAddressValidator()). AddAddressValidator(axelarnetTypes.ModuleName, axelarnetKeeper.NewAddressValidator(getKeeper[axelarnetKeeper.Keeper](keepers))) + addressValidators.Seal() nexusK := nexusKeeper.NewKeeper(appCodec, keys[nexusTypes.StoreKey], keepers.getSubspace(nexusTypes.ModuleName)) - nexusK.SetAddressValidator(addressValidator) + nexusK.SetAddressValidators(addressValidators) return &nexusK } diff --git a/go.mod b/go.mod index 20f4a23da..ce16087dc 100644 --- a/go.mod +++ b/go.mod @@ -35,12 +35,12 @@ require ( github.com/stretchr/testify v1.8.4 github.com/tendermint/tendermint v0.34.27 github.com/tendermint/tm-db v0.6.8-0.20220506192307-f628bb5dc95b - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.15.0 golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 - golang.org/x/mod v0.13.0 - golang.org/x/sync v0.4.0 - golang.org/x/text v0.13.0 - golang.org/x/tools v0.14.0 + golang.org/x/mod v0.14.0 + golang.org/x/sync v0.5.0 + golang.org/x/text v0.14.0 + golang.org/x/tools v0.15.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a google.golang.org/grpc v1.55.0 @@ -171,9 +171,9 @@ require ( github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect google.golang.org/genproto v0.0.0-20230526015343-6ee61e4f9d5f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index ad84d8742..0fc833700 100644 --- a/go.sum +++ b/go.sum @@ -1186,6 +1186,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1229,6 +1231,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1285,6 +1289,8 @@ golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1307,6 +1313,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1393,10 +1401,14 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1408,6 +1420,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1478,6 +1492,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/x/nexus/keeper/address.go b/x/nexus/keeper/address.go index 1049a7c98..059f010c9 100644 --- a/x/nexus/keeper/address.go +++ b/x/nexus/keeper/address.go @@ -90,12 +90,12 @@ func (k Keeper) GetRecipient(ctx sdk.Context, depositAddress exported.CrossChain // ValidateAddress validates the given cross chain address func (k Keeper) ValidateAddress(ctx sdk.Context, address exported.CrossChainAddress) error { - validator := k.getAddressValidator().GetAddressValidator(address.Chain.Module) - if validator == nil { - return fmt.Errorf("unknown module for chain %s", address.Chain.String()) + validate, err := k.getAddressValidator(address.Chain.Module) + if err != nil { + return err } - if err := validator(ctx, address); err != nil { + if err := validate(ctx, address); err != nil { return err } diff --git a/x/nexus/keeper/genesis_test.go b/x/nexus/keeper/genesis_test.go index a905bbf17..b318c8def 100644 --- a/x/nexus/keeper/genesis_test.go +++ b/x/nexus/keeper/genesis_test.go @@ -48,10 +48,12 @@ func setup() (sdk.Context, Keeper) { }, } - router := types.NewAddressValidator() - router.AddAddressValidator(evmTypes.ModuleName, evmkeeper.NewAddressValidator()). + addressValidators := types.NewAddressValidators() + addressValidators.AddAddressValidator(evmTypes.ModuleName, evmkeeper.NewAddressValidator()). AddAddressValidator(axelarnetTypes.ModuleName, axelarnetkeeper.NewAddressValidator(axelarnetK)) - keeper.SetAddressValidator(router) + + addressValidators.Seal() + keeper.SetAddressValidators(addressValidators) return ctx, keeper } diff --git a/x/nexus/keeper/grpc_query_test.go b/x/nexus/keeper/grpc_query_test.go index 1f5aea5e0..b9e42fc83 100644 --- a/x/nexus/keeper/grpc_query_test.go +++ b/x/nexus/keeper/grpc_query_test.go @@ -62,13 +62,14 @@ func TestKeeper_TransfersForChain(t *testing.T) { funcs.MustNoErr(k.RegisterAsset(ctx, evm.Ethereum, exported.NewAsset(axelarnet.NativeAsset, false), utils.MaxUint, time.Hour)) funcs.MustNoErr(k.RegisterAsset(ctx, axelarnet.Axelarnet, exported.NewAsset(axelarnet.NativeAsset, true), utils.MaxUint, time.Hour)) - nexusRouter := types.NewAddressValidator(). + addressValidators := types.NewAddressValidators(). AddAddressValidator("evm", func(sdk.Context, exported.CrossChainAddress) error { return nil }).AddAddressValidator("axelarnet", func(sdk.Context, exported.CrossChainAddress) error { return nil }) - k.SetAddressValidator(nexusRouter) + addressValidators.Seal() + k.SetAddressValidators(addressValidators) }). When("there are some pending transfers", func() { diff --git a/x/nexus/keeper/keeper.go b/x/nexus/keeper/keeper.go index ab4160eb7..7ec38bc32 100644 --- a/x/nexus/keeper/keeper.go +++ b/x/nexus/keeper/keeper.go @@ -10,6 +10,7 @@ import ( "github.com/axelarnetwork/axelar-core/utils" "github.com/axelarnetwork/axelar-core/utils/key" + "github.com/axelarnetwork/axelar-core/x/nexus/exported" "github.com/axelarnetwork/axelar-core/x/nexus/types" ) @@ -42,8 +43,8 @@ type Keeper struct { cdc codec.BinaryCodec params params.Subspace - addressValidator types.AddressValidator - messageRouter types.MessageRouter + addressValidators *types.AddressValidators + messageRouter types.MessageRouter } // NewKeeper returns a new nexus keeper @@ -68,26 +69,25 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params { return p } -// SetAddressValidator sets the nexus address validator. It will panic if called more than once -func (k *Keeper) SetAddressValidator(validator types.AddressValidator) { - if k.addressValidator != nil { - panic("validator already set") +// SetAddressValidators sets the nexus address validator. It will panic if called more than once +func (k *Keeper) SetAddressValidators(validators *types.AddressValidators) { + if !validators.IsSealed() { + panic("address validator must be sealed") } - k.addressValidator = validator + if k.addressValidators != nil { + panic("address validator already set") + } - // In order to avoid invalid or non-deterministic behavior, we seal the validator immediately - // to prevent additionals handlers from being registered after the keeper is initialized. - k.addressValidator.Seal() + k.addressValidators = validators } -// getAddressValidator returns the nexus address validator. If not set, it returns a sealed empty validator -func (k Keeper) getAddressValidator() types.AddressValidator { - if k.addressValidator == nil { - k.SetAddressValidator(types.NewAddressValidator()) +func (k Keeper) getAddressValidator(module string) (exported.AddressValidator, error) { + if k.addressValidators == nil { + return nil, fmt.Errorf("address validator not set") } - return k.addressValidator + return k.addressValidators.GetAddressValidator(module) } func (k *Keeper) SetMessageRouter(router types.MessageRouter) { diff --git a/x/nexus/keeper/keeper_test.go b/x/nexus/keeper/keeper_test.go index 60e0e3d9a..0b1bc28bb 100644 --- a/x/nexus/keeper/keeper_test.go +++ b/x/nexus/keeper/keeper_test.go @@ -31,7 +31,7 @@ const maxAmount int64 = 100000000000 var k keeper.Keeper -func addressValidator() types.AddressValidator { +func addressValidators() *types.AddressValidators { axelarnetK := &axelarnetmock.BaseKeeperMock{ GetCosmosChainByNameFunc: func(ctx sdk.Context, chain exported.ChainName) (axelarnetTypes.CosmosChain, bool) { var prefix string @@ -47,18 +47,20 @@ func addressValidator() types.AddressValidator { }, } - router := types.NewAddressValidator() - router.AddAddressValidator(evmTypes.ModuleName, evmkeeper.NewAddressValidator()). + validators := types.NewAddressValidators() + validators.AddAddressValidator(evmTypes.ModuleName, evmkeeper.NewAddressValidator()). AddAddressValidator(axelarnetTypes.ModuleName, axelarnetkeeper.NewAddressValidator(axelarnetK)) - return router + validators.Seal() + + return validators } func init() { encCfg := app.MakeEncodingConfig() subspace := params.NewSubspace(encCfg.Codec, encCfg.Amino, sdk.NewKVStoreKey("nexusKey"), sdk.NewKVStoreKey("tNexusKey"), "nexus") k = keeper.NewKeeper(encCfg.Codec, sdk.NewKVStoreKey("nexus"), subspace) - k.SetAddressValidator(addressValidator()) + k.SetAddressValidators(addressValidators()) } func TestLinkAddress(t *testing.T) { diff --git a/x/nexus/keeper/transfer_test.go b/x/nexus/keeper/transfer_test.go index c76ebbc43..c48155133 100644 --- a/x/nexus/keeper/transfer_test.go +++ b/x/nexus/keeper/transfer_test.go @@ -442,7 +442,7 @@ func setup(cfg params.EncodingConfig) (nexusKeeper.Keeper, sdk.Context) { ctx := sdk.NewContext(fake.NewMultiStore(), tmproto.Header{}, false, log.TestingLogger()) k.SetParams(ctx, types.DefaultParams()) - k.SetAddressValidator(addressValidator()) + k.SetAddressValidators(addressValidators()) // register asset in ChainState for _, chain := range chains { diff --git a/x/nexus/types/address_validator.go b/x/nexus/types/address_validator.go index 40467c25b..cbe982910 100644 --- a/x/nexus/types/address_validator.go +++ b/x/nexus/types/address_validator.go @@ -6,36 +6,36 @@ import ( "github.com/axelarnetwork/axelar-core/x/nexus/exported" ) -// AddressValidator implements a AddressValidator based on module name. -type AddressValidator interface { - AddAddressValidator(module string, validator exported.AddressValidator) AddressValidator - HasAddressValidator(module string) bool - GetAddressValidator(module string) exported.AddressValidator - Seal() -} - -var _ AddressValidator = (*addressValidator)(nil) - -type addressValidator struct { +// AddressValidators collects all registered address validators by module +type AddressValidators struct { validators map[string]exported.AddressValidator sealed bool } -// NewAddressValidator creates a new AddressValidator interface instance -func NewAddressValidator() AddressValidator { - return &addressValidator{ +// NewAddressValidators returns a new AddressValidators instance +func NewAddressValidators() *AddressValidators { + return &AddressValidators{ validators: make(map[string]exported.AddressValidator), } } // Seal prevents additional validators from being added -func (r *addressValidator) Seal() { +func (r *AddressValidators) Seal() { + if r.sealed { + panic("cannot seal address validator (validator already sealed)") + } + r.sealed = true } +// IsSealed returns true if the validator is sealed +func (r *AddressValidators) IsSealed() bool { + return r.sealed +} + // AddAddressValidator registers a validator for a given path // panics if the validator is sealed, module is an empty string, or if the module has been registered already -func (r *addressValidator) AddAddressValidator(module string, validator exported.AddressValidator) AddressValidator { +func (r *AddressValidators) AddAddressValidator(module string, validator exported.AddressValidator) *AddressValidators { if r.sealed { panic("cannot add validator (validator sealed)") } @@ -44,7 +44,7 @@ func (r *addressValidator) AddAddressValidator(module string, validator exported panic("module name cannot be an empty string") } - if r.HasAddressValidator(module) { + if r.hasAddressValidator(module) { panic(fmt.Sprintf("validator for module %s has already been registered", module)) } @@ -52,16 +52,17 @@ func (r *addressValidator) AddAddressValidator(module string, validator exported return r } -// HasAddressValidator returns true if a validator is registered for the given module -func (r *addressValidator) HasAddressValidator(module string) bool { - return r.validators[module] != nil +// hasAddressValidator returns true if a validator is registered for the given module +func (r *AddressValidators) hasAddressValidator(module string) bool { + _, err := r.GetAddressValidator(module) + return err == nil } // GetAddressValidator returns a validator for a given module -func (r *addressValidator) GetAddressValidator(module string) exported.AddressValidator { - if !r.HasAddressValidator(module) { - panic(fmt.Sprintf("validator for module \"%s\" not registered", module)) +func (r *AddressValidators) GetAddressValidator(module string) (exported.AddressValidator, error) { + validate, ok := r.validators[module] + if !ok || validate == nil { + return nil, fmt.Errorf("validator for module \"%s\" not registered", module) } - - return r.validators[module] + return validate, nil }