Skip to content

Commit

Permalink
CreateStorageKey improvements + Comprehensive tests (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
ParthDesai authored Jun 21, 2021
1 parent 00abec4 commit db7f2af
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 63 deletions.
39 changes: 4 additions & 35 deletions types/metadataV13.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,45 +291,14 @@ func (s StorageFunctionMetadataV13) Hashers() ([]hash.Hash, error) {
return nil, fmt.Errorf("only NMaps have Hashers")
}

var hashers []hash.Hash
if s.Type.IsMap {
hashers = make([]hash.Hash, 1)
mapHasher, err := s.Type.AsMap.Hasher.HashFunc()
hashers := make([]hash.Hash, len(s.Type.AsNMap.Hashers))
for i, hasher := range s.Type.AsNMap.Hashers {
hasherFn, err := hasher.HashFunc()
if err != nil {
return nil, err
}
hashers[0] = mapHasher
return hashers, nil
hashers[i] = hasherFn
}
if s.Type.IsDoubleMap {
hashers = make([]hash.Hash, 2)
firstDoubleMapHasher, err := s.Type.AsDoubleMap.Hasher.HashFunc()
if err != nil {
return nil, err
}
hashers[0] = firstDoubleMapHasher
secondDoubleMapHasher, err := s.Type.AsDoubleMap.Key2Hasher.HashFunc()
if err != nil {
return nil, err
}
hashers[1] = secondDoubleMapHasher
return hashers, nil
}
if s.Type.IsNMap {
hashers = make([]hash.Hash, len(s.Type.AsNMap.Hashers))
for i, hasher := range s.Type.AsNMap.Hashers {
hasherFn, err := hasher.HashFunc()
if err != nil {
return nil, err
}
hashers[i] = hasherFn
}
return hashers, nil
}

hashers = make([]hash.Hash, 1)
hashers[0] = xxhash.New128(nil)

return hashers, nil
}

Expand Down
41 changes: 17 additions & 24 deletions types/storage_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,37 @@ func CreateStorageKey(meta *Metadata, prefix, method string, args ...[]byte) (St
}

if entryMeta.IsNMap() {
return createKeyNMap(meta, method, prefix, validatedArgs, entryMeta)
hashers, err := entryMeta.Hashers()
if err != nil {
return nil, fmt.Errorf("unable to get hashers for %s nmap", method)
}
if len(hashers) != len(validatedArgs) {
return nil, fmt.Errorf("%s:%s is a nmap, therefore requires that number of arguments should "+
"exactly match number of hashers in metadata. "+
"Expected: %d, received: %d", prefix, method, len(hashers), len(validatedArgs))
}
return createKeyNMap(method, prefix, validatedArgs, entryMeta)
}

if entryMeta.IsDoubleMap() {
if len(validatedArgs) != 2 {
return nil, fmt.Errorf("%v is a double map, therefore requires precisely two arguments. "+
"received: %d", method, len(validatedArgs))
return nil, fmt.Errorf("%s:%s is a double map, therefore requires precisely two arguments. "+
"received: %d", prefix, method, len(validatedArgs))
}
return createKeyDoubleMap(meta, method, prefix, stringKey, validatedArgs[0], validatedArgs[1], entryMeta)
}

if entryMeta.IsMap() {
if len(validatedArgs) != 1 {
return nil, fmt.Errorf("%v is a map, therefore requires precisely one argument. "+
"received: %d", method, len(validatedArgs))
return nil, fmt.Errorf("%s:%s is a map, therefore requires precisely one argument. "+
"received: %d", prefix, method, len(validatedArgs))
}
return createKey(meta, method, prefix, stringKey, validatedArgs[0], entryMeta)
}

if entryMeta.IsPlain() && len(validatedArgs) != 0 {
return nil, fmt.Errorf("%v is a plain key, therefore requires no argument. "+
"received: %d", method, len(validatedArgs))
return nil, fmt.Errorf("%s:%s is a plain key, therefore requires no argument. "+
"received: %d", prefix, method, len(validatedArgs))
}

return createKey(meta, method, prefix, stringKey, nil, entryMeta)
Expand Down Expand Up @@ -131,22 +140,12 @@ func (s StorageKey) Hex() string {
return fmt.Sprintf("%#x", s)
}

func createKeyNMap(meta *Metadata, method, prefix string, args [][]byte,
entryMeta StorageEntryMetadata) (StorageKey, error) {
if !meta.IsMetadataV13 {
return nil, fmt.Errorf("storage n map is only supported in metadata version 13 or up")
}

func createKeyNMap(method, prefix string, args [][]byte, entryMeta StorageEntryMetadata) (StorageKey, error) {
hashers, err := entryMeta.Hashers()
if err != nil {
return nil, err
}

if len(hashers) != len(args) {
return nil, fmt.Errorf("number of arguments should exactly match number of hashers in metadata. "+
"Expected: %d, received: %d", len(hashers), len(args))
}

key := createPrefixedKey(method, prefix)

for i, arg := range args {
Expand All @@ -163,9 +162,6 @@ func createKeyNMap(meta *Metadata, method, prefix string, args [][]byte,
// createKeyDoubleMap creates a key for a DoubleMap type
func createKeyDoubleMap(meta *Metadata, method, prefix string, stringKey, arg, arg2 []byte,
entryMeta StorageEntryMetadata) (StorageKey, error) {
if arg == nil || arg2 == nil {
return nil, fmt.Errorf("%v is a DoubleMap and requires two arguments", method)
}

hasher, err := entryMeta.Hasher()
if err != nil {
Expand Down Expand Up @@ -208,9 +204,6 @@ func createKeyDoubleMap(meta *Metadata, method, prefix string, stringKey, arg, a
// createKey creates a key for either a map or a plain value
func createKey(meta *Metadata, method, prefix string, stringKey, arg []byte, entryMeta StorageEntryMetadata) (
StorageKey, error) {
if entryMeta.IsMap() && arg == nil {
return nil, fmt.Errorf("%v is a Map and requires one argument", method)
}

hasher, err := entryMeta.Hasher()
if err != nil {
Expand Down
204 changes: 200 additions & 4 deletions types/storage_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
package types_test

import (
"encoding/binary"
"github.com/centrifuge/go-substrate-rpc-client/v3/hash"
"github.com/centrifuge/go-substrate-rpc-client/v3/xxhash"
"strings"
"testing"

. "github.com/centrifuge/go-substrate-rpc-client/v3/types"
Expand All @@ -27,10 +31,202 @@ const (
AlicePubKey = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
)

func TestCreateStorageKeyArgValidationForPlainKey(t *testing.T) {
m := ExamplaryMetadataV13

_, err := CreateStorageKey(m, "Timestamp", "Now")
assert.NoError(t, err)

_, err = CreateStorageKey(m, "Timestamp", "Now", nil)
assert.NoError(t, err)

_, err = CreateStorageKey(m, "Timestamp", "Now", nil, []byte{})
assert.NoError(t, err)

_, err = CreateStorageKey(m, "Timestamp", "Now", nil, []byte{0x01})
assert.EqualError(t, err, "non-nil arguments cannot be preceded by nil arguments")

_, err = CreateStorageKey(m, "Timestamp", "Now", []byte{0x01})
assert.EqualError(t, err, "Timestamp:Now is a plain key, therefore requires no argument. received: 1")

expectedKeyBuilder := strings.Builder{}
hexStr, err := Hex(xxhash.New128([]byte("Timestamp")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(hexStr)
hexStr, err = Hex(xxhash.New128([]byte("Now")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))

key, err := CreateStorageKey(m, "Timestamp", "Now")
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)
assert.Equal(t, expectedKeyBuilder.String(), hex)
}

func TestCreateStorageKeyArgValidationForMapKey(t *testing.T) {
m := ExamplaryMetadataV13

_, err := CreateStorageKey(m, "System", "Account")
assert.EqualError(t, err, "System:Account is a map, therefore requires precisely one argument. " +
"received: 0")

_, err = CreateStorageKey(m, "System", "Account", nil)
assert.EqualError(t, err, "System:Account is a map, therefore requires precisely one argument. " +
"received: 0")

_, err = CreateStorageKey(m, "System", "Account", nil, []byte{})
assert.EqualError(t, err, "System:Account is a map, therefore requires precisely one argument. " +
"received: 0")

_, err = CreateStorageKey(m, "System", "Account", nil, []byte{0x01})
assert.EqualError(t, err, "non-nil arguments cannot be preceded by nil arguments")

accountIdSerialized := MustHexDecodeString(AlicePubKey)

// Build expected answer
expectedKeyBuilder := strings.Builder{}
hexStr, err := Hex(xxhash.New128([]byte("System")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(hexStr)
hexStr, err = Hex(xxhash.New128([]byte("Account")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))
accountIdHasher, err := hash.NewBlake2b128Concat(nil)
assert.NoError(t, err)
_, err = accountIdHasher.Write(accountIdSerialized)
assert.NoError(t, err)
hexStr, err = Hex(accountIdHasher.Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))

key, err := CreateStorageKey(m, "System", "Account", accountIdSerialized)
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)
assert.Equal(t, expectedKeyBuilder.String(), hex)
}

func TestCreateStorageKeyArgValidationForDoubleMapKey(t *testing.T) {
m := ExamplaryMetadataV13

_, err := CreateStorageKey(m, "Staking", "ErasStakers")
assert.EqualError(t, err, "Staking:ErasStakers is a double map, therefore requires precisely two " +
"arguments. received: 0")

_, err = CreateStorageKey(m, "Staking", "ErasStakers", nil)
assert.EqualError(t, err, "Staking:ErasStakers is a double map, therefore requires precisely two " +
"arguments. received: 0")

_, err = CreateStorageKey(m, "Staking", "ErasStakers", nil, []byte{})
assert.EqualError(t, err, "Staking:ErasStakers is a double map, therefore requires precisely two " +
"arguments. received: 0")

_, err = CreateStorageKey(m, "Staking", "ErasStakers", nil, []byte{0x01})
assert.EqualError(t, err, "non-nil arguments cannot be preceded by nil arguments")

_, err = CreateStorageKey(m, "Staking", "ErasStakers", []byte{0x01})
assert.EqualError(t, err, "Staking:ErasStakers is a double map, therefore requires precisely two " +
"arguments. received: 1")

// Serialize EraIndex and AccountId
accountIdSerialized := MustHexDecodeString(AlicePubKey)
var eraIndex uint32 = 3
eraIndexSerialized := make([]byte, 4)
binary.LittleEndian.PutUint32(eraIndexSerialized, eraIndex)

// Build expected answer
expectedKeyBuilder := strings.Builder{}
hexStr, err := Hex(xxhash.New128([]byte("Staking")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(hexStr)
hexStr, err = Hex(xxhash.New128([]byte("ErasStakers")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))
hexStr, err = Hex(xxhash.New64Concat(eraIndexSerialized).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))
hexStr, err = Hex(xxhash.New64Concat(accountIdSerialized).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))

key, err := CreateStorageKey(m, "Staking", "ErasStakers", eraIndexSerialized, accountIdSerialized)
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)

assert.Equal(t, expectedKeyBuilder.String(), hex)
}

func TestCreateStorageKeyArgValidationForNMapKey(t *testing.T) {
m := ExamplaryMetadataV13
//"Assets", "Approvals", "AssetId(u32)", "AccountId", "AccountId"

_, err := CreateStorageKey(m, "Assets", "Approvals")
assert.EqualError(t, err, "Assets:Approvals is a nmap, therefore requires that number of arguments " +
"should exactly match number of hashers in metadata. Expected: 3, received: 0")

_, err = CreateStorageKey(m, "Assets", "Approvals", nil)
assert.EqualError(t, err, "Assets:Approvals is a nmap, therefore requires that number of arguments " +
"should exactly match number of hashers in metadata. Expected: 3, received: 0")

_, err = CreateStorageKey(m, "Assets", "Approvals", nil, []byte{})
assert.EqualError(t, err, "Assets:Approvals is a nmap, therefore requires that number of arguments " +
"should exactly match number of hashers in metadata. Expected: 3, received: 0")

_, err = CreateStorageKey(m, "Assets", "Approvals", nil, []byte{0x01})
assert.EqualError(t, err, "non-nil arguments cannot be preceded by nil arguments")

_, err = CreateStorageKey(m, "Assets", "Approvals", []byte{0x01})
assert.EqualError(t, err, "Assets:Approvals is a nmap, therefore requires that number of arguments " +
"should exactly match number of hashers in metadata. Expected: 3, received: 1")

// Serialize EraIndex and AccountId
var assetId uint32 = 3
assetIdSerialized := make([]byte, 4)
binary.LittleEndian.PutUint32(assetIdSerialized, assetId)
// Will be used both as owner as well as delegate
accountIdSerialized := MustHexDecodeString(AlicePubKey)

// Build expected answer
expectedKeyBuilder := strings.Builder{}
hexStr, err := Hex(xxhash.New128([]byte("Assets")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(hexStr)
hexStr, err = Hex(xxhash.New128([]byte("Approvals")).Sum(nil))
assert.NoError(t, err)
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))
// Hashing serialized asset id
assetIdHasher, err := hash.NewBlake2b128Concat(nil)
assert.NoError(t, err)
_, err = assetIdHasher.Write(assetIdSerialized)
assert.NoError(t, err)
hexStr, err = Hex(assetIdHasher.Sum(nil))
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))
// Hashing serialized account id
accountIdHasher, err := hash.NewBlake2b128Concat(nil)
assert.NoError(t, err)
_, err = accountIdHasher.Write(accountIdSerialized)
assert.NoError(t, err)
hexStr, err = Hex(accountIdHasher.Sum(nil))
// Writing it multiple times as both owner and delegate
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))
expectedKeyBuilder.WriteString(strings.TrimPrefix(hexStr, "0x"))


key, err := CreateStorageKey(m, "Assets", "Approvals", assetIdSerialized, accountIdSerialized,
accountIdSerialized)
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)

assert.Equal(t, expectedKeyBuilder.String(), hex)
}

func TestCreateStorageKeyPlainV13(t *testing.T) {
m := ExamplaryMetadataV13

key, err := CreateStorageKey(m, "Timestamp", "Now", nil)
key, err := CreateStorageKey(m, "Timestamp", "Now")
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)
Expand All @@ -40,7 +236,7 @@ func TestCreateStorageKeyPlainV13(t *testing.T) {
func TestCreateStorageKeyPlainV10(t *testing.T) {
m := ExamplaryMetadataV10

key, err := CreateStorageKey(m, "Timestamp", "Now", nil)
key, err := CreateStorageKey(m, "Timestamp", "Now")
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)
Expand All @@ -50,7 +246,7 @@ func TestCreateStorageKeyPlainV10(t *testing.T) {
func TestCreateStorageKeyPlainV9(t *testing.T) {
m := ExamplaryMetadataV9

key, err := CreateStorageKey(m, "Timestamp", "Now", nil)
key, err := CreateStorageKey(m, "Timestamp", "Now")
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)
Expand All @@ -60,7 +256,7 @@ func TestCreateStorageKeyPlainV9(t *testing.T) {
func TestCreateStorageKeyPlainV4(t *testing.T) {
m := ExamplaryMetadataV4

key, err := CreateStorageKey(m, "Timestamp", "Now", nil)
key, err := CreateStorageKey(m, "Timestamp", "Now")
assert.NoError(t, err)
hex, err := Hex(key)
assert.NoError(t, err)
Expand Down

0 comments on commit db7f2af

Please sign in to comment.