From 9cf53f25f7bfff9454aa2728e05d8eccbb386aa6 Mon Sep 17 00:00:00 2001 From: svaishnavy Date: Wed, 31 Oct 2018 20:13:13 +0100 Subject: [PATCH] Merge PR #2614: Configurable Bech32 prefix for SDK users --- PENDING.md | 1 + cmd/gaia/cmd/gaiacli/main.go | 7 ++ cmd/gaia/cmd/gaiad/main.go | 8 ++ cmd/gaia/cmd/gaiadebug/main.go | 7 ++ examples/democoin/cmd/democli/main.go | 9 ++ examples/democoin/cmd/democoind/main.go | 9 ++ types/address.go | 36 +++++--- types/address_test.go | 43 ++++++++++ types/config.go | 105 ++++++++++++++++++++++++ 9 files changed, 213 insertions(+), 12 deletions(-) create mode 100644 types/config.go diff --git a/PENDING.md b/PENDING.md index ff6196848b9c..8a16136c0965 100644 --- a/PENDING.md +++ b/PENDING.md @@ -26,6 +26,7 @@ FEATURES * Gaia * SDK + * (#1336) Mechanism for SDK Users to configure their own Bech32 prefixes instead of using the default cosmos prefixes. * Tendermint diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 6f11e68a5acd..212a67021404 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -15,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" @@ -47,6 +48,12 @@ func main() { cobra.EnableCommandSorting = false cdc := app.MakeCodec() + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 1304b32f3486..ed777bf478b2 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -18,10 +18,18 @@ import ( "github.com/cosmos/cosmos-sdk/cmd/gaia/app" gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" ) func main() { cdc := app.MakeCodec() + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + ctx := server.NewDefaultContext() cobra.EnableCommandSorting = false rootCmd := &cobra.Command{ diff --git a/cmd/gaia/cmd/gaiadebug/main.go b/cmd/gaia/cmd/gaiadebug/main.go index 73840537b2bc..96887376cd61 100644 --- a/cmd/gaia/cmd/gaiadebug/main.go +++ b/cmd/gaia/cmd/gaiadebug/main.go @@ -19,6 +19,13 @@ import ( ) func init() { + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + rootCmd.AddCommand(txCmd) rootCmd.AddCommand(pubkeyCmd) rootCmd.AddCommand(addrCmd) diff --git a/examples/democoin/cmd/democli/main.go b/examples/democoin/cmd/democli/main.go index 08f131168710..6bb2da868083 100644 --- a/examples/democoin/cmd/democli/main.go +++ b/examples/democoin/cmd/democli/main.go @@ -21,6 +21,8 @@ import ( coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/client/cli" powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/client/cli" simplestakingcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake/client/cli" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // rootCmd is the entry point for this binary @@ -38,6 +40,13 @@ func main() { // get the codec cdc := app.MakeCodec() + // Setup certain SDK config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("demoacc", "demopub") + config.SetBech32PrefixForValidator("demoval", "demovalpub") + config.SetBech32PrefixForConsensusNode("democons", "democonspub") + config.Seal() + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 692564b88e3f..b0256b1749dc 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -23,6 +23,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/app" "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -135,6 +136,14 @@ func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (j func main() { cdc := app.MakeCodec() + + // Setup certain SDK config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("demoacc", "demopub") + config.SetBech32PrefixForValidator("demoval", "demovalpub") + config.SetBech32PrefixForConsensusNode("democons", "democonspub") + config.Seal() + ctx := server.NewDefaultContext() rootCmd := &cobra.Command{ diff --git a/types/address.go b/types/address.go index ae13b2ad0afc..36eb5ac32bc5 100644 --- a/types/address.go +++ b/types/address.go @@ -55,7 +55,8 @@ func AccAddressFromHex(address string) (addr AccAddress, err error) { // AccAddressFromBech32 creates an AccAddress from a Bech32 string. func AccAddressFromBech32(address string) (addr AccAddress, err error) { - bz, err := GetFromBech32(address, Bech32PrefixAccAddr) + bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix() + bz, err := GetFromBech32(address, bech32PrefixAccAddr) if err != nil { return nil, err } @@ -124,7 +125,8 @@ func (aa AccAddress) Bytes() []byte { // String implements the Stringer interface. func (aa AccAddress) String() string { - bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixAccAddr, aa.Bytes()) + bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix() + bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixAccAddr, aa.Bytes()) if err != nil { panic(err) } @@ -169,7 +171,8 @@ func ValAddressFromHex(address string) (addr ValAddress, err error) { // ValAddressFromBech32 creates a ValAddress from a Bech32 string. func ValAddressFromBech32(address string) (addr ValAddress, err error) { - bz, err := GetFromBech32(address, Bech32PrefixValAddr) + bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix() + bz, err := GetFromBech32(address, bech32PrefixValAddr) if err != nil { return nil, err } @@ -239,7 +242,8 @@ func (va ValAddress) Bytes() []byte { // String implements the Stringer interface. func (va ValAddress) String() string { - bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixValAddr, va.Bytes()) + bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix() + bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixValAddr, va.Bytes()) if err != nil { panic(err) } @@ -284,7 +288,8 @@ func ConsAddressFromHex(address string) (addr ConsAddress, err error) { // ConsAddressFromBech32 creates a ConsAddress from a Bech32 string. func ConsAddressFromBech32(address string) (addr ConsAddress, err error) { - bz, err := GetFromBech32(address, Bech32PrefixConsAddr) + bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix() + bz, err := GetFromBech32(address, bech32PrefixConsAddr) if err != nil { return nil, err } @@ -359,7 +364,8 @@ func (ca ConsAddress) Bytes() []byte { // String implements the Stringer interface. func (ca ConsAddress) String() string { - bech32Addr, err := bech32.ConvertAndEncode(Bech32PrefixConsAddr, ca.Bytes()) + bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix() + bech32Addr, err := bech32.ConvertAndEncode(bech32PrefixConsAddr, ca.Bytes()) if err != nil { panic(err) } @@ -387,7 +393,8 @@ func (ca ConsAddress) Format(s fmt.State, verb rune) { // Bech32ifyAccPub returns a Bech32 encoded string containing the // Bech32PrefixAccPub prefix for a given account PubKey. func Bech32ifyAccPub(pub crypto.PubKey) (string, error) { - return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes()) + bech32PrefixAccPub := GetConfig().GetBech32AccountPubPrefix() + return bech32.ConvertAndEncode(bech32PrefixAccPub, pub.Bytes()) } // MustBech32ifyAccPub returns the result of Bech32ifyAccPub panicing on failure. @@ -403,7 +410,8 @@ func MustBech32ifyAccPub(pub crypto.PubKey) string { // Bech32ifyValPub returns a Bech32 encoded string containing the // Bech32PrefixValPub prefix for a given validator operator's PubKey. func Bech32ifyValPub(pub crypto.PubKey) (string, error) { - return bech32.ConvertAndEncode(Bech32PrefixValPub, pub.Bytes()) + bech32PrefixValPub := GetConfig().GetBech32ValidatorPubPrefix() + return bech32.ConvertAndEncode(bech32PrefixValPub, pub.Bytes()) } // MustBech32ifyValPub returns the result of Bech32ifyValPub panicing on failure. @@ -419,7 +427,8 @@ func MustBech32ifyValPub(pub crypto.PubKey) string { // Bech32ifyConsPub returns a Bech32 encoded string containing the // Bech32PrefixConsPub prefixfor a given consensus node's PubKey. func Bech32ifyConsPub(pub crypto.PubKey) (string, error) { - return bech32.ConvertAndEncode(Bech32PrefixConsPub, pub.Bytes()) + bech32PrefixConsPub := GetConfig().GetBech32ConsensusPubPrefix() + return bech32.ConvertAndEncode(bech32PrefixConsPub, pub.Bytes()) } // MustBech32ifyConsPub returns the result of Bech32ifyConsPub panicing on @@ -436,7 +445,8 @@ func MustBech32ifyConsPub(pub crypto.PubKey) string { // GetAccPubKeyBech32 creates a PubKey for an account with a given public key // string using the Bech32 Bech32PrefixAccPub prefix. func GetAccPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := GetFromBech32(pubkey, Bech32PrefixAccPub) + bech32PrefixAccPub := GetConfig().GetBech32AccountPubPrefix() + bz, err := GetFromBech32(pubkey, bech32PrefixAccPub) if err != nil { return nil, err } @@ -463,7 +473,8 @@ func MustGetAccPubKeyBech32(pubkey string) (pk crypto.PubKey) { // GetValPubKeyBech32 creates a PubKey for a validator's operator with a given // public key string using the Bech32 Bech32PrefixValPub prefix. func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := GetFromBech32(pubkey, Bech32PrefixValPub) + bech32PrefixValPub := GetConfig().GetBech32ValidatorPubPrefix() + bz, err := GetFromBech32(pubkey, bech32PrefixValPub) if err != nil { return nil, err } @@ -490,7 +501,8 @@ func MustGetValPubKeyBech32(pubkey string) (pk crypto.PubKey) { // GetConsPubKeyBech32 creates a PubKey for a consensus node with a given public // key string using the Bech32 Bech32PrefixConsPub prefix. func GetConsPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) { - bz, err := GetFromBech32(pubkey, Bech32PrefixConsPub) + bech32PrefixConsPub := GetConfig().GetBech32ConsensusPubPrefix() + bz, err := GetFromBech32(pubkey, bech32PrefixConsPub) if err != nil { return nil, err } diff --git a/types/address_test.go b/types/address_test.go index e2ec36876cd5..51c44a12cd60 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -9,6 +9,8 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" + "strings" + "github.com/cosmos/cosmos-sdk/types" ) @@ -178,3 +180,44 @@ func TestConsAddress(t *testing.T) { require.NotNil(t, err) } } + +const letterBytes = "abcdefghijklmnopqrstuvwxyz" + +func RandString(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return string(b) +} + +func TestConfiguredPrefix(t *testing.T) { + var pub ed25519.PubKeyEd25519 + for length := 1; length < 10; length++ { + for times := 1; times < 20; times++ { + rand.Read(pub[:]) + // Test if randomly generated prefix of a given length works + prefix := RandString(length) + // Assuming that GetConfig is not sealed. + config := types.GetConfig() + config.SetBech32PrefixForAccount(prefix+"acc", prefix+"pub") + acc := types.AccAddress(pub.Address()) + require.True(t, strings.HasPrefix(acc.String(), prefix+"acc")) + bech32Pub := types.MustBech32ifyAccPub(pub) + require.True(t, strings.HasPrefix(bech32Pub, prefix+"pub")) + + config.SetBech32PrefixForValidator(prefix+"valaddr", prefix+"valpub") + val := types.ValAddress(pub.Address()) + require.True(t, strings.HasPrefix(val.String(), prefix+"valaddr")) + bech32ValPub := types.MustBech32ifyValPub(pub) + require.True(t, strings.HasPrefix(bech32ValPub, prefix+"valpub")) + + config.SetBech32PrefixForConsensusNode(prefix+"consaddr", prefix+"conspub") + cons := types.ConsAddress(pub.Address()) + require.True(t, strings.HasPrefix(cons.String(), prefix+"consaddr")) + bech32ConsPub := types.MustBech32ifyConsPub(pub) + require.True(t, strings.HasPrefix(bech32ConsPub, prefix+"conspub")) + } + + } +} diff --git a/types/config.go b/types/config.go new file mode 100644 index 000000000000..da1a387fe17b --- /dev/null +++ b/types/config.go @@ -0,0 +1,105 @@ +package types + +import ( + "sync" +) + +// Config is the structure that holds the SDK configuration parameters. +// This could be used to initialize certain configuration parameters for the SDK. +type Config struct { + mtx sync.RWMutex + sealed bool + bech32AddressPrefix map[string]string +} + +var ( + // Initializing an instance of Config + sdkConfig = &Config{ + sealed: false, + bech32AddressPrefix: map[string]string{ + "account_addr": Bech32PrefixAccAddr, + "validator_addr": Bech32PrefixValAddr, + "consensus_addr": Bech32PrefixConsAddr, + "account_pub": Bech32PrefixAccPub, + "validator_pub": Bech32PrefixValPub, + "consensus_pub": Bech32PrefixConsPub, + }, + } +) + +// GetConfig returns the config instance for the SDK. +func GetConfig() *Config { + return sdkConfig +} + +func (config *Config) assertNotSealed() { + config.mtx.Lock() + defer config.mtx.Unlock() + + if config.sealed { + panic("Config is sealed") + } +} + +// SetBech32PrefixForAccount builds the Config with Bech32 addressPrefix and publKeyPrefix for accounts +// and returns the config instance +func (config *Config) SetBech32PrefixForAccount(addressPrefix, pubKeyPrefix string) { + config.assertNotSealed() + config.bech32AddressPrefix["account_addr"] = addressPrefix + config.bech32AddressPrefix["account_pub"] = pubKeyPrefix +} + +// SetBech32PrefixForValidator builds the Config with Bech32 addressPrefix and publKeyPrefix for validators +// and returns the config instance +func (config *Config) SetBech32PrefixForValidator(addressPrefix, pubKeyPrefix string) { + config.assertNotSealed() + config.bech32AddressPrefix["validator_addr"] = addressPrefix + config.bech32AddressPrefix["validator_pub"] = pubKeyPrefix +} + +// SetBech32PrefixForConsensusNode builds the Config with Bech32 addressPrefix and publKeyPrefix for consensus nodes +// and returns the config instance +func (config *Config) SetBech32PrefixForConsensusNode(addressPrefix, pubKeyPrefix string) { + config.assertNotSealed() + config.bech32AddressPrefix["consensus_addr"] = addressPrefix + config.bech32AddressPrefix["consensus_pub"] = pubKeyPrefix +} + +// Seal seals the config such that the config state could not be modified further +func (config *Config) Seal() *Config { + config.mtx.Lock() + defer config.mtx.Unlock() + + config.sealed = true + return config +} + +// GetBech32AccountAddrPrefix returns the Bech32 prefix for account address +func (config *Config) GetBech32AccountAddrPrefix() string { + return config.bech32AddressPrefix["account_addr"] +} + +// GetBech32ValidatorAddrPrefix returns the Bech32 prefix for validator address +func (config *Config) GetBech32ValidatorAddrPrefix() string { + return config.bech32AddressPrefix["validator_addr"] +} + +// GetBech32ConsensusAddrPrefix returns the Bech32 prefix for consensus node address +func (config *Config) GetBech32ConsensusAddrPrefix() string { + return config.bech32AddressPrefix["consensus_addr"] +} + +// GetBech32AccountPubPrefix returns the Bech32 prefix for account public key +func (config *Config) GetBech32AccountPubPrefix() string { + return config.bech32AddressPrefix["account_pub"] +} + +// GetBech32ValidatorPubPrefix returns the Bech32 prefix for validator public key +func (config *Config) GetBech32ValidatorPubPrefix() string { + return config.bech32AddressPrefix["validator_pub"] +} + +// GetBech32ConsensusPubPrefix returns the Bech32 prefix for consensus node public key +func (config *Config) GetBech32ConsensusPubPrefix() string { + return config.bech32AddressPrefix["consensus_pub"] +}