From 25a2b79b2f563cab3a145f54f3da88931afdf960 Mon Sep 17 00:00:00 2001 From: Axay Sagathiya Date: Wed, 12 Jul 2023 03:12:07 +0530 Subject: [PATCH] enhancement(lib/genesis): Create struct of Runtime field of the Genesis struct. (#2808) - Created struct for Runtime field of Genesis struct. - Created structs for all the fields of Runtime struct. - Wrote Marshal and Unmarshal methods for - some fields of Runtime struct - AuthorityAsAddress struct in lib/crypto/keypair.go - Wrote Marshal method for Uint128. - added empty string check for function argument of `PublicAddressToByteArray` function in `lib/crypto/keypair.go` --- cmd/gossamer/commands/import_runtime.go | 2 +- cmd/gossamer/commands/import_runtime_test.go | 2 +- dot/build_spec.go | 2 +- dot/build_spec_integration_test.go | 2 +- dot/build_spec_test.go | 17 +- dot/rpc/modules/sync_state.go | 2 +- dot/rpc/modules/sync_state_test.go | 2 +- dot/types/authority.go | 17 + dot/types/authority_test.go | 49 +++ lib/crypto/keypair.go | 3 + lib/genesis/genesis.go | 172 +++++++- lib/genesis/genesis_test.go | 37 ++ lib/genesis/helpers.go | 435 ++++++++++++------- lib/genesis/helpers_test.go | 253 +++-------- lib/genesis/pallet.go | 186 ++------ lib/genesis/pallet_test.go | 55 +++ pkg/scale/uint128.go | 5 + 17 files changed, 721 insertions(+), 520 deletions(-) create mode 100644 dot/types/authority_test.go create mode 100644 lib/genesis/pallet_test.go diff --git a/cmd/gossamer/commands/import_runtime.go b/cmd/gossamer/commands/import_runtime.go index d7d905bda3..f340e9ebad 100644 --- a/cmd/gossamer/commands/import_runtime.go +++ b/cmd/gossamer/commands/import_runtime.go @@ -69,7 +69,7 @@ func createGenesisWithRuntime(fp string, genesisSpecFilePath string) (string, er return "", err } - chainSpec.Genesis.Runtime["system"]["code"] = fmt.Sprintf("0x%x", runtime) + chainSpec.Genesis.Runtime.System.Code = fmt.Sprintf("0x%x", runtime) jsonSpec, err := json.MarshalIndent(chainSpec, "", "\t") if err != nil { return "", err diff --git a/cmd/gossamer/commands/import_runtime_test.go b/cmd/gossamer/commands/import_runtime_test.go index ee529188bd..a5024f5ca3 100644 --- a/cmd/gossamer/commands/import_runtime_test.go +++ b/cmd/gossamer/commands/import_runtime_test.go @@ -50,5 +50,5 @@ func TestCreateGenesisWithRuntime(t *testing.T) { g := new(genesis.Genesis) err = json.Unmarshal([]byte(out), g) require.NoError(t, err) - require.Equal(t, testHex, g.Genesis.Runtime["system"]["code"].(string)) + require.Equal(t, testHex, g.Genesis.Runtime.System.Code) } diff --git a/dot/build_spec.go b/dot/build_spec.go index fb877196a2..fd377da69c 100644 --- a/dot/build_spec.go +++ b/dot/build_spec.go @@ -92,7 +92,7 @@ func BuildFromDB(path string) (*BuildSpec, error) { }, } tmpGen.Genesis.Raw = make(map[string]map[string]string) - tmpGen.Genesis.Runtime = make(map[string]map[string]interface{}) + tmpGen.Genesis.Runtime = new(genesis.Runtime) config := state.Config{ Path: path, diff --git a/dot/build_spec_integration_test.go b/dot/build_spec_integration_test.go index 4a8dc3e76e..42b3498019 100644 --- a/dot/build_spec_integration_test.go +++ b/dot/build_spec_integration_test.go @@ -75,5 +75,5 @@ func TestBuildFromDB_Integration(t *testing.T) { err = json.Unmarshal(res, &jGen) require.NoError(t, err) - require.Equal(t, expected.Genesis.Raw["top"][codeHex], jGen.Genesis.Runtime["system"]["code"]) + require.Equal(t, expected.Genesis.Raw["top"][codeHex], jGen.Genesis.Runtime.System.Code) } diff --git a/dot/build_spec_test.go b/dot/build_spec_test.go index eee6456c33..990a2a1741 100644 --- a/dot/build_spec_test.go +++ b/dot/build_spec_test.go @@ -143,7 +143,7 @@ func TestBuildFromDB(t *testing.T) { ProtocolID: "dot", Genesis: genesis.Fields{ Raw: map[string]map[string]string{}, - Runtime: map[string]map[string]interface{}{}, + Runtime: new(genesis.Runtime), }, }}}, {name: "invalid_db_path", path: t.TempDir(), @@ -160,7 +160,7 @@ func TestBuildFromDB(t *testing.T) { } if tt.want != nil { got.genesis.Genesis.Raw = map[string]map[string]string{} - got.genesis.Genesis.Runtime = map[string]map[string]interface{}{} + got.genesis.Genesis.Runtime = new(genesis.Runtime) assert.Equal(t, tt.want, got) } }) @@ -171,9 +171,10 @@ func TestBuildFromGenesis(t *testing.T) { rawGenesis := genesis.Genesis{ Name: "test", Genesis: genesis.Fields{ - Runtime: map[string]map[string]interface{}{ - "System": { - "code": "mocktestcode", + Raw: map[string]map[string]string{}, + Runtime: &genesis.Runtime{ + System: &genesis.System{ + Code: "mocktestcode", }, }, }, @@ -208,7 +209,11 @@ func TestBuildFromGenesis(t *testing.T) { Raw: map[string]map[string]string{"top" + "": {"0x26aa394eea5630e07c48ae0c9558cef7c21aab032aaa6e946ca50ad39ab66603": "0x01", "0x3a636f6465": "mocktestcode"}}, - Runtime: map[string]map[string]interface{}{"System": {"code": "mocktestcode"}}, + Runtime: &genesis.Runtime{ + System: &genesis.System{ + Code: "mocktestcode", + }, + }, }, }}, }, diff --git a/dot/rpc/modules/sync_state.go b/dot/rpc/modules/sync_state.go index 5d08a87a2c..ab6b8ec1d7 100644 --- a/dot/rpc/modules/sync_state.go +++ b/dot/rpc/modules/sync_state.go @@ -54,7 +54,7 @@ func NewStateSync(gData *genesis.Data, storageAPI StorageAPI) (SyncStateAPI, err }, } tmpGen.Genesis.Raw = make(map[string]map[string]string) - tmpGen.Genesis.Runtime = make(map[string]map[string]interface{}) + tmpGen.Genesis.Runtime = new(genesis.Runtime) // set genesis fields data ent, err := storageAPI.Entries(nil) diff --git a/dot/rpc/modules/sync_state_test.go b/dot/rpc/modules/sync_state_test.go index dfeaecc1d8..b4249364ea 100644 --- a/dot/rpc/modules/sync_state_test.go +++ b/dot/rpc/modules/sync_state_test.go @@ -118,7 +118,7 @@ func TestNewStateSync(t *testing.T) { ProtocolID: "", Genesis: genesis.Fields{ Raw: map[string]map[string]string{}, - Runtime: map[string]map[string]interface{}{}, + Runtime: new(genesis.Runtime), }, }, }, diff --git a/dot/types/authority.go b/dot/types/authority.go index 2dff7a4434..66a7ccdd54 100644 --- a/dot/types/authority.go +++ b/dot/types/authority.go @@ -5,6 +5,7 @@ package types import ( "encoding/binary" + "encoding/json" "fmt" "io" @@ -128,6 +129,22 @@ type AuthorityAsAddress struct { Weight uint64 } +// UnmarshalJSON converts data to Go struct of type AuthorityAsAddress. +func (a *AuthorityAsAddress) UnmarshalJSON(buf []byte) error { + // It's encoded as an array [] instead of an object {}, which is why this need. + tmp := []interface{}{&a.Address, &a.Weight} + return json.Unmarshal(buf, &tmp) +} + +func (a AuthorityAsAddress) MarshalJSON() ([]byte, error) { + tmp := []interface{}{&a.Address, &a.Weight} + buf, err := json.Marshal(tmp) + if err != nil { + return nil, err + } + return buf, nil +} + // AuthoritiesRawToAuthorityAsAddress converts an array of AuthorityRaws into an array of AuthorityAsAddress func AuthoritiesRawToAuthorityAsAddress(authsRaw []AuthorityRaw, kt crypto.KeyType) ([]AuthorityAsAddress, error) { auths := make([]AuthorityAsAddress, len(authsRaw)) diff --git a/dot/types/authority_test.go b/dot/types/authority_test.go new file mode 100644 index 0000000000..75bcd38d64 --- /dev/null +++ b/dot/types/authority_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var tcAuthorityAsAddress = []struct { + name string + jsonValue []byte + goValue AuthorityAsAddress +}{ + { + name: "test1", + jsonValue: []byte{ + 91, 34, 53, 71, 114, 119, 118, 97, 69, 70, 53, 122, 88, 98, 50, 54, 70, 122, + 57, 114, 99, 81, 112, 68, 87, 83, 53, 55, 67, 116, 69, 82, 72, 112, 78, 101, + 104, 88, 67, 80, 99, 78, 111, 72, 71, 75, 117, 116, 81, 89, 34, 44, 49, 93, + }, + goValue: AuthorityAsAddress{Address: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", Weight: 1}, + }, +} + +func TestAuthorityAsAddressMarshal(t *testing.T) { + for _, tt := range tcAuthorityAsAddress { + t.Run(tt.name, func(t *testing.T) { + marshalledValue, err := tt.goValue.MarshalJSON() + require.NoError(t, err) + require.Equal(t, tt.jsonValue, marshalledValue) + }) + } + +} + +func TestAuthorityAsAddressUnmarshal(t *testing.T) { + for _, tt := range tcAuthorityAsAddress { + t.Run(tt.name, func(t *testing.T) { + var authorityAsAddress AuthorityAsAddress + err := authorityAsAddress.UnmarshalJSON(tt.jsonValue) + require.NoError(t, err) + require.EqualValues(t, tt.goValue, authorityAsAddress) + }) + } + +} diff --git a/lib/crypto/keypair.go b/lib/crypto/keypair.go index 31b04cd7a5..49791022ec 100644 --- a/lib/crypto/keypair.go +++ b/lib/crypto/keypair.go @@ -69,6 +69,9 @@ func publicKeyBytesToAddress(b []byte) common.Address { // PublicAddressToByteArray returns []byte address for given PublicKey Address func PublicAddressToByteArray(add common.Address) []byte { + if add == "" { + return nil + } k := base58.Decode(string(add)) return k[1:33] } diff --git a/lib/genesis/genesis.go b/lib/genesis/genesis.go index 8b8c107f0b..bf81485027 100644 --- a/lib/genesis/genesis.go +++ b/lib/genesis/genesis.go @@ -4,6 +4,9 @@ package genesis import ( + "encoding/json" + + "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" ) @@ -46,8 +49,157 @@ type TelemetryEndpoint struct { // Fields stores genesis raw data, and human readable runtime data type Fields struct { - Raw map[string]map[string]string `json:"raw,omitempty"` - Runtime map[string]map[string]interface{} `json:"runtime,omitempty"` + Raw map[string]map[string]string `json:"raw,omitempty"` + Runtime *Runtime `json:"runtime,omitempty"` +} + +// Runtime is the structure of the genesis runtime field. +type Runtime struct { + System *System `json:"system,omitempty"` + Babe *babe `json:"babe,omitempty"` + Grandpa *grandpa `json:"grandpa,omitempty"` + Balances *balances `json:"balances,omitempty"` + Sudo *sudo `json:"sudo,omitempty"` + Session *session `json:"session,omitempty"` + Staking *staking `json:"staking,omitempty"` + Indices *indices `json:"indices,omitempty"` + ImOnline *imOnline `json:"imOnline,omitempty"` + AuthorityDiscovery *authorityDiscovery `json:"authorityDiscovery,omitempty"` + Vesting *vesting `json:"vesting"` + NominationPools *nominationPools `json:"nominationPools,omitempty"` + Configuration *configuration `json:"configuration,omitempty"` + Paras *paras `json:"paras"` + Hrmp *hrmp `json:"hrmp"` + Registrar *registrar `json:"registrar,omitempty"` + XcmPallet *xcmPallet `json:"xcmPallet,omitempty"` +} + +// System is the system structure inside the runtime field for the genesis. +type System struct { + Code string `json:"code,omitempty"` +} + +type babe struct { + Authorities []types.AuthorityAsAddress `json:"authorities"` + EpochConfig *epochConfig `json:"epochConfig,omitempty"` +} + +type epochConfig struct { + C []int `json:"c,omitempty"` + AllowedSlots string `json:"allowed_slots,omitempty"` +} + +type grandpa struct { + Authorities []types.AuthorityAsAddress `json:"authorities"` +} + +type balances struct { + Balances []balancesFields `json:"balances,omitempty"` +} + +type balancesFields struct { + AccountID string + Balance float64 +} + +type sudo struct { + Key string `json:"key,omitempty"` +} + +type indices struct { + Indices []interface{} `json:"indices"` +} + +type imOnline struct { + Keys []string `json:"keys"` +} + +type authorityDiscovery struct { + Keys []string `json:"keys"` +} + +type vesting struct { + Vesting []interface{} `json:"vesting"` +} + +type nominationPools struct { + MinJoinBond *uint `json:"minJoinBond,omitempty"` + MinCreateBond *uint `json:"minCreateBond,omitempty"` + MaxPools *uint `json:"maxPools,omitempty"` + MaxMembersPerPool *uint `json:"maxMembersPerPool,omitempty"` + MaxMembers *uint `json:"maxMembers,omitempty"` +} + +type configuration struct { + Config config `json:"config,omitempty"` +} + +type config struct { + MaxCodeSize *uint `json:"max_code_size"` + MaxHeadDataSize *uint `json:"max_head_data_size"` + MaxUpwardQueueCount *uint `json:"max_upward_queue_count"` + MaxUpwardQueueSize *uint `json:"max_upward_queue_size"` + MaxUpwardMessageSize *uint `json:"max_upward_message_size"` + MaxUpwardMessageNumPerCandidate *uint `json:"max_upward_message_num_per_candidate"` + HrmpMaxMessageNumPerCandidate *uint `json:"hrmp_max_message_num_per_candidate"` + ValidationUpgradeCooldown *uint `json:"validation_upgrade_cooldown"` + ValidationUpgradeDelay *uint `json:"validation_upgrade_delay"` + MaxPovSize *uint `json:"max_pov_size"` + MaxDownwardMessageSize *uint `json:"max_downward_message_size"` + UmpServiceTotalWeight *struct { + RefTime *uint `json:"ref_time"` + ProofSize *uint `json:"proof_size"` + } `json:"ump_service_total_weight"` + HrmpMaxParachainOutboundChannels *uint `json:"hrmp_max_parachain_outbound_channels"` + HrmpMaxParathreadOutboundChannels *uint `json:"hrmp_max_parathread_outbound_channels"` + HrmpSenderDeposit *uint `json:"hrmp_sender_deposit"` + HrmpRecipientDeposit *uint `json:"hrmp_recipient_deposit"` + HrmpChannelMaxCapacity *uint `json:"hrmp_channel_max_capacity"` + HrmpChannelMaxTotalSize *uint `json:"hrmp_channel_max_total_size"` + HrmpMaxParachainInboundChannels *uint `json:"hrmp_max_parachain_inbound_channels"` + HrmpMaxParathreadInboundChannels *uint `json:"hrmp_max_parathread_inbound_channels"` + HrmpChannelMaxMessageSize *uint `json:"hrmp_channel_max_message_size"` + CodeRetentionPeriod *uint `json:"code_retention_period"` + ParathreadCores *uint `json:"parathread_cores"` + ParathreadRetries *uint `json:"parathread_retries"` + GroupRotationFrequency *uint `json:"group_rotation_frequency"` + ChainAvailabilityPeriod *uint `json:"chain_availability_period"` + ThreadAvailabilityPeriod *uint `json:"thread_availability_period"` + SchedulingLookahead *uint `json:"scheduling_lookahead"` + MaxValidatorsPerCore *uint `json:"max_validators_per_core"` + MaxValidators *uint `json:"max_validators"` + DisputePeriod *uint `json:"dispute_period"` + DisputePostConclusionAcceptancePeriod *uint `json:"dispute_post_conclusion_acceptance_period"` + DisputeMaxSpamSlots *uint `json:"dispute_max_spam_slots"` + DisputeConclusionByTimeOutPeriod *uint `json:"dispute_conclusion_by_time_out_period"` + NoShowSlots *uint `json:"no_show_slots"` + NDelayTranches *uint `json:"n_delay_tranches"` + ZerothDelayTrancheWidth *uint `json:"zeroth_delay_tranche_width"` + NeededApprovals *uint `json:"needed_approvals"` + RelayVrfModuloSamples *uint `json:"relay_vrf_modulo_samples"` + UmpMaxIndividualWeight *struct { + RefTime *uint `json:"ref_time"` + ProofSize *uint `json:"proof_size"` + } `json:"ump_max_individual_weight"` + PvfCheckingEnabled bool `json:"pvf_checking_enabled"` + PvfVotingTTL *uint `json:"pvf_voting_ttl"` + MinimumValidationUpgradeDelay *uint `json:"minimum_validation_upgrade_delay"` +} + +type paras struct { + Paras []interface{} `json:"paras"` +} + +type hrmp struct { + PreopenHrmpChannels []interface{} `json:"preopenHrmpChannels"` +} + +type registrar struct { + NextFreeParaID *uint `json:"nextFreeParaId,omitempty"` +} + +type xcmPallet struct { + SafeXcmVersion *uint `json:"safeXcmVersion,omitempty"` } // GenesisData formats genesis for trie storage @@ -84,7 +236,7 @@ func (g *Genesis) ToRaw() error { } grt := g.Genesis.Runtime - res, err := buildRawMap(grt) + res, err := buildRawMap(*grt) if err != nil { return err } @@ -121,3 +273,17 @@ func interfaceToTelemetryEndpoint(endpoints []interface{}) []*TelemetryEndpoint return res } + +func (b *balancesFields) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&b.AccountID, &b.Balance} + return json.Unmarshal(buf, &tmp) +} + +func (b balancesFields) MarshalJSON() ([]byte, error) { + tmp := []interface{}{&b.AccountID, &b.Balance} + buf, err := json.Marshal(tmp) + if err != nil { + return nil, err + } + return buf, nil +} diff --git a/lib/genesis/genesis_test.go b/lib/genesis/genesis_test.go index be8184b7dd..98ac2d27ef 100644 --- a/lib/genesis/genesis_test.go +++ b/lib/genesis/genesis_test.go @@ -62,3 +62,40 @@ func Test_interfaceToTelemetryEndpoint(t *testing.T) { }) } } + +var tcBalancesFields = []struct { + name string + jsonValue []byte + goValue balancesFields +}{ + { + name: "test1", + jsonValue: []byte{ + 91, 34, 53, 71, 114, 119, 118, 97, 69, 70, 53, 122, 88, 98, 50, 54, 70, 122, 57, 114, 99, + 81, 112, 68, 87, 83, 53, 55, 67, 116, 69, 82, 72, 112, 78, 101, 104, 88, 67, 80, 99, 78, + 111, 72, 71, 75, 117, 116, 81, 89, 34, 44, 49, 50, 51, 52, 50, 51, 52, 50, 51, 52, 93, + }, + goValue: balancesFields{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1234234234}, + }, +} + +func TestBalancesFieldsMarshal(t *testing.T) { + for _, tt := range tcBalancesFields { + t.Run(tt.name, func(t *testing.T) { + marshalledValue, err := tt.goValue.MarshalJSON() + require.NoError(t, err) + require.Equal(t, tt.jsonValue, marshalledValue) + }) + } +} + +func TestBalancesFieldsUnmarshal(t *testing.T) { + for _, tt := range tcBalancesFields { + t.Run(tt.name, func(t *testing.T) { + var bfs balancesFields + err := bfs.UnmarshalJSON(tt.jsonValue) + require.NoError(t, err) + require.EqualValues(t, bfs, tt.goValue) + }) + } +} diff --git a/lib/genesis/helpers.go b/lib/genesis/helpers.go index 14d18cd9cf..8d37cdab1f 100644 --- a/lib/genesis/helpers.go +++ b/lib/genesis/helpers.go @@ -12,6 +12,7 @@ import ( "os" "path/filepath" "reflect" + "strings" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" @@ -22,20 +23,13 @@ import ( type accountAddr [32]byte const ( - societyConst = "Society" - stakingConst = "Staking" - contractsConst = "Contracts" - sessionConst = "Session" - instance1CollectiveConst = "Instance1Collective" - instance2CollectiveConst = "Instance2Collective" - instance1MembershipConst = "Instance1Membership" - phragmenElectionConst = "PhragmenElection" - notForcing = "NotForcing" - forceNew = "ForceNew" - forceNone = "ForceNone" - forceAlways = "ForceAlways" - currentSchedule = "CurrentSchedule" - phantom = "Phantom" + stakingConst = "staking" + sessionConst = "session" + notForcing = "NotForcing" + forceNew = "ForceNew" + forceNone = "ForceNone" + forceAlways = "ForceAlways" + phantom = "Phantom" ) // NewGenesisFromJSONRaw parses a JSON formatted genesis file @@ -56,19 +50,22 @@ func NewGenesisFromJSONRaw(file string) (*Genesis, error) { // trimGenesisAuthority iterates over authorities in genesis and keeps only `authCount` number of authorities. func trimGenesisAuthority(g *Genesis, authCount int) { - for k, authMap := range g.Genesis.Runtime { - if k != "Babe" && k != "Grandpa" { - continue + if g.Genesis.Runtime == nil { + return + } + + babe := g.Genesis.Runtime.Babe + if babe != nil { + if len(babe.Authorities) > authCount { + babe.Authorities = babe.Authorities[:authCount] } - authorities, _ := authMap["Authorities"].([]interface{}) - var newAuthorities []interface{} - for _, authority := range authorities { - if len(newAuthorities) >= authCount { - break - } - newAuthorities = append(newAuthorities, authority) + } + + grandpa := g.Genesis.Runtime.Grandpa + if grandpa != nil { + if len(grandpa.Authorities) > authCount { + grandpa.Authorities = grandpa.Authorities[:authCount] } - authMap["Authorities"] = newAuthorities } } @@ -84,15 +81,18 @@ func NewGenesisFromJSON(file string, authCount int) (*Genesis, error) { trimGenesisAuthority(g, authCount) } + g.Genesis.Raw = make(map[string]map[string]string) grt := g.Genesis.Runtime - res, err := buildRawMap(grt) + if grt == nil { + return g, nil + } + + res, err := buildRawMap(*grt) if err != nil { return nil, err } - g.Genesis.Raw = make(map[string]map[string]string) g.Genesis.Raw["top"] = res - return g, err } @@ -126,7 +126,7 @@ type keyValue struct { iVal []interface{} } -func generatePalletKeyValue(k string, v map[string]interface{}, res map[string]string) (bool, error) { +func generatePalletKeyValue(k string, v interface{}, res map[string]string) (bool, error) { jsonBody, err := json.Marshal(v) if err != nil { return false, err @@ -134,21 +134,9 @@ func generatePalletKeyValue(k string, v map[string]interface{}, res map[string]s var s interface{} switch k { - case societyConst: - s = &society{} case stakingConst: s = &staking{} - case contractsConst: - c := &contracts{} - if err = json.Unmarshal(jsonBody, c); err != nil { - return false, err - } - err = generateContractKeyValue(c, k, res) - if err != nil { - return false, err - } - return true, nil case sessionConst: sc := &session{} if err = json.Unmarshal(jsonBody, sc); err != nil { @@ -160,14 +148,6 @@ func generatePalletKeyValue(k string, v map[string]interface{}, res map[string]s return false, err } return true, nil - case instance1CollectiveConst: - s = &instance1Collective{} - case instance2CollectiveConst: - s = &instance2Collective{} - case instance1MembershipConst: - s = &instance1Membership{} - case phragmenElectionConst: - s = &phragmenElection{} default: return false, nil } @@ -182,13 +162,23 @@ func generatePalletKeyValue(k string, v map[string]interface{}, res map[string]s return true, nil } -func buildRawMap(m map[string]map[string]interface{}) (map[string]string, error) { +func buildRawMap(m Runtime) (map[string]string, error) { res := make(map[string]string) - for k, v := range m { + mRefObjVal := reflect.ValueOf(m) + + for i := 0; i < mRefObjVal.NumField(); i++ { + v := mRefObjVal.Field(i) + vInterface := v.Interface() + if v.IsNil() { + continue + } + + jsonTag := mRefObjVal.Type().Field(i).Tag.Get("json") + k := strings.Split(jsonTag, ",")[0] kv := new(keyValue) kv.key = append(kv.key, k) - ok, err := generatePalletKeyValue(k, v, res) + ok, err := generatePalletKeyValue(k, vInterface, res) if err != nil { return nil, err } @@ -197,11 +187,11 @@ func buildRawMap(m map[string]map[string]interface{}) (map[string]string, error) continue } - if err = buildRawMapInterface(v, kv); err != nil { + if err = buildRawMapInterface(vInterface, kv); err != nil { return nil, err } - if reflect.DeepEqual([]string{"Balances", "balances"}, kv.key) { + if reflect.DeepEqual([]string{"balances", "balances"}, kv.key) { err = buildBalances(kv, res) if err != nil { return nil, err @@ -225,35 +215,105 @@ func buildRawMap(m map[string]map[string]interface{}) (map[string]string, error) return res, nil } -func buildRawMapInterface(m map[string]interface{}, kv *keyValue) error { - for k, v := range m { +func buildRawMapInterface(m interface{}, kv *keyValue) error { + mRefObjVal := reflect.Indirect(reflect.ValueOf(m)) + + for i := 0; i < mRefObjVal.NumField(); i++ { + jsonTag := mRefObjVal.Type().Field(i).Tag.Get("json") + k := strings.Split(jsonTag, ",")[0] kv.key = append(kv.key, k) - switch v2 := v.(type) { - case []interface{}: - kv.valueLen = big.NewInt(int64(len(v2))) - if err := buildRawArrayInterface(v2, kv); err != nil { - return err + v := mRefObjVal.Field(i) + + if v.Kind() == reflect.Ptr { + if v.IsNil() { + continue } + v = v.Elem() + } + + if v.IsZero() { + continue + } + + switch v2 := v.Interface().(type) { case string: kv.value = v2 + case uint64, int64, int: + kv.value = fmt.Sprint(v2) + default: + + switch v.Kind() { + case reflect.Slice: + + vLen := v.Len() + listOfInterface := []interface{}{} + for i := 0; i < vLen; i++ { + listOfInterface = append(listOfInterface, v.Index(i).Interface()) + } + + if vLen > 0 && v.Index(0).Kind() == reflect.Struct { + kv.valueLen = big.NewInt(int64(v.Index(0).NumField())) + } else { + kv.valueLen = big.NewInt(int64(vLen)) + + } + if err := buildRawArrayInterface(listOfInterface, kv); err != nil { + return fmt.Errorf("error building raw array interface: %w", err) + } + case reflect.Struct: + kv.valueLen = big.NewInt(int64(v.NumField())) + if err := buildRawStructInterface(v2, kv); err != nil { + return fmt.Errorf("error building raw struct interface: %w", err) + } + default: + return fmt.Errorf("invalid value type %T", v2) + } } } return nil } -func buildRawArrayInterface(a []interface{}, kv *keyValue) error { - for _, v := range a { - switch v2 := v.(type) { +func buildRawStructInterface(m interface{}, kv *keyValue) error { + mRefObjVal := reflect.Indirect(reflect.ValueOf(m)) + for i := 0; i < mRefObjVal.NumField(); i++ { + v := mRefObjVal.Field(i) + + switch v2 := v.Interface().(type) { case []interface{}: - err := buildRawArrayInterface(v2, kv) - if err != nil { - return err + if err := buildRawArrayInterface(v2, kv); err != nil { + return fmt.Errorf("error building raw array interface: %w", err) } case string: // TODO: check to confirm it's an address (#1865) tba := crypto.PublicAddressToByteArray(common.Address(v2)) kv.value = kv.value + fmt.Sprintf("%x", tba) kv.iVal = append(kv.iVal, tba) + case common.Address: + // TODO: check to confirm it's an address (#1865) + tba := crypto.PublicAddressToByteArray(v2) + kv.value = kv.value + fmt.Sprintf("%x", tba) + kv.iVal = append(kv.iVal, tba) + case int64: + encVal, err := scale.Marshal(uint64(v2)) + if err != nil { + return err + } + kv.value = kv.value + fmt.Sprintf("%x", encVal) + kv.iVal = append(kv.iVal, big.NewInt(v2)) + case int: + encVal, err := scale.Marshal(uint64(v2)) + if err != nil { + return err + } + kv.value = kv.value + fmt.Sprintf("%x", encVal) + kv.iVal = append(kv.iVal, big.NewInt(int64(v2))) + case uint64: + encVal, err := scale.Marshal(v2) + if err != nil { + return err + } + kv.value = kv.value + fmt.Sprintf("%x", encVal) + kv.iVal = append(kv.iVal, big.NewInt(int64(v2))) case float64: encVal, err := scale.Marshal(uint64(v2)) if err != nil { @@ -261,6 +321,62 @@ func buildRawArrayInterface(a []interface{}, kv *keyValue) error { } kv.value = kv.value + fmt.Sprintf("%x", encVal) kv.iVal = append(kv.iVal, big.NewInt(int64(v2))) + case bool: + encVal, err := scale.Marshal(v2) + if err != nil { + return err + } + kv.value = kv.value + fmt.Sprintf("%x", encVal) + kv.iVal = append(kv.iVal, v2) + default: + switch v.Kind() { + case reflect.Slice: + + list := []interface{}{} + for i := 0; i < v.Len(); i++ { + list = append(list, v.Index(i).Interface()) + } + kv.valueLen = big.NewInt(int64(v.Len())) + if err := buildRawArrayInterface(list, kv); err != nil { + return fmt.Errorf("error building raw array interface: %w", err) + } + case reflect.Struct: + kv.valueLen = big.NewInt(int64(v.NumField())) + if err := buildRawStructInterface(v2, kv); err != nil { + return fmt.Errorf("error building raw struct interface: %w", err) + } + default: + return fmt.Errorf("invalid value type %T", v2) + } + } + } + return nil +} + +func buildRawArrayInterface(a []interface{}, kv *keyValue) error { + for _, v := range a { + switch v2 := v.(type) { + case int: + encVal, err := scale.Marshal(uint64(v2)) + if err != nil { + return err + } + kv.value = kv.value + fmt.Sprintf("%x", encVal) + kv.iVal = append(kv.iVal, big.NewInt(int64(v2))) + case string: + // TODO: check to confirm it's an address (#1865) + tba := crypto.PublicAddressToByteArray(common.Address(v2)) + kv.value = kv.value + fmt.Sprintf("%x", tba) + kv.iVal = append(kv.iVal, tba) + default: + switch reflect.ValueOf(v2).Kind() { + case reflect.Struct: + if err := buildRawStructInterface(v2, kv); err != nil { + return fmt.Errorf("error building raw struct interface: %w", err) + } + default: + return fmt.Errorf("invalid value type %T", v2) + } } } return nil @@ -286,8 +402,9 @@ func generateStorageValue(i interface{}, idx int) ([]byte, error) { err error ) - switch t := reflect.Indirect(val).Field(idx).Interface().(type) { - case int64, uint64, uint32, *scale.Uint128: + idxField := reflect.Indirect(val).Field(idx) + switch t := idxField.Interface().(type) { + case int, int64, uint64, uint32, *uint32, *scale.Uint128: encode, err = scale.Marshal(t) if err != nil { return nil, err @@ -328,58 +445,49 @@ func generateStorageValue(i interface{}, idx int) ([]byte, error) { if err != nil { return nil, err } - case [][]interface{}: - // TODO: for members field in phragmenElection struct figure out the correct format for encoding value (#1866) - for _, data := range t { - for _, v := range data { - var accAddr accountAddr - switch v1 := v.(type) { - case string: - copy(accAddr[:], crypto.PublicAddressToByteArray(common.Address(v1))) - encode = append(encode, accAddr[:]...) - case float64: - var bytesVal []byte - bytesVal, err = scale.Marshal(big.NewInt(int64(v1))) - if err != nil { - return nil, err + + default: + switch idxField.Kind() { + case reflect.Slice: + sliceOfData := []interface{}{} + + for i := 0; i < idxField.Len(); i++ { + sliceOfData = append(sliceOfData, idxField.Index(i).Interface()) + } + + for _, data := range sliceOfData { + mRefObjVal := reflect.Indirect(reflect.ValueOf(data)) + for i := 0; i < mRefObjVal.NumField(); i++ { + v := mRefObjVal.Field(i) + var accAddr accountAddr + switch v1 := v.Interface().(type) { + case string: + copy(accAddr[:], crypto.PublicAddressToByteArray(common.Address(v1))) + encode = append(encode, accAddr[:]...) + case float64: + var bytesVal []byte + bytesVal, err = scale.Marshal(big.NewInt(int64(v1))) + if err != nil { + return nil, err + } + encode = append(encode, bytesVal...) + default: + return nil, fmt.Errorf("invalid value type %T", v1) + } - encode = append(encode, bytesVal...) } } - } - - encode, err = scale.Marshal(encode) - if err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("invalid value type") - } - return encode, nil -} -func generateContractKeyValue(c *contracts, prefixKey string, res map[string]string) error { - var ( - key string - err error - ) + encode, err = scale.Marshal(encode) + if err != nil { + return nil, err + } - // First field of contract is the storage key - val := reflect.ValueOf(c) - if k := reflect.Indirect(val).Type().Field(0).Name; k == currentSchedule { - key, err = generateStorageKey(prefixKey, k) - if err != nil { - return err + default: + return nil, fmt.Errorf("invalid value type %T", t) } } - - encode, err := scale.Marshal(c) - if err != nil { - return err - } - - res[key] = common.BytesToHex(encode) - return nil + return encode, nil } func generateKeyValue(s interface{}, prefixKey string, res map[string]string) error { @@ -410,10 +518,10 @@ func generateKeyValue(s interface{}, prefixKey string, res map[string]string) er func formatKey(kv *keyValue) (string, error) { switch { - case reflect.DeepEqual([]string{"Grandpa", "Authorities"}, kv.key): + case reflect.DeepEqual([]string{"grandpa", "authorities"}, kv.key): kb := []byte(`:grandpa_authorities`) return common.BytesToHex(kb), nil - case reflect.DeepEqual([]string{"System", "code"}, kv.key): + case reflect.DeepEqual([]string{"system", "code"}, kv.key): kb := []byte(`:code`) return common.BytesToHex(kb), nil default: @@ -432,13 +540,15 @@ func generateSessionKeyValue(s *session, prefixKey string, res map[string]string return err } - storageVal, ok := val.Field(0).Interface().([][]interface{}) + storageVal, ok := val.Field(0).Interface().([]nextKey) if !ok { return nil } for _, strV := range storageVal { - for _, v := range strV { + refValOfStrV := reflect.ValueOf(strV) + for idx := 0; idx < refValOfStrV.NumField(); idx++ { + v := refValOfStrV.Field(idx).Interface() var validatorAccID []byte switch t := v.(type) { case string: @@ -458,41 +568,39 @@ func generateSessionKeyValue(s *session, prefixKey string, res map[string]string prefix := bytes.Join([][]byte{moduleName, nextKeyHash}, nil) suffix := bytes.Join([][]byte{accIDHash, validatorAccID}, nil) res[common.BytesToHex(append(prefix, suffix...))] = common.BytesToHex(validatorAccID) - case map[string]interface{}: + + case keyOwner: + keyOwnerAlias := map[string]string{ + "Grandpa": "gran", + "Babe": "babe", + "ImOnline": "imon", + "ParaValidator": "para", + "ParaAssignment": "asgn", + "AuthorityDiscovery": "audi", + } + var storagePrefixKey []byte storagePrefixKey, err = common.Twox128Hash([]byte("KeyOwner")) if err != nil { return err } - storagePrefixKey = append(moduleName, storagePrefixKey...) - for key, v1 := range t { - var addressKey []byte - switch key { - case "grandpa": - addressKey, err = generateAddressHash(v1.(string), "gran") - if err != nil { - return err - } - case "babe": - addressKey, err = generateAddressHash(v1.(string), "babe") - if err != nil { - return err - } - case "im_online": - addressKey, err = generateAddressHash(v1.(string), "imon") - if err != nil { - return err - } - case "authority_discovery": - addressKey, err = generateAddressHash(v1.(string), "audi") - if err != nil { - return err - } - default: + + refValOfT := reflect.ValueOf(t) + for idxT := 0; idxT < refValOfT.NumField(); idxT++ { + key := refValOfT.Type().Field(idxT).Name + v1 := refValOfT.Field(idxT).String() + + k, ok := keyOwnerAlias[key] + if !ok { return fmt.Errorf("invalid storage keys") } + addressKey, err := generateAddressHash(v1, k) + if err != nil { + return err + } + res[common.BytesToHex(append(storagePrefixKey, addressKey...))] = common.BytesToHex(validatorAccID) } } @@ -515,7 +623,7 @@ func generateAddressHash(accAddr, key string) ([]byte, error) { } func formatValue(kv *keyValue) (string, error) { switch { - case reflect.DeepEqual([]string{"Grandpa", "Authorities"}, kv.key): + case reflect.DeepEqual([]string{"grandpa", "authorities"}, kv.key): if kv.valueLen != nil { lenEnc, err := scale.Marshal(kv.valueLen) if err != nil { @@ -525,9 +633,9 @@ func formatValue(kv *keyValue) (string, error) { return fmt.Sprintf("0x01%x%v", lenEnc, kv.value), nil } return "", fmt.Errorf("error formatting value for grandpa authorities") - case reflect.DeepEqual([]string{"System", "code"}, kv.key): + case reflect.DeepEqual([]string{"system", "code"}, kv.key): return kv.value, nil - case reflect.DeepEqual([]string{"Sudo", "Key"}, kv.key): + case reflect.DeepEqual([]string{"sudo", "key"}, kv.key): return common.BytesToHex(crypto.PublicAddressToByteArray(common.Address(kv.value))), nil default: if kv.valueLen != nil { @@ -589,16 +697,16 @@ func BuildFromMap(m map[string][]byte, gen *Genesis) error { case GrandpaAuthoritiesKeyHex: // handle :grandpa_authorities // slice value since it was encoded starting with 0x01 - err := addAuthoritiesValues("grandpa", "authorities", crypto.Ed25519Type, v[1:], gen) + err := addAuthoritiesValues("grandpa", crypto.Ed25519Type, v[1:], gen) if err != nil { - return err + return fmt.Errorf("error adding grandpa authorities values: %v", err) } addRawValue(key, v, gen) case BABEAuthoritiesKeyHex: // handle Babe Authorities - err := addAuthoritiesValues("babe", "authorities", crypto.Sr25519Type, v, gen) + err := addAuthoritiesValues("babe", crypto.Sr25519Type, v, gen) if err != nil { - return err + return fmt.Errorf("error adding babe authorities values: %v", err) } addRawValue(key, v, gen) } @@ -614,17 +722,13 @@ func addRawValue(key string, value []byte, gen *Genesis) { } func addCodeValue(value []byte, gen *Genesis) { - if gen.Genesis.Runtime["system"] == nil { - gen.Genesis.Runtime["system"] = make(map[string]interface{}) + if gen.Genesis.Runtime.System == nil { + gen.Genesis.Runtime.System = new(System) } - gen.Genesis.Runtime["system"]["code"] = common.BytesToHex(value) + gen.Genesis.Runtime.System.Code = common.BytesToHex(value) } -func addAuthoritiesValues(k1, k2 string, kt crypto.KeyType, value []byte, gen *Genesis) error { - if gen.Genesis.Runtime[k1] == nil { - gen.Genesis.Runtime[k1] = make(map[string]interface{}) - } - +func addAuthoritiesValues(k1 string, kt crypto.KeyType, value []byte, gen *Genesis) error { var auths []types.AuthorityRaw err := scale.Unmarshal(value, &auths) if err != nil { @@ -636,6 +740,17 @@ func addAuthoritiesValues(k1, k2 string, kt crypto.KeyType, value []byte, gen *G return err } - gen.Genesis.Runtime[k1][k2] = authAddrs + switch k1 { + case "babe": + if gen.Genesis.Runtime.Babe == nil { + gen.Genesis.Runtime.Babe = new(babe) + } + gen.Genesis.Runtime.Babe.Authorities = authAddrs + case "grandpa": + if gen.Genesis.Runtime.Grandpa == nil { + gen.Genesis.Runtime.Grandpa = new(grandpa) + } + gen.Genesis.Runtime.Grandpa.Authorities = authAddrs + } return nil } diff --git a/lib/genesis/helpers_test.go b/lib/genesis/helpers_test.go index 53f13f95ce..dba922a268 100644 --- a/lib/genesis/helpers_test.go +++ b/lib/genesis/helpers_test.go @@ -5,11 +5,14 @@ package genesis import ( "encoding/json" + "math/big" "os" "path/filepath" "testing" + "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" "github.com/stretchr/testify/require" ) @@ -63,226 +66,80 @@ func TestNewGenesisFromJSON(t *testing.T) { expRaw := make(map[string]map[string]string) expRaw["top"] = make(map[string]string) - expRaw["top"]["0x3a636f6465"] = "0xfoo" + expRaw["top"]["0x3a636f6465"] = "0xfoo" // raw system code expRaw["top"]["0x3a6772616e6470615f617574686f726974696573"] = "0x010834602b88f60513f1c805d87ef52896934baf6a662bc37414dbdbf69356b1a6910000000000000000" // raw grandpa authorities - expRaw["top"]["0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d"] = "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0100000000000000" // raw babe authorities - expRaw["top"]["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x000000000000000000000000000000007aeb9049000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // raw system account + expRaw["top"]["0x014f204c006a2837deb5551ba5211d6ce887d1f35708af762efe7b709b5eff15"] = "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d0100000000000000" // raw babe authorities + expRaw["top"]["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x000000000000000000000000000000007aeb9049000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // Balances expRaw["top"][common.BytesToHex(common.UpgradedToDualRefKey)] = oneByte - expRaw["top"]["0x426e15054d267946093858132eb537f1a47a9ff5cd5bf4d848a80a0b1a947dc3"] = "0x00000000000000000000000000000000" // Society - expRaw["top"]["0x426e15054d267946093858132eb537f1ba7fb8745735dc3be2a2c61a72c39e78"] = "0x0101d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48" // Society - expRaw["top"]["0x426e15054d267946093858132eb537f1d0b4a3f7631f0c0e761898fe198211de"] = "0xe7030000" // Society // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1"] = "0x02000000" // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a"] = "0x80be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f" // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedcaac0a2cbf8e355f5ea6cb2de8727bfb0c"] = "0x54000000" // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedcab49a2738eeb30896aacb8b3fb46471bd"] = "0x01000000" // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedcac29a0310e1bb45d20cace77ccb62c97d"] = "0x00e1f505" // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3"] = "0x00e1f505" // Staking - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3"] = zeroByte // Staking - expRaw["top"]["0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f"] = "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f" // Session - expRaw["top"]["0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195066b8d48da86b869b6261626580d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session - expRaw["top"]["0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c9b0c13125732d276175646980d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session - expRaw["top"]["0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ed43a85541921049696d6f6e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session - expRaw["top"]["0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f5537bdb2a1f626b6772616e8088dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee"] = "0x" // Session - expRaw["top"]["0x11f3ba2e1cdd6d62f2ff9b5589e7ff81ba7fb8745735dc3be2a2c61a72c39e78"] = zeroByte // Instance1Collective - expRaw["top"]["0x5f3e4907f716ac89b6347d15ececedca28dccb559b95c40168a1b2696581b5a7"] = "0x00000000000000000000000000000000" // Staking.CanceledSlashPayout - expRaw["top"]["0x8985776095addd4789fccbce8ca77b23ba7fb8745735dc3be2a2c61a72c39e78"] = "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48" // Instance2Collective - expRaw["top"]["0x492a52699edf49c972c21db794cfcf57ba7fb8745735dc3be2a2c61a72c39e78"] = zeroByte // Instance1Membership + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707138e71612491192d68deab7e6f563fe1"] = "0x02000000" // Staking.ValidatorCount + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e7075579297f4dfb9609e7e4c2ebab9ce40a"] = "0x80be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f" // Staking.Invulnerables + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707b49a2738eeb30896aacb8b3fb46471bd"] = "0x01000000" // Staking.MinimumValidatorCount + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707c29a0310e1bb45d20cace77ccb62c97d"] = "0x00e1f505" // Staking.SlashRewardFraction + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707d32c6475a1afd11c5d3645883a408350"] = "0x00000000000000000000000000000000" // Staking.CanceledPayout + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707f7dad0317324aecae8744b87fc95f2f3"] = zeroByte // Staking.ForceEra + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707666fdcbb473985b3ac933d13f4acff8d"] = "0x00000000" // Staking.MinValidatorBond + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707cfcf8606ab1b2ac8c58f68f2551112be"] = "0x00" // Staking.MaxValidatorCount + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707ed441ceb81326c56263efbb60c95c2e4"] = "0x00000000" // Staking.MinNominatorBond + expRaw["top"]["0x7bbd1b4c54319a153cc9fdbd5792e707fda3863edefdd0f36f86ab168187e2c7"] = "0x00" // Staking.MaxNominatorCount + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b54c014e6bf8b8c2c011e7290b85696bb3e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f"] = "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f" // Session + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b5726380404683fc89e8233450c8aa1950f5537bdb2a1f626b6772616e8088dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee"] = "0x" // Session.Grandpa + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b5726380404683fc89e8233450c8aa195066b8d48da86b869b6261626580d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session.Babe + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b5726380404683fc89e8233450c8aa1950ed43a85541921049696d6f6e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session.ImOnline + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b5726380404683fc89e8233450c8aa195079b38849014a07307061726180d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session.ParaValidator + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b5726380404683fc89e8233450c8aa19504a8e42157609c6c86173676e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session.ParaAssignment + expRaw["top"]["0x3113eb570b4ee7c041d467c912beb8b5726380404683fc89e8233450c8aa1950c9b0c13125732d276175646980d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"] = "0x" // Session.AuthorityDiscovery + expRaw["top"]["0x26aa394eea5630e07c48ae0c9558cef7c21aab032aaa6e946ca50ad39ab66603"] = oneByte - // Contract - expRaw["top"]["0x4342193e496fab7ec59d615ed0dc5530d2d505c0e6f76fd7ce0796ebe187401c"] = "0x010000000001040000000002000000010000800000001000000000100000000100002000000000000800150600004c6b02004c8103009e1800000b1d0000160d0000651800006d2b00008a000000f5700100fdf602008e070000440600006e070000030600004f180000b528000091070000b60da70827080000590800006a0a0000ef070000560800004a0800008e080000f509000061090000dd090000a10a00009c090000e409000091090000650900001e0a0000120a0000ae09000099090000060a00006b2000000b1d000051200000221d000094090000ad090000b6090000160a0000660a0000fd090000260a0000440a0000d41a2a0000000000a0c729000000000092122900000000001ab5580000000000ba1c290000000000e000290000000000b0ef280000000000ee325c0000000000dec1280000000000ca07290000000000c07d4e00000000009c77140000000000303a7200000000000b01000000000000f0ab450000000000ff0200000000000060a21c270000000030078d31000000002635af09000000000ae164000000000038b18e0000000000b6b1cc0700000000890900000000000040036c00000000008ad6e21100000000de020000000000006e67cc080000000078f6110200000000e605000000000000acb1b50a00000000b24419090000000092579f08000000004702000000000000240300000000000016eaeb220000000055020000000000003503000000000000db0a000000000000f4802600000000006a100000000000006a9a280000000000220d0000000000004e9c2400000000001c0600000000000026832400000000001b06000000000000" expectedGenesis.Genesis = Fields{ Raw: expRaw, } // create human readable test genesis testGenesis := &Genesis{} - hrData := make(map[string]map[string]interface{}) - hrData["System"] = map[string]interface{}{"code": "0xfoo"} // system code entry - hrData["Babe"] = make(map[string]interface{}) - hrData["Babe"]["Authorities"] = []interface{}{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1} // babe authority data - hrData["Grandpa"] = make(map[string]interface{}) - hrData["Grandpa"]["Authorities"] = []interface{}{"5DFNv4Txc4b88qHqQ6GG4D646QcT4fN3jjS2G3r1PyZkfDut", 0} // grandpa authority data - hrData["Balances"] = make(map[string]interface{}) - hrData["Balances"]["balances"] = []interface{}{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1234234234} // balances + hrData := new(Runtime) + hrData.System = &System{Code: "0xfoo"} // system code entry + BabeAuth1 := types.AuthorityAsAddress{Address: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", Weight: 1} + hrData.Babe = &babe{Authorities: []types.AuthorityAsAddress{BabeAuth1}} // babe authority data + GrandpaAuth1 := types.AuthorityAsAddress{Address: "5DFNv4Txc4b88qHqQ6GG4D646QcT4fN3jjS2G3r1PyZkfDut", Weight: 0} + hrData.Grandpa = &grandpa{Authorities: []types.AuthorityAsAddress{GrandpaAuth1}} // grandpa authority data + balConf1 := balancesFields{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1234234234} + hrData.Balances = &balances{Balances: []balancesFields{balConf1}} // balances // Add test cases for new fields... - hrData["Society"] = make(map[string]interface{}) - hrData["Society"] = map[string]interface{}{ - "Pot": 0, - "MaxMembers": 999, - "Members": []interface{}{ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - }} - hrData["Staking"] = make(map[string]interface{}) - hrData["Staking"] = map[string]interface{}{ - "HistoryDepth": 84, - "ValidatorCount": 2, - "MinimumValidatorCount": 1, - "ForceEra": "NotForcing", - "SlashRewardFraction": 100000000, - "Invulnerables": []interface{}{ + zeroOfUint128 := scale.MustNewUint128(new(big.Int).SetUint64(0)) + + hrData.Staking = &staking{ + ValidatorCount: 2, + MinimumValidatorCount: 1, + Invulnerables: []string{ "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", }, - "CanceledSlashPayout": 0, + ForceEra: "NotForcing", + SlashRewardFraction: 100000000, + CanceledPayout: zeroOfUint128, + MinNominatorBond: 0, + MinValidatorBond: 0, + MaxValidatorCount: nil, + MaxNominatorCount: nil, } - hrData["Session"] = make(map[string]interface{}) - hrData["Session"] = map[string]interface{}{ - "NextKeys": []interface{}{ - []interface{}{ + hrData.Session = &session{ + NextKeys: []nextKey{ + { "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", - map[string]interface{}{ - "grandpa": "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", - "babe": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "im_online": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "authority_discovery": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + keyOwner{ + Grandpa: "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", + Babe: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ImOnline: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ParaValidator: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ParaAssignment: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + AuthorityDiscovery: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", }, }, }, } - hrData["Instance1Collective"] = make(map[string]interface{}) - hrData["Instance1Collective"] = map[string]interface{}{ - "Phantom": nil, - "Members": []interface{}{}, - } - hrData["Instance2Collective"] = make(map[string]interface{}) - hrData["Instance2Collective"] = map[string]interface{}{ - "Phantom": nil, - "Members": []interface{}{ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - }, - } - - hrData["Instance1Membership"] = make(map[string]interface{}) - hrData["Instance1Membership"] = map[string]interface{}{ - "Members": []interface{}{}, - "Phantom": nil, - } - - hrData["Contracts"] = make(map[string]interface{}) - hrData["Contracts"] = map[string]interface{}{ - "CurrentSchedule": map[string]interface{}{ - "version": 0, - "enable_println": true, - "limits": map[string]interface{}{ - "event_topics": 4, - "stack_height": 512, - "globals": 256, - "parameters": 128, - "memory_pages": 16, - "table_size": 4096, - "br_table_size": 256, - "subject_len": 32, - "code_size": 524288, - }, - "instruction_weights": map[string]interface{}{ - "i64const": 1557, - "i64load": 158540, - "i64store": 229708, - "select": 6302, - "if": 7435, - "br": 3350, - "br_if": 6245, - "br_table": 11117, - "br_table_per_entry": 138, - "call": 94453, - "call_indirect": 194301, - "call_indirect_per_param": 1934, - "local_get": 1604, - "local_set": 1902, - "local_tee": 1539, - "global_get": 6223, - "global_set": 10421, - "memory_current": 1937, - "memory_grow": 145165750, - "i64clz": 2087, - "i64ctz": 2137, - "i64popcnt": 2666, - "i64eqz": 2031, - "i64extendsi32": 2134, - "i64extendui32": 2122, - "i32wrapi64": 2190, - "i64eq": 2549, - "i64ne": 2401, - "i64lts": 2525, - "i64ltu": 2721, - "i64gts": 2460, - "i64gtu": 2532, - "i64les": 2449, - "i64leu": 2405, - "i64ges": 2590, - "i64geu": 2578, - "i64add": 2478, - "i64sub": 2457, - "i64mul": 2566, - "i64divs": 8299, - "i64divu": 7435, - "i64rems": 8273, - "i64remu": 7458, - "i64and": 2452, - "i64or": 2477, - "i64xor": 2486, - "i64shl": 2582, - "i64shrs": 2662, - "i64shru": 2557, - "i64rotl": 2598, - "i64rotr": 2628, - }, - "host_fn_weights": map[string]interface{}{ - "caller": 2759380, - "address": 2738080, - "gas_left": 2691730, - "balance": 5813530, - "value_transferred": 2694330, - "minimum_balance": 2687200, - "tombstone_deposit": 2682800, - "rent_allowance": 6042350, - "block_number": 2671070, - "now": 2688970, - "weight_to_fee": 5144000, - "gas": 1341340, - "input": 7486000, - "input_per_byte": 267, - "return": 4566000, - "return_per_byte": 767, - "terminate": 656188000, - "restore_to": 831326000, - "restore_to_per_delta": 162477350, - "random": 6611210, - "deposit_event": 9351480, - "deposit_event_per_topic": 130855350, - "deposit_event_per_byte": 2441, - "set_rent_allowance": 7078720, - "set_storage": 300078730, - "set_storage_per_byte": 734, - "clear_storage": 147613550, - "get_storage": 34731640, - "get_storage_per_byte": 1510, - "transfer": 179679660, - "call": 152650930, - "call_transfer_surcharge": 144660370, - "call_per_input_byte": 583, - "call_per_output_byte": 804, - "instantiate": 585886230, - "instantiate_per_input_byte": 597, - "instantiate_per_output_byte": 821, - "instantiate_per_salt_byte": 2779, - "hash_sha2_256": 2523380, - "hash_sha2_256_per_byte": 4202, - "hash_keccak_256": 2660970, - "hash_keccak_256_per_byte": 3362, - "hash_blake2_256": 2399310, - "hash_blake2_256_per_byte": 1564, - "hash_blake2_128": 2392870, - "hash_blake2_128_per_byte": 1563, - }, - }, - } - testGenesis.Genesis = Fields{ Runtime: hrData, } diff --git a/lib/genesis/pallet.go b/lib/genesis/pallet.go index e8e4ece6c1..5aa6e34950 100644 --- a/lib/genesis/pallet.go +++ b/lib/genesis/pallet.go @@ -3,166 +3,58 @@ package genesis -import "github.com/ChainSafe/gossamer/pkg/scale" +import ( + "encoding/json" -type contracts struct { - CurrentSchedule struct { - Version uint32 `json:"version"` - EnablePrintln bool `json:"enable_println"` - Limits struct { - EventTopics uint32 `json:"event_topics"` - StackHeight uint32 `json:"stack_height"` - Globals uint32 `json:"globals"` - Parameters uint32 `json:"parameters"` - MemoryPages uint32 `json:"memory_pages"` - TableSize uint32 `json:"table_size"` - BrTableSize uint32 `json:"br_table_size"` - SubjectLen uint32 `json:"subject_len"` - CodeSize uint32 `json:"code_size"` - } `json:"limits"` - InstructionWeights struct { - I64Const uint32 `json:"i64const"` - I64Load uint32 `json:"i64load"` - I64Store uint32 `json:"i64store"` - Select uint32 `json:"select"` - If uint32 `json:"if"` - Br uint32 `json:"br"` - BrIf uint32 `json:"br_if"` - BrTable uint32 `json:"br_table"` - BrTablePerEntry uint32 `json:"br_table_per_entry"` - Call uint32 `json:"call"` - CallIndirect uint32 `json:"call_indirect"` - CallIndirectPerParam uint32 `json:"call_indirect_per_param"` - LocalGet uint32 `json:"local_get"` - LocalSet uint32 `json:"local_set"` - LocalTee uint32 `json:"local_tee"` - GlobalGet uint32 `json:"global_get"` - GlobalSet uint32 `json:"global_set"` - MemoryCurrent uint32 `json:"memory_current"` - MemoryGrow uint32 `json:"memory_grow"` - I64Clz uint32 `json:"i64clz"` - I64Ctz uint32 `json:"i64ctz"` - I64Popcnt uint32 `json:"i64popcnt"` - I64Eqz uint32 `json:"i64eqz"` - I64Extendsi32 uint32 `json:"i64extendsi32"` - I64Extendui32 uint32 `json:"i64extendui32"` - I32Wrapi64 uint32 `json:"i32wrapi64"` - I64Eq uint32 `json:"i64eq"` - I64Ne uint32 `json:"i64ne"` - I64Lts uint32 `json:"i64lts"` - I64Ltu uint32 `json:"i64ltu"` - I64Gts uint32 `json:"i64gts"` - I64Gtu uint32 `json:"i64gtu"` - I64Les uint32 `json:"i64les"` - I64Leu uint32 `json:"i64leu"` - I64Ges uint32 `json:"i64ges"` - I64Geu uint32 `json:"i64geu"` - I64Add uint32 `json:"i64add"` - I64Sub uint32 `json:"i64sub"` - I64Mul uint32 `json:"i64mul"` - I64Divs uint32 `json:"i64divs"` - I64Divu uint32 `json:"i64divu"` - I64Rems uint32 `json:"i64rems"` - I64Remu uint32 `json:"i64remu"` - I64And uint32 `json:"i64and"` - I64Or uint32 `json:"i64or"` - I64Xor uint32 `json:"i64xor"` - I64Shl uint32 `json:"i64shl"` - I64Shrs uint32 `json:"i64shrs"` - I64Shru uint32 `json:"i64shru"` - I64Rotl uint32 `json:"i64rotl"` - I64Rotr uint32 `json:"i64rotr"` - } `json:"instruction_weights"` - HostFnWeights struct { - Caller uint64 `json:"caller"` - Address uint64 `json:"address"` - GasLeft uint64 `json:"gas_left"` - Balance uint64 `json:"balance"` - ValueTransferred uint64 `json:"value_transferred"` - MinimumBalance uint64 `json:"minimum_balance"` - TombstoneDeposit uint64 `json:"tombstone_deposit"` - RentAllowance uint64 `json:"rent_allowance"` - BlockNumber uint64 `json:"block_number"` - Now uint64 `json:"now"` - WeightToFee uint64 `json:"weight_to_fee"` - Gas uint64 `json:"gas"` - Input uint64 `json:"input"` - InputPerByte uint64 `json:"input_per_byte"` - Return uint64 `json:"return"` - ReturnPerByte uint64 `json:"return_per_byte"` - Terminate uint64 `json:"terminate"` - RestoreTo uint64 `json:"restore_to"` - RestoreToPerDelta uint64 `json:"restore_to_per_delta"` - Random uint64 `json:"random"` - DepositEvent uint64 `json:"deposit_event"` - DepositEventPerTopic uint64 `json:"deposit_event_per_topic"` - DepositEventPerByte uint64 `json:"deposit_event_per_byte"` - SetRentAllowance uint64 `json:"set_rent_allowance"` - SetStorage uint64 `json:"set_storage"` - SetStoragePerByte uint64 `json:"set_storage_per_byte"` - ClearStorage uint64 `json:"clear_storage"` - GetStorage uint64 `json:"get_storage"` - GetStoragePerByte uint64 `json:"get_storage_per_byte"` - Transfer uint64 `json:"transfer"` - Call uint64 `json:"call"` - CallTransferSurcharge uint64 `json:"call_transfer_surcharge"` - CallPerInputByte uint64 `json:"call_per_input_byte"` - CallPerOutputByte uint64 `json:"call_per_output_byte"` - Instantiate uint64 `json:"instantiate"` - InstantiatePerInputByte uint64 `json:"instantiate_per_input_byte"` - InstantiatePerOutputByte uint64 `json:"instantiate_per_output_byte"` - InstantiatePerSaltByte uint64 `json:"instantiate_per_salt_byte"` - HashSha2256 uint64 `json:"hash_sha2_256"` - HashSha2256PerByte uint64 `json:"hash_sha2_256_per_byte"` - HashKeccak256 uint64 `json:"hash_keccak_256"` - HashKeccak256PerByte uint64 `json:"hash_keccak_256_per_byte"` - HashBlake2256 uint64 `json:"hash_blake2_256"` - HashBlake2256PerByte uint64 `json:"hash_blake2_256_per_byte"` - HashBlake2128 uint64 `json:"hash_blake2_128"` - HashBlake2128PerByte uint64 `json:"hash_blake2_128_per_byte"` - } `json:"host_fn_weights"` - } `json:"CurrentSchedule"` -} - -type society struct { - Pot *scale.Uint128 `json:"Pot"` - MaxMembers uint32 `json:"MaxMembers"` - // TODO: figure out the correct encoding format of members field (#1867) - Members []string `json:"Members"` -} + "github.com/ChainSafe/gossamer/pkg/scale" +) +// according to chain/westend-local-spec.json type staking struct { - HistoryDepth uint32 `json:"HistoryDepth"` - ValidatorCount uint32 `json:"ValidatorCount"` - MinimumValidatorCount uint32 `json:"MinimumValidatorCount"` - Invulnerables []string `json:"Invulnerables"` - ForceEra string `json:"ForceEra"` - SlashRewardFraction uint32 `json:"SlashRewardFraction"` - CanceledSlashPayout *scale.Uint128 `json:"CanceledSlashPayout"` + ValidatorCount uint32 `json:"validatorCount"` + MinimumValidatorCount uint32 `json:"minimumValidatorCount"` + Invulnerables []string `json:"invulnerables"` + ForceEra string `json:"forceEra"` + SlashRewardFraction uint32 `json:"slashRewardFraction"` + CanceledPayout *scale.Uint128 `json:"canceledPayout"` + MinNominatorBond uint32 `json:"minNominatorBond"` + MinValidatorBond uint32 `json:"minValidatorBond"` + MaxValidatorCount *uint32 `json:"maxValidatorCount"` + MaxNominatorCount *uint32 `json:"maxNominatorCount"` // TODO: figure out below fields storage key. (#1868) - // Stakers [][]interface{} `json:"Stakers"` + // Stakers [][]interface{} `json:"stakers"` } type session struct { - NextKeys [][]interface{} `json:"NextKeys"` + NextKeys []nextKey `json:"nextKeys"` } -type instance1Collective struct { - Phantom interface{} `json:"Phantom"` - Members []interface{} `json:"Members"` +type nextKey struct { + AccountID1 string + AccountID2 string + KeyOwner keyOwner } -type instance2Collective struct { - Phantom interface{} `json:"Phantom"` - Members []interface{} `json:"Members"` +type keyOwner struct { + Grandpa string `json:"grandpa"` + Babe string `json:"babe"` + ImOnline string `json:"im_online"` + ParaValidator string `json:"para_validator"` + ParaAssignment string `json:"para_assignment"` + AuthorityDiscovery string `json:"authority_discovery"` } -type instance1Membership struct { - Phantom interface{} `json:"Phantom"` - Members []interface{} `json:"Members"` +func (b *nextKey) UnmarshalJSON(buf []byte) error { + tmp := []interface{}{&b.AccountID1, &b.AccountID2, &b.KeyOwner} + return json.Unmarshal(buf, &tmp) + } -type phragmenElection struct { - // TODO: figure out the correct encoding format of members data (#1866) - Members [][]interface{} `json:"Members"` +func (b nextKey) MarshalJSON() ([]byte, error) { + tmp := []interface{}{&b.AccountID1, &b.AccountID2, &b.KeyOwner} + buf, err := json.Marshal(tmp) + if err != nil { + return nil, err + } + return buf, nil } diff --git a/lib/genesis/pallet_test.go b/lib/genesis/pallet_test.go new file mode 100644 index 0000000000..db251c16c9 --- /dev/null +++ b/lib/genesis/pallet_test.go @@ -0,0 +1,55 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package genesis + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/stretchr/testify/require" +) + +var tcNextKey = []struct { + name string + jsonValue []byte + goValue nextKey +}{ + { + name: "test1", + jsonValue: common.MustHexToBytes("0x5b2235474e4a715450794e71414e426b55564d4e314c50507278586e466f7557586f6532774e536d6d456f4c637478695a59222c2235474e4a715450794e71414e426b55564d4e314c50507278586e466f7557586f6532774e536d6d456f4c637478695a59222c7b226772616e647061223a22354641396e51445667323637444564386d315a7970584c426e764e37534678597756376e6471535947694e3954547075222c2262616265223a223547727776614546357a58623236467a397263517044575335374374455248704e6568584350634e6f48474b75745159222c22696d5f6f6e6c696e65223a223547727776614546357a58623236467a397263517044575335374374455248704e6568584350634e6f48474b75745159222c22706172615f76616c696461746f72223a223547727776614546357a58623236467a397263517044575335374374455248704e6568584350634e6f48474b75745159222c22706172615f61737369676e6d656e74223a223547727776614546357a58623236467a397263517044575335374374455248704e6568584350634e6f48474b75745159222c22617574686f726974795f646973636f76657279223a223547727776614546357a58623236467a397263517044575335374374455248704e6568584350634e6f48474b75745159227d5d"), //nolint:lll + goValue: nextKey{ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + keyOwner{ + Grandpa: "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", + Babe: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ImOnline: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ParaValidator: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + ParaAssignment: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + AuthorityDiscovery: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + }, + }, + }, +} + +func TestNextKeyMarshal(t *testing.T) { + for _, tt := range tcNextKey { + t.Run(tt.name, func(t *testing.T) { + marshalledValue, err := tt.goValue.MarshalJSON() + require.NoError(t, err, "couldn't marshal nextKey") + require.EqualValues(t, tt.jsonValue, marshalledValue, "nextKey doesn't match") + }) + } +} + +func TestNextKeyUnmarshal(t *testing.T) { + for _, tt := range tcNextKey { + t.Run(tt.name, func(t *testing.T) { + var nk nextKey + err := nk.UnmarshalJSON(tt.jsonValue) + require.NoError(t, err, "couldn't unmarshal nextKey") + require.EqualValues(t, tt.goValue, nk, "nextKey doesn't match") + }) + } +} diff --git a/pkg/scale/uint128.go b/pkg/scale/uint128.go index b096fd82b3..5149c08dcb 100644 --- a/pkg/scale/uint128.go +++ b/pkg/scale/uint128.go @@ -161,3 +161,8 @@ func (u *Uint128) UnmarshalJSON(data []byte) error { u.Lower = dec.Lower return nil } + +// MarshalJSON converts Uint128 to []byte. +func (u Uint128) MarshalJSON() ([]byte, error) { + return []byte(u.String()), nil +}