diff --git a/CHANGELOG.md b/CHANGELOG.md index 30cc4e23f3..68d9cb9260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ if the provided arguments are invalid. ### Client Breaking Changes +* (rest) [\#5270](https://github.com/cosmos/cosmos-sdk/issues/5270) All account types now implement custom JSON serialization. * (rest) [\#4783](https://github.com/cosmos/cosmos-sdk/issues/4783) The balance field in the DelegationResponse type is now sdk.Coin instead of sdk.Int * (x/auth) [\#5006](https://github.com/cosmos/cosmos-sdk/pull/5006) The gas required to pass the `AnteHandler` has increased significantly due to modular `AnteHandler` support. Increase GasLimit accordingly. diff --git a/x/auth/types/account.go b/x/auth/types/account.go index 063907ad6c..69dc5be447 100644 --- a/x/auth/types/account.go +++ b/x/auth/types/account.go @@ -2,8 +2,8 @@ package types import ( "bytes" + "encoding/json" "errors" - "fmt" "time" "github.com/tendermint/tendermint/crypto" @@ -44,24 +44,6 @@ func NewBaseAccount(address sdk.AccAddress, coins sdk.Coins, } } -// String implements fmt.Stringer -func (acc BaseAccount) String() string { - var pubkey string - - if acc.PubKey != nil { - pubkey = sdk.MustBech32ifyAccPub(acc.PubKey) - } - - return fmt.Sprintf(`Account: - Address: %s - Pubkey: %s - Coins: %s - AccountNumber: %d - Sequence: %d`, - acc.Address, pubkey, acc.Coins, acc.AccountNumber, acc.Sequence, - ) -} - // ProtoBaseAccount - a prototype function for BaseAccount func ProtoBaseAccount() exported.Account { return &BaseAccount{} @@ -138,39 +120,6 @@ func (acc *BaseAccount) SpendableCoins(_ time.Time) sdk.Coins { return acc.GetCoins() } -// MarshalYAML returns the YAML representation of an account. -func (acc BaseAccount) MarshalYAML() (interface{}, error) { - var bs []byte - var err error - var pubkey string - - if acc.PubKey != nil { - pubkey, err = sdk.Bech32ifyAccPub(acc.PubKey) - if err != nil { - return nil, err - } - } - - bs, err = yaml.Marshal(struct { - Address sdk.AccAddress - Coins sdk.Coins - PubKey string - AccountNumber uint64 - Sequence uint64 - }{ - Address: acc.Address, - Coins: acc.Coins, - PubKey: pubkey, - AccountNumber: acc.AccountNumber, - Sequence: acc.Sequence, - }) - if err != nil { - return nil, err - } - - return string(bs), err -} - // Validate checks for errors on the account fields func (acc BaseAccount) Validate() error { if acc.PubKey != nil && acc.Address != nil && @@ -180,3 +129,87 @@ func (acc BaseAccount) Validate() error { return nil } + +type baseAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` +} + +func (acc BaseAccount) String() string { + out, _ := acc.MarshalYAML() + return out.(string) +} + +// MarshalYAML returns the YAML representation of an account. +func (acc BaseAccount) MarshalYAML() (interface{}, error) { + alias := baseAccountPretty{ + Address: acc.Address, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, + } + + if acc.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(acc.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + bz, err := yaml.Marshal(alias) + if err != nil { + return nil, err + } + + return string(bz), err +} + +// MarshalJSON returns the JSON representation of a BaseAccount. +func (acc BaseAccount) MarshalJSON() ([]byte, error) { + alias := baseAccountPretty{ + Address: acc.Address, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, + } + + if acc.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(acc.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a BaseAccount. +func (acc *BaseAccount) UnmarshalJSON(bz []byte) error { + var alias baseAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + if alias.PubKey != "" { + pk, err := sdk.GetAccPubKeyBech32(alias.PubKey) + if err != nil { + return err + } + + acc.PubKey = pk + } + + acc.Address = alias.Address + acc.Coins = alias.Coins + acc.AccountNumber = alias.AccountNumber + acc.Sequence = alias.Sequence + + return nil +} diff --git a/x/auth/types/account_test.go b/x/auth/types/account_test.go index 14236172a8..171c15d721 100644 --- a/x/auth/types/account_test.go +++ b/x/auth/types/account_test.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "testing" @@ -128,3 +129,21 @@ func TestGenesisAccountValidate(t *testing.T) { }) } } + +func TestBaseAccountJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) + baseAcc := NewBaseAccount(addr, coins, pubkey, 10, 50) + + bz, err := json.Marshal(baseAcc) + require.NoError(t, err) + + bz1, err := baseAcc.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(bz1), string(bz)) + + var a BaseAccount + require.NoError(t, json.Unmarshal(bz, &a)) + require.Equal(t, baseAcc.String(), a.String()) +} diff --git a/x/auth/vesting/types/vesting_account.go b/x/auth/vesting/types/vesting_account.go index cda3b12ee5..da9343024f 100644 --- a/x/auth/vesting/types/vesting_account.go +++ b/x/auth/vesting/types/vesting_account.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "time" @@ -8,16 +9,18 @@ import ( authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" + + "github.com/tendermint/tendermint/crypto" "gopkg.in/yaml.v2" ) -// Assert BaseVestingAccount implements authexported.Account at compile-time -var _ authexported.Account = (*BaseVestingAccount)(nil) - -// Assert vesting accounts implement vestexported.VestingAccount at compile-time -var _ vestexported.VestingAccount = (*ContinuousVestingAccount)(nil) -var _ vestexported.VestingAccount = (*PeriodicVestingAccount)(nil) -var _ vestexported.VestingAccount = (*DelayedVestingAccount)(nil) +// Compile-time type assertions +var ( + _ authexported.Account = (*BaseVestingAccount)(nil) + _ vestexported.VestingAccount = (*ContinuousVestingAccount)(nil) + _ vestexported.VestingAccount = (*PeriodicVestingAccount)(nil) + _ vestexported.VestingAccount = (*DelayedVestingAccount)(nil) +) // Register the vesting account types on the auth module codec func init() { @@ -182,45 +185,108 @@ func (bva BaseVestingAccount) Validate() error { return bva.BaseAccount.Validate() } -// MarshalYAML returns the YAML representation of an account. +type vestingAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `json:"end_time" yaml:"end_time"` + + // custom fields based on concrete vesting type which can be omitted + StartTime int64 `json:"start_time,omitempty" yaml:"start_time,omitempty"` + VestingPeriods Periods `json:"vesting_periods,omitempty" yaml:"vesting_periods,omitempty"` +} + +func (bva BaseVestingAccount) String() string { + out, _ := bva.MarshalYAML() + return out.(string) +} + +// MarshalYAML returns the YAML representation of a BaseVestingAccount. func (bva BaseVestingAccount) MarshalYAML() (interface{}, error) { - var bs []byte - var err error - var pubkey string - - if bva.PubKey != nil { - pubkey, err = sdk.Bech32ifyAccPub(bva.PubKey) - if err != nil { - return nil, err - } - } - - bs, err = yaml.Marshal(struct { - Address sdk.AccAddress - Coins sdk.Coins - PubKey string - AccountNumber uint64 - Sequence uint64 - OriginalVesting sdk.Coins - DelegatedFree sdk.Coins - DelegatedVesting sdk.Coins - EndTime int64 - }{ + alias := vestingAccountPretty{ Address: bva.Address, Coins: bva.Coins, - PubKey: pubkey, AccountNumber: bva.AccountNumber, Sequence: bva.Sequence, OriginalVesting: bva.OriginalVesting, DelegatedFree: bva.DelegatedFree, DelegatedVesting: bva.DelegatedVesting, EndTime: bva.EndTime, - }) + } + + if bva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(bva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + bz, err := yaml.Marshal(alias) if err != nil { return nil, err } - return string(bs), err + return string(bz), err +} + +// MarshalJSON returns the JSON representation of a BaseVestingAccount. +func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: bva.Address, + Coins: bva.Coins, + AccountNumber: bva.AccountNumber, + Sequence: bva.Sequence, + OriginalVesting: bva.OriginalVesting, + DelegatedFree: bva.DelegatedFree, + DelegatedVesting: bva.DelegatedVesting, + EndTime: bva.EndTime, + } + + if bva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(bva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a BaseVestingAccount. +func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk crypto.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetAccPubKeyBech32(alias.PubKey) + if err != nil { + return err + } + } + + bva.BaseAccount = authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence) + bva.OriginalVesting = alias.OriginalVesting + bva.DelegatedFree = alias.DelegatedFree + bva.DelegatedVesting = alias.DelegatedVesting + bva.EndTime = alias.EndTime + + return nil } //----------------------------------------------------------------------------- @@ -320,34 +386,16 @@ func (cva ContinuousVestingAccount) Validate() error { return cva.BaseVestingAccount.Validate() } -// MarshalYAML returns the YAML representation of an account. +func (cva ContinuousVestingAccount) String() string { + out, _ := cva.MarshalYAML() + return out.(string) +} + +// MarshalYAML returns the YAML representation of a ContinuousVestingAccount. func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { - var bs []byte - var err error - var pubkey string - - if cva.PubKey != nil { - pubkey, err = sdk.Bech32ifyAccPub(cva.PubKey) - if err != nil { - return nil, err - } - } - - bs, err = yaml.Marshal(struct { - Address sdk.AccAddress - Coins sdk.Coins - PubKey string - AccountNumber uint64 - Sequence uint64 - OriginalVesting sdk.Coins - DelegatedFree sdk.Coins - DelegatedVesting sdk.Coins - EndTime int64 - StartTime int64 - }{ + alias := vestingAccountPretty{ Address: cva.Address, Coins: cva.Coins, - PubKey: pubkey, AccountNumber: cva.AccountNumber, Sequence: cva.Sequence, OriginalVesting: cva.OriginalVesting, @@ -355,12 +403,80 @@ func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { DelegatedVesting: cva.DelegatedVesting, EndTime: cva.EndTime, StartTime: cva.StartTime, - }) + } + + if cva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(cva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + bz, err := yaml.Marshal(alias) if err != nil { return nil, err } - return string(bs), err + return string(bz), err +} + +// MarshalJSON returns the JSON representation of a ContinuousVestingAccount. +func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: cva.Address, + Coins: cva.Coins, + AccountNumber: cva.AccountNumber, + Sequence: cva.Sequence, + OriginalVesting: cva.OriginalVesting, + DelegatedFree: cva.DelegatedFree, + DelegatedVesting: cva.DelegatedVesting, + EndTime: cva.EndTime, + StartTime: cva.StartTime, + } + + if cva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(cva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a ContinuousVestingAccount. +func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk crypto.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetAccPubKeyBech32(alias.PubKey) + if err != nil { + return err + } + } + + cva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + cva.StartTime = alias.StartTime + + return nil } //----------------------------------------------------------------------------- @@ -485,35 +601,16 @@ func (pva PeriodicVestingAccount) Validate() error { return pva.BaseVestingAccount.Validate() } -// MarshalYAML returns the YAML representation of an account. +func (pva PeriodicVestingAccount) String() string { + out, _ := pva.MarshalYAML() + return out.(string) +} + +// MarshalYAML returns the YAML representation of a PeriodicVestingAccount. func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { - var bs []byte - var err error - var pubkey string - - if pva.PubKey != nil { - pubkey, err = sdk.Bech32ifyAccPub(pva.PubKey) - if err != nil { - return nil, err - } - } - - bs, err = yaml.Marshal(struct { - Address sdk.AccAddress - Coins sdk.Coins - PubKey string - AccountNumber uint64 - Sequence uint64 - OriginalVesting sdk.Coins - DelegatedFree sdk.Coins - DelegatedVesting sdk.Coins - EndTime int64 - StartTime int64 - VestingPeriods Periods - }{ + alias := vestingAccountPretty{ Address: pva.Address, Coins: pva.Coins, - PubKey: pubkey, AccountNumber: pva.AccountNumber, Sequence: pva.Sequence, OriginalVesting: pva.OriginalVesting, @@ -522,12 +619,82 @@ func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { EndTime: pva.EndTime, StartTime: pva.StartTime, VestingPeriods: pva.VestingPeriods, - }) + } + + if pva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(pva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + bz, err := yaml.Marshal(alias) if err != nil { return nil, err } - return string(bs), err + return string(bz), err +} + +// MarshalJSON returns the JSON representation of a PeriodicVestingAccount. +func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: pva.Address, + Coins: pva.Coins, + AccountNumber: pva.AccountNumber, + Sequence: pva.Sequence, + OriginalVesting: pva.OriginalVesting, + DelegatedFree: pva.DelegatedFree, + DelegatedVesting: pva.DelegatedVesting, + EndTime: pva.EndTime, + StartTime: pva.StartTime, + VestingPeriods: pva.VestingPeriods, + } + + if pva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(pva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a PeriodicVestingAccount. +func (pva *PeriodicVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk crypto.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetAccPubKeyBech32(alias.PubKey) + if err != nil { + return err + } + } + + pva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + pva.StartTime = alias.StartTime + pva.VestingPeriods = alias.VestingPeriods + + return nil } //----------------------------------------------------------------------------- @@ -599,3 +766,58 @@ func (dva DelayedVestingAccount) GetStartTime() int64 { func (dva DelayedVestingAccount) Validate() error { return dva.BaseVestingAccount.Validate() } + +// MarshalJSON returns the JSON representation of a DelayedVestingAccount. +func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: dva.Address, + Coins: dva.Coins, + AccountNumber: dva.AccountNumber, + Sequence: dva.Sequence, + OriginalVesting: dva.OriginalVesting, + DelegatedFree: dva.DelegatedFree, + DelegatedVesting: dva.DelegatedVesting, + EndTime: dva.EndTime, + } + + if dva.PubKey != nil { + pks, err := sdk.Bech32ifyAccPub(dva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. +func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk crypto.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetAccPubKeyBech32(alias.PubKey) + if err != nil { + return err + } + } + + dva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + + return nil +} diff --git a/x/auth/vesting/types/vesting_account_test.go b/x/auth/vesting/types/vesting_account_test.go index 24c07f697e..8c87cbb9af 100644 --- a/x/auth/vesting/types/vesting_account_test.go +++ b/x/auth/vesting/types/vesting_account_test.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "testing" "time" @@ -732,3 +733,86 @@ func TestGenesisAccountValidate(t *testing.T) { }) } } + +func TestBaseVestingAccountJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) + baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + + acc, err := NewBaseVestingAccount(baseAcc, coins, time.Now().Unix()) + require.NoError(t, err) + + bz, err := json.Marshal(acc) + require.NoError(t, err) + + bz1, err := acc.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(bz1), string(bz)) + + var a BaseVestingAccount + require.NoError(t, json.Unmarshal(bz, &a)) + require.Equal(t, acc.String(), a.String()) +} + +func TestContinuousVestingAccountJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) + baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + + baseVesting, err := NewBaseVestingAccount(baseAcc, coins, time.Now().Unix()) + acc := NewContinuousVestingAccountRaw(baseVesting, baseVesting.EndTime) + require.NoError(t, err) + + bz, err := json.Marshal(acc) + require.NoError(t, err) + + bz1, err := acc.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(bz1), string(bz)) + + var a ContinuousVestingAccount + require.NoError(t, json.Unmarshal(bz, &a)) + require.Equal(t, acc.String(), a.String()) +} + +func TestPeriodicVestingAccountJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) + baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + + acc := NewPeriodicVestingAccount(baseAcc, time.Now().Unix(), Periods{Period{3600, coins}}) + + bz, err := json.Marshal(acc) + require.NoError(t, err) + + bz1, err := acc.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(bz1), string(bz)) + + var a PeriodicVestingAccount + require.NoError(t, json.Unmarshal(bz, &a)) + require.Equal(t, acc.String(), a.String()) +} + +func TestDelayedVestingAccountJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) + baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + + acc := NewDelayedVestingAccount(baseAcc, time.Now().Unix()) + + bz, err := json.Marshal(acc) + require.NoError(t, err) + + bz1, err := acc.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(bz1), string(bz)) + + var a DelayedVestingAccount + require.NoError(t, json.Unmarshal(bz, &a)) + require.Equal(t, acc.String(), a.String()) +} diff --git a/x/supply/internal/types/account.go b/x/supply/internal/types/account.go index 7ab4546913..ac2136ec69 100644 --- a/x/supply/internal/types/account.go +++ b/x/supply/internal/types/account.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "fmt" "strings" @@ -30,6 +31,7 @@ func init() { // ModuleAccount defines an account for modules that holds coins on a pool type ModuleAccount struct { *authtypes.BaseAccount + Name string `json:"name" yaml:"name"` // name of the module Permissions []string `json:"permissions" yaml:"permissions"` // permissions of module account } @@ -100,15 +102,6 @@ func (ma ModuleAccount) SetSequence(seq uint64) error { return fmt.Errorf("not supported for module accounts") } -// String follows stringer interface -func (ma ModuleAccount) String() string { - b, err := yaml.Marshal(ma) - if err != nil { - panic(err) - } - return string(b) -} - // Validate checks for errors on the account fields func (ma ModuleAccount) Validate() error { if strings.TrimSpace(ma.Name) == "" { @@ -121,17 +114,24 @@ func (ma ModuleAccount) Validate() error { return ma.BaseAccount.Validate() } +type moduleAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` +} + +func (ma ModuleAccount) String() string { + out, _ := ma.MarshalYAML() + return out.(string) +} + // MarshalYAML returns the YAML representation of a ModuleAccount. func (ma ModuleAccount) MarshalYAML() (interface{}, error) { - bs, err := yaml.Marshal(struct { - Address sdk.AccAddress - Coins sdk.Coins - PubKey string - AccountNumber uint64 - Sequence uint64 - Name string - Permissions []string - }{ + bs, err := yaml.Marshal(moduleAccountPretty{ Address: ma.Address, Coins: ma.Coins, PubKey: "", @@ -147,3 +147,30 @@ func (ma ModuleAccount) MarshalYAML() (interface{}, error) { return string(bs), nil } + +// MarshalJSON returns the JSON representation of a ModuleAccount. +func (ma ModuleAccount) MarshalJSON() ([]byte, error) { + return json.Marshal(moduleAccountPretty{ + Address: ma.Address, + Coins: ma.Coins, + PubKey: "", + AccountNumber: ma.AccountNumber, + Sequence: ma.Sequence, + Name: ma.Name, + Permissions: ma.Permissions, + }) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. +func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { + var alias moduleAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + ma.BaseAccount = authtypes.NewBaseAccount(alias.Address, alias.Coins, nil, alias.AccountNumber, alias.Sequence) + ma.Name = alias.Name + ma.Permissions = alias.Permissions + + return nil +} diff --git a/x/supply/internal/types/account_test.go b/x/supply/internal/types/account_test.go index 853049a800..396f1a90be 100644 --- a/x/supply/internal/types/account_test.go +++ b/x/supply/internal/types/account_test.go @@ -1,13 +1,13 @@ package types import ( + "encoding/json" "errors" "fmt" "testing" yaml "gopkg.in/yaml.v2" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" @@ -20,25 +20,11 @@ import ( func TestModuleAccountMarshalYAML(t *testing.T) { name := "test" moduleAcc := NewEmptyModuleAccount(name, Minter, Burner, Staking) - moduleAddress := sdk.AccAddress(crypto.AddressHash([]byte(name))) bs, err := yaml.Marshal(moduleAcc) require.NoError(t, err) - want := fmt.Sprintf(`| - address: %s - coins: [] - pubkey: "" - accountnumber: 0 - sequence: 0 - name: %s - permissions: - - %s - - %s - - %s -`, moduleAddress, name, Minter, Burner, Staking) - + want := "|\n address: cosmos1n7rdpqvgf37ktx30a2sv2kkszk3m7ncmg5drhe\n coins: []\n public_key: \"\"\n account_number: 0\n sequence: 0\n name: test\n permissions:\n - minter\n - burner\n - staking\n" require.Equal(t, want, string(bs)) - require.Equal(t, want, moduleAcc.String()) } func TestHasPermissions(t *testing.T) { @@ -96,3 +82,22 @@ func TestValidate(t *testing.T) { }) } } + +func TestModuleAccountJSON(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) + baseAcc := authtypes.NewBaseAccount(addr, coins, nil, 10, 50) + acc := NewModuleAccount(baseAcc, "test", "burner") + + bz, err := json.Marshal(acc) + require.NoError(t, err) + + bz1, err := acc.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(bz1), string(bz)) + + var a ModuleAccount + require.NoError(t, json.Unmarshal(bz, &a)) + require.Equal(t, acc.String(), a.String()) +}