Skip to content

Commit

Permalink
Merge PR #2614: Configurable Bech32 prefix for SDK users
Browse files Browse the repository at this point in the history
  • Loading branch information
svaishnavy authored and cwgoes committed Oct 31, 2018
1 parent fcf5b77 commit 9cf53f2
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 12 deletions.
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions cmd/gaia/cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions cmd/gaia/cmd/gaiad/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
7 changes: 7 additions & 0 deletions cmd/gaia/cmd/gaiadebug/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions examples/democoin/cmd/democli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
9 changes: 9 additions & 0 deletions examples/democoin/cmd/democoind/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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{
Expand Down
36 changes: 24 additions & 12 deletions types/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down
43 changes: 43 additions & 0 deletions types/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (

"github.com/tendermint/tendermint/crypto/ed25519"

"strings"

"github.com/cosmos/cosmos-sdk/types"
)

Expand Down Expand Up @@ -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"))
}

}
}
105 changes: 105 additions & 0 deletions types/config.go
Original file line number Diff line number Diff line change
@@ -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"]
}

0 comments on commit 9cf53f2

Please sign in to comment.