chore: simplify ADR-028 and address.Module (backport #14017) (#14632)

Co-authored-by: Robert Zaremba <robert@zaremba.ch>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2023-01-16 10:07:58 -05:00 committed by GitHub
parent 1a23b7c324
commit 8df686ea06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 177 additions and 92 deletions

View File

@ -37,6 +37,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased] ## [Unreleased]
## Improvements
* [#14017](https://github.com/cosmos/cosmos-sdk/pull/14017) Simplify ADR-028 and `address.Module`.
* This updates the [ADR-028](https://docs.cosmos.network/main/architecture/adr-028-public-key-addresses) and enhance the `address.Module` API to support module addresses and sub-module addresses in a backward compatible way.
## State Machine Breaking ## State Machine Breaking
* (baseapp, x/auth/posthandler) [#13940](https://github.com/cosmos/cosmos-sdk/pull/13940) Update `PostHandler` to receive the `runTx` success boolean. * (baseapp, x/auth/posthandler) [#13940](https://github.com/cosmos/cosmos-sdk/pull/13940) Update `PostHandler` to receive the `runTx` success boolean.

View File

@ -97,7 +97,7 @@ As in other parts of the Cosmos SDK, we will use `sha256`.
### Basic Address ### Basic Address
We start with defining a base hash algorithm for generating addresses. Notably, it's used for accounts represented by a single key pair. For each public key schema we have to have an associated `typ` string, which we discuss in a section below. `hash` is the cryptographic hash function defined in the previous section. We start with defining a base algorithm for generating addresses which we will call `Hash`. Notably, it's used for accounts represented by a single key pair. For each public key schema we have to have an associated `typ` string, explained in the next section. `hash` is the cryptographic hash function defined in the previous section.
```go ```go
const A_LEN = 32 const A_LEN = 32
@ -114,16 +114,13 @@ Motivation: this algorithm keeps the address relatively small (length of the `ty
and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 bytes of a pubkey hash, significantly reducing the address space). and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 bytes of a pubkey hash, significantly reducing the address space).
Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack. Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack.
We use the `address.Hash` function for generating addresses for all accounts represented by a single key: `address.Hash` is a low level function to generate _base_ addresses for new key types. Example:
* simple public keys: `address.Hash(keyType, pubkey)` * BLS: `address.Hash("bls", pubkey)`
* aggregated keys (eg: BLS): `address.Hash(keyType, aggregatedPubKey)`
* modules: `address.Hash("module", moduleName)`
### Composed Addresses ### Composed Addresses
For simple composed accounts (like new naive multisig), we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address. For simple composed accounts (like a new naive multisig) we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address.
```go ```go
// We don't need a PubKey interface - we need anything which is addressable. // We don't need a PubKey interface - we need anything which is addressable.
@ -140,13 +137,13 @@ func Composed(typ string, subaccounts []Addressable) []byte {
The `typ` parameter should be a schema descriptor, containing all significant attributes with deterministic serialization (eg: utf8 string). The `typ` parameter should be a schema descriptor, containing all significant attributes with deterministic serialization (eg: utf8 string).
`LengthPrefix` is a function which prepends 1 byte to the address. The value of that byte is the length of the address bits before prepending. The address must be at most 255 bits long. `LengthPrefix` is a function which prepends 1 byte to the address. The value of that byte is the length of the address bits before prepending. The address must be at most 255 bits long.
We are using `LengthPrefix` to eliminate conflicts - it assures, that for 2 lists of addresses: `as = {a1, a2, ..., an}` and `bs = {b1, b2, ..., bm}` such that every `bi` and `ai` is at most 255 long, `concatenate(map(as, \a -> LengthPrefix(a))) = map(bs, \b -> LengthPrefix(b))` iff `as = bs`. We are using `LengthPrefix` to eliminate conflicts - it assures, that for 2 lists of addresses: `as = {a1, a2, ..., an}` and `bs = {b1, b2, ..., bm}` such that every `bi` and `ai` is at most 255 long, `concatenate(map(as, (a) => LengthPrefix(a))) = map(bs, (b) => LengthPrefix(b))` if `as = bs`.
Implementation Tip: account implementations should cache addresses. Implementation Tip: account implementations should cache addresses.
#### Multisig Addresses #### Multisig Addresses
For new multisig public keys, we define the `typ` parameter not based on any encoding scheme (amino or protobuf). This avoids issues with non-determinism in the encoding scheme. For a new multisig public keys, we define the `typ` parameter not based on any encoding scheme (amino or protobuf). This avoids issues with non-determinism in the encoding scheme.
Example: Example:
@ -161,40 +158,63 @@ message PubKey {
```go ```go
func (multisig PubKey) Address() { func (multisig PubKey) Address() {
// first gather all nested pub keys // first gather all nested pub keys
var keys []address.Addressable // cryptotypes.PubKey implements Addressable var keys []address.Addressable // cryptotypes.PubKey implements Addressable
for _, _key := range multisig.Pubkeys { for _, _key := range multisig.Pubkeys {
keys = append(keys, key.GetCachedValue().(cryptotypes.PubKey)) keys = append(keys, key.GetCachedValue().(cryptotypes.PubKey))
} }
// form the type from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together // form the type from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together
prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold) prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold)
// use the Composed function defined above // use the Composed function defined above
return address.Composed(prefix, keys) return address.Composed(prefix, keys)
} }
``` ```
#### Module Account Addresses
NOTE: this section is not finalize and it's in active discussion. ### Derived Addresses
In Basic Address section we defined a module account address as: We must be able to cryptographically derive one address from another one. The derivation process must guarantee hash properties, hence we use the already defined `Hash` function:
```go ```go
address.Hash("module", moduleName) func Derive(address, derivationKey []byte) []byte {
``` return Hash(addres, derivationKey)
We use `"module"` as a schema type for all module derived addresses. Module accounts can have sub accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key.
Module account addresses are heavily used in the Cosmos SDK so it makes sense to optimize the derivation process: instead of using of using `LengthPrefix` for the module name, we use a null byte (`'\x00'`) as a separator. This works, because null byte is not a part of a valid module name.
```go
func Module(moduleName string, key []byte) []byte{
return Hash("module", []byte(moduleName) + 0 + key)
} }
``` ```
**Example** A lending BTC pool address would be: ### Module Account Addresses
A module account will have `"module"` type. Module accounts can have sub accounts. The submodule account will be created based on module name, and sequence of derivation keys. Typically, the first derivation key should be a class of the derived accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key... An example module account is created using:
```go
address.Module(moduleName, key)
```
An example sub-module account is created using:
```go
groupPolicyAddresses := []byte{1}
address.Module(moduleName, groupPolicyAddresses, policyID)
```
The `address.Module` function is using `address.Hash` with `"module"` as the type argument, and byte representation of the module name concatenated with submodule key. The two last component must be uniquely separated to avoid potential clashes (example: modulename="ab" & submodulekey="bc" will have the same derivation key as modulename="a" & submodulekey="bbc").
We use a null byte (`'\x00'`) to separate module name from the submodule key. This works, because null byte is not a part of a valid module name. Finally, the sub-submodule accounts are created by applying the `Derive` function recursively.
We could use `Derive` function also in the first step (rather than concatenating module name with zero byte and the submodule key). We decided to do concatenation to avoid one level of derivation and speed up computation.
For backward compatibility with the existing `authtypes.NewModuleAddress`, we add a special case in `Module` function: when no derivation key is provided, we fallback to the "legacy" implementation.
```go
func Module(moduleName string, derivationKeys ...[]byte) []byte{
if len(derivationKeys) == 0 {
return authtypes.NewModuleAddress(modulenName) // legacy case
}
submoduleAddress := Hash("module", []byte(moduleName) + 0 + key)
return fold((a, k) => Derive(a, k), subsubKeys, submoduleAddress)
}
```
**Example 1** A lending BTC pool address would be:
```go ```go
btcPool := address.Module("lending", btc.Address()}) btcPool := address.Module("lending", btc.Address()})
@ -206,22 +226,15 @@ If we want to create an address for a module account depending on more than one
btcAtomAMM := address.Module("amm", btc.Address() + atom.Address()}) btcAtomAMM := address.Module("amm", btc.Address() + atom.Address()})
``` ```
#### Derived Addresses **Example 2** a smart-contract address could be constructed by:
We must be able to cryptographically derive one address from another one. The derivation process must guarantee hash properties, hence we use the already defined `Hash` function:
```go ```go
func Derive(address []byte, derivationKey []byte) []byte { smartContractAddr = Module("mySmartContractVM", smartContractsNamespace, smartContractKey})
return Hash(addres, derivationKey)
}
```
Note: `Module` is a special case of the more general _derived_ address, where we set the `"module"` string for the _from address_. // which equals to:
smartContractAddr = Derived(
**Example** For a cosmwasm smart-contract address we could use the following construction: Module("mySmartContractVM", smartContractsNamespace),
[]{smartContractKey})
```go
smartContractAddr := Derived(Module("cosmwasm", smartContractsNamespace), []{smartContractKey})
``` ```
### Schema Types ### Schema Types
@ -235,7 +248,7 @@ Example: all public key types have a unique protobuf message type similar to:
package cosmos.crypto.sr25519; package cosmos.crypto.sr25519;
message PubKey { message PubKey {
bytes key = 1; bytes key = 1;
} }
``` ```

View File

@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"sort" "sort"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/internal/conv" "github.com/cosmos/cosmos-sdk/internal/conv"
"github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/errors"
) )
@ -18,7 +20,9 @@ type Addressable interface {
Address() []byte Address() []byte
} }
// Hash creates a new address from address type and key // Hash creates a new address from address type and key.
// The functions should only be used by new types defining their own address function
// (eg public keys).
func Hash(typ string, key []byte) []byte { func Hash(typ string, key []byte) []byte {
hasher := sha256.New() hasher := sha256.New()
_, err := hasher.Write(conv.UnsafeStrToBytes(typ)) _, err := hasher.Write(conv.UnsafeStrToBytes(typ))
@ -59,13 +63,30 @@ func Compose(typ string, subAddresses []Addressable) ([]byte, error) {
} }
// Module is a specialized version of a composed address for modules. Each module account // Module is a specialized version of a composed address for modules. Each module account
// is constructed from a module name and module account key. // is constructed from a module name and a sequence of derivation keys (at least one
func Module(moduleName string, key []byte) []byte { // derivation key must be provided). The derivation keys must be unique
mKey := append([]byte(moduleName), 0) // in the module scope, and is usually constructed from some object id. Example, let's
return Hash("module", append(mKey, key...)) // a x/dao module, and a new DAO object, it's address would be:
//
// address.Module(dao.ModuleName, newDAO.ID)
func Module(moduleName string, derivationKeys ...[]byte) []byte {
mKey := []byte(moduleName)
if len(derivationKeys) == 0 { // fallback to the "traditional" ModuleAddress
return crypto.AddressHash(mKey)
}
// need to append zero byte to avoid potential clash between the module name and the first
// derivation key
mKey = append(mKey, 0)
addr := Hash("module", append(mKey, derivationKeys[0]...))
for _, k := range derivationKeys[1:] {
addr = Derive(addr, k)
}
return addr
} }
// Derive derives a new address from the main `address` and a derivation `key`. // Derive derives a new address from the main `address` and a derivation `key`.
// This function is used to create a sub accounts. To create a module accounts use the
// `Module` function.
func Derive(address []byte, key []byte) []byte { func Derive(address []byte, key []byte) []byte {
return Hash(conv.UnsafeBytesToStr(address), key) return Hash(conv.UnsafeBytesToStr(address), key)
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/crypto/tmhash"
) )
func TestAddressSuite(t *testing.T) { func TestAddressSuite(t *testing.T) {
@ -64,15 +65,30 @@ func (suite *AddressSuite) TestComposed() {
func (suite *AddressSuite) TestModule() { func (suite *AddressSuite) TestModule() {
assert := suite.Assert() assert := suite.Assert()
modName, key := "myModule", []byte{1, 2} modName, key := "myModule", []byte{1, 2}
addrLegacy := Module(modName)
assert.Equal(tmhash.SumTruncated([]byte(modName)), addrLegacy,
"when no derivation keys, we fall back to the legacy module address using sha256 of the module name")
addr := Module(modName, key) addr := Module(modName, key)
assert.Len(addr, Len, "must have address length") assert.Len(addr, Len, "must have correct address length")
assert.NotEqual(addrLegacy, addr,
"when derivation key is specified, it must generate non legacy module address")
addr2 := Module("myModule2", key) addr2 := Module("myModule2", key)
assert.NotEqual(addr, addr2, "changing module name must change address") assert.NotEqual(addr, addr2, "changing module name must change address")
addr3 := Module(modName, []byte{1, 2, 3}) k1 := []byte{1, 2, 3}
addr3 := Module(modName, k1)
assert.NotEqual(addr, addr3, "changing key must change address") assert.NotEqual(addr, addr3, "changing key must change address")
assert.NotEqual(addr2, addr3, "changing key must change address") assert.NotEqual(addr2, addr3, "changing key must change address")
addr4 := Module(modName, k1, k1)
assert.Equal(Derive(addr3, k1), addr4)
k2 := []byte{0, 0, 7}
addr5 := Module(modName, k1, k1, k2)
assert.Equal(Derive(addr4, k2), addr5)
} }
func (suite *AddressSuite) TestDerive() { func (suite *AddressSuite) TestDerive() {

View File

@ -16,6 +16,7 @@ import (
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
) )
var ( var (
@ -172,7 +173,7 @@ func NewModuleAddressOrBech32Address(input string) sdk.AccAddress {
// NewModuleAddress creates an AccAddress from the hash of the module's name // NewModuleAddress creates an AccAddress from the hash of the module's name
func NewModuleAddress(name string) sdk.AccAddress { func NewModuleAddress(name string) sdk.AccAddress {
return sdk.AccAddress(crypto.AddressHash([]byte(name))) return address.Module(name)
} }
// NewEmptyModuleAccount creates a empty ModuleAccount from a string // NewEmptyModuleAccount creates a empty ModuleAccount from a string

View File

@ -32,25 +32,22 @@ const ModuleCredentialType = "ModuleCredential"
var _ cryptotypes.PubKey = &ModuleCredential{} var _ cryptotypes.PubKey = &ModuleCredential{}
func NewModuleCredential(moduleName string, derivationKeys [][]byte) *ModuleCredential { // NewModuleCredential creates new module credential key.
// All derivation keys must be non-empty.
func NewModuleCredential(moduleName string, derivationKeys ...[]byte) (*ModuleCredential, error) {
for i := range derivationKeys {
if len(derivationKeys[i]) == 0 {
return nil, fmt.Errorf("module credential derivation keys at index %d is empty", i)
}
}
return &ModuleCredential{ return &ModuleCredential{
ModuleName: moduleName, ModuleName: moduleName,
DerivationKeys: derivationKeys, DerivationKeys: derivationKeys,
} }, nil
} }
func (m *ModuleCredential) Address() cryptotypes.Address { func (m *ModuleCredential) Address() cryptotypes.Address {
var addr []byte return address.Module(m.ModuleName, m.DerivationKeys...)
for i, dk := range m.DerivationKeys {
if i == 0 {
addr = address.Module(m.ModuleName, dk)
continue
}
addr = address.Derive(addr, dk)
}
return addr
} }
func (m *ModuleCredential) Bytes() []byte { func (m *ModuleCredential) Bytes() []byte {

View File

@ -10,23 +10,43 @@ import (
) )
func TestNewModuleCrendentials(t *testing.T) { func TestNewModuleCrendentials(t *testing.T) {
// wrong derivation keys
_, err := authtypes.NewModuleCredential("group", []byte{})
require.Error(t, err, "derivation keys must be non empty")
_, err = authtypes.NewModuleCredential("group", [][]byte{{0x0, 0x30}, {}}...)
require.Error(t, err)
expected := sdk.MustAccAddressFromBech32("cosmos1fpn0w0yf4x300llf5r66jnfhgj4ul6cfahrvqsskwkhsw6sv84wsmz359y") expected := sdk.MustAccAddressFromBech32("cosmos1fpn0w0yf4x300llf5r66jnfhgj4ul6cfahrvqsskwkhsw6sv84wsmz359y")
credential := authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x0}}) credential, err := authtypes.NewModuleCredential("group")
require.NoError(t, sdk.VerifyAddressFormat(credential.Address().Bytes())) require.NoError(t, err, "must be able to create a Root Module credential (see ADR-33)")
require.NoError(t, sdk.VerifyAddressFormat(credential.Address()))
credential, err = authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x0}}...)
require.NoError(t, err)
require.NoError(t, sdk.VerifyAddressFormat(credential.Address()))
addr, err := sdk.AccAddressFromHexUnsafe(credential.Address().String()) addr, err := sdk.AccAddressFromHexUnsafe(credential.Address().String())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expected.String(), addr.String()) require.Equal(t, expected.String(), addr.String())
require.True(t, credential.Equals(authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x0}}))) c, err := authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x0}}...)
require.False(t, credential.Equals(authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x1}}))) require.NoError(t, err)
require.False(t, credential.Equals(authtypes.NewModuleCredential("group", [][]byte{{0x20}}))) require.True(t, credential.Equals(c))
c, err = authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x1}}...)
require.NoError(t, err)
require.False(t, credential.Equals(c))
c, err = authtypes.NewModuleCredential("group", []byte{0x20})
require.NoError(t, err)
require.False(t, credential.Equals(c))
} }
func TestNewBaseAccountWithPubKey(t *testing.T) { func TestNewBaseAccountWithPubKey(t *testing.T) {
expected := sdk.MustAccAddressFromBech32("cosmos1fpn0w0yf4x300llf5r66jnfhgj4ul6cfahrvqsskwkhsw6sv84wsmz359y") expected := sdk.MustAccAddressFromBech32("cosmos1fpn0w0yf4x300llf5r66jnfhgj4ul6cfahrvqsskwkhsw6sv84wsmz359y")
credential := authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x0}}) credential, err := authtypes.NewModuleCredential("group", [][]byte{{0x20}, {0x0}}...)
require.NoError(t, err)
account, err := authtypes.NewBaseAccountWithPubKey(credential) account, err := authtypes.NewBaseAccountWithPubKey(credential)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expected, account.GetAddress()) require.Equal(t, expected, account.GetAddress())

View File

@ -127,16 +127,17 @@ func (s TestSuite) setNextAccount() {
derivationKey := make([]byte, 8) derivationKey := make([]byte, 8)
binary.BigEndian.PutUint64(derivationKey, nextAccVal) binary.BigEndian.PutUint64(derivationKey, nextAccVal)
accountCredentials := authtypes.NewModuleCredential(group.ModuleName, [][]byte{{keeper.GroupPolicyTablePrefix}, derivationKey}) ac, err := authtypes.NewModuleCredential(group.ModuleName, []byte{keeper.GroupPolicyTablePrefix}, derivationKey)
groupPolicyAcc, err := authtypes.NewBaseAccountWithPubKey(accountCredentials)
s.Require().NoError(err) s.Require().NoError(err)
groupPolicyAccBumpAccountNumber, err := authtypes.NewBaseAccountWithPubKey(accountCredentials) groupPolicyAcc, err := authtypes.NewBaseAccountWithPubKey(ac)
s.Require().NoError(err)
groupPolicyAccBumpAccountNumber, err := authtypes.NewBaseAccountWithPubKey(ac)
s.Require().NoError(err) s.Require().NoError(err)
groupPolicyAccBumpAccountNumber.SetAccountNumber(nextAccVal) groupPolicyAccBumpAccountNumber.SetAccountNumber(nextAccVal)
s.accountKeeper.EXPECT().GetAccount(gomock.Any(), sdk.AccAddress(accountCredentials.Address())).Return(nil).AnyTimes() s.accountKeeper.EXPECT().GetAccount(gomock.Any(), sdk.AccAddress(ac.Address())).Return(nil).AnyTimes()
s.accountKeeper.EXPECT().NewAccount(gomock.Any(), groupPolicyAcc).Return(groupPolicyAccBumpAccountNumber).AnyTimes() s.accountKeeper.EXPECT().NewAccount(gomock.Any(), groupPolicyAcc).Return(groupPolicyAccBumpAccountNumber).AnyTimes()
s.accountKeeper.EXPECT().SetAccount(gomock.Any(), authtypes.AccountI(groupPolicyAccBumpAccountNumber)).Return().AnyTimes() s.accountKeeper.EXPECT().SetAccount(gomock.Any(), authtypes.AccountI(groupPolicyAccBumpAccountNumber)).Return().AnyTimes()
} }

View File

@ -336,8 +336,11 @@ func (k Keeper) CreateGroupPolicy(goCtx context.Context, req *group.MsgCreateGro
derivationKey := make([]byte, 8) derivationKey := make([]byte, 8)
binary.BigEndian.PutUint64(derivationKey, nextAccVal) binary.BigEndian.PutUint64(derivationKey, nextAccVal)
accountCredentials := authtypes.NewModuleCredential(group.ModuleName, [][]byte{{GroupPolicyTablePrefix}, derivationKey}) ac, err := authtypes.NewModuleCredential(group.ModuleName, []byte{GroupPolicyTablePrefix}, derivationKey)
accountAddr = sdk.AccAddress(accountCredentials.Address()) if err != nil {
return nil, err
}
accountAddr = sdk.AccAddress(ac.Address())
if k.accKeeper.GetAccount(ctx, accountAddr) != nil { if k.accKeeper.GetAccount(ctx, accountAddr) != nil {
// handle a rare collision, in which case we just go on to the // handle a rare collision, in which case we just go on to the
// next sequence value and derive a new address. // next sequence value and derive a new address.
@ -345,7 +348,7 @@ func (k Keeper) CreateGroupPolicy(goCtx context.Context, req *group.MsgCreateGro
} }
// group policy accounts are unclaimable base accounts // group policy accounts are unclaimable base accounts
account, err := authtypes.NewBaseAccountWithPubKey(accountCredentials) account, err := authtypes.NewBaseAccountWithPubKey(ac)
if err != nil { if err != nil {
return nil, sdkerrors.Wrap(err, "could not create group policy account") return nil, sdkerrors.Wrap(err, "could not create group policy account")
} }

View File

@ -33,9 +33,11 @@ func MigrateGenState(oldState *authtypes.GenesisState) *authtypes.GenesisState {
derivationKey := make([]byte, 8) derivationKey := make([]byte, 8)
binary.BigEndian.PutUint64(derivationKey, groupPolicyAccountCounter) binary.BigEndian.PutUint64(derivationKey, groupPolicyAccountCounter)
baseAccount, err := authtypes.NewBaseAccountWithPubKey( cred, err := authtypes.NewModuleCredential(ModuleName, []byte{GroupPolicyTablePrefix}, derivationKey)
authtypes.NewModuleCredential(ModuleName, [][]byte{{GroupPolicyTablePrefix}, derivationKey}), if err != nil {
) panic(err)
}
baseAccount, err := authtypes.NewBaseAccountWithPubKey(cred)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -52,10 +52,13 @@ func TestMigrateGenState(t *testing.T) {
AccountNumber: 8, AccountNumber: 8,
} }
derivationKey := make([]byte, 8) k := make([]byte, 8)
binary.BigEndian.PutUint64(derivationKey, 0) binary.BigEndian.PutUint64(k, 0)
c, err := authtypes.NewModuleCredential(group.ModuleName, []byte{v2.GroupPolicyTablePrefix}, k)
err := baseAccount.SetPubKey(authtypes.NewModuleCredential(group.ModuleName, [][]byte{{v2.GroupPolicyTablePrefix}, derivationKey})) if err != nil {
panic(err)
}
err = baseAccount.SetPubKey(c)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -21,7 +21,7 @@ const (
) )
// Migrate migrates the x/group module state from the consensus version 1 to version 2. // Migrate migrates the x/group module state from the consensus version 1 to version 2.
// Specifically, it changes the group policy account to from module account to base account. // Specifically, it changes the group policy account from module account to base account.
func Migrate( func Migrate(
ctx sdk.Context, ctx sdk.Context,
storeKey storetypes.StoreKey, storeKey storetypes.StoreKey,
@ -32,11 +32,11 @@ func Migrate(
store := ctx.KVStore(storeKey) store := ctx.KVStore(storeKey)
curAccVal := groupPolicySeq.CurVal(store) curAccVal := groupPolicySeq.CurVal(store)
groupPolicyAccountDerivationKey := make(map[string][]byte, 0) groupPolicyAccountDerivationKey := make(map[string][]byte, 0)
policyKey := []byte{GroupPolicyTablePrefix}
for i := uint64(0); i <= curAccVal; i++ { for i := uint64(0); i <= curAccVal; i++ {
derivationKey := make([]byte, 8) derivationKey := make([]byte, 8)
binary.BigEndian.PutUint64(derivationKey, i) binary.BigEndian.PutUint64(derivationKey, i)
parentAcc := address.Module(group.ModuleName, []byte{GroupPolicyTablePrefix}) groupPolicyAcc := sdk.AccAddress(address.Module(group.ModuleName, policyKey, derivationKey))
groupPolicyAcc := sdk.AccAddress(address.Derive(parentAcc, derivationKey))
groupPolicyAccountDerivationKey[groupPolicyAcc.String()] = derivationKey groupPolicyAccountDerivationKey[groupPolicyAcc.String()] = derivationKey
} }
@ -59,8 +59,11 @@ func Migrate(
panic(fmt.Errorf("group policy account %s derivation key not found", policy.Address)) panic(fmt.Errorf("group policy account %s derivation key not found", policy.Address))
} }
accountCredentials := authtypes.NewModuleCredential(group.ModuleName, [][]byte{{GroupPolicyTablePrefix}, derivationKey}) ac, err := authtypes.NewModuleCredential(group.ModuleName, []byte{GroupPolicyTablePrefix}, derivationKey)
baseAccount, err := authtypes.NewBaseAccountWithPubKey(accountCredentials) if err != nil {
return err
}
baseAccount, err := authtypes.NewBaseAccountWithPubKey(ac)
if err != nil { if err != nil {
return fmt.Errorf("failed to create new group policy account: %w", err) return fmt.Errorf("failed to create new group policy account: %w", err)
} }