Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Migrate from substrate to polkadot-sdk repo + Implement genesis builder #313

Merged
merged 17 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
[submodule "goscale"]
path = goscale
url = git@github.com:LimeChain/goscale.git
[submodule "substrate"]
path = substrate
url = git@github.com:LimeChain/substrate.git
[submodule "polkadot-sdk"]
path = polkadot-sdk
url = git@github.com:LimeChain/polkadot-sdk.git
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ build-dev: build-tinygo
WASMOPT="$(CURRENT_DIR)/$(WASMOPT_PATH)" $(TINYGO_BUILD_COMMAND) -o=$(BUILD_PATH) runtime/runtime.go

start-network:
cp build/runtime.wasm substrate/bin/node-template/runtime.wasm; \
cd substrate/bin/node-template; \
cp build/runtime.wasm polkadot-sdk/substrate/bin/node-template/runtime.wasm; \
cd polkadot-sdk/substrate/bin/node-template/node; \
cargo build --release; \
cd ../..; \
WASMTIME_BACKTRACE_DETAILS=1 RUST_LOG=runtime=trace ./target/release/node-template --dev --execution Wasm
cd ../../../..; \
WASMTIME_BACKTRACE_DETAILS=1 RUST_LOG=runtime=trace ./target/release/node-template --dev --execution=wasm

test: test-unit test-integration

Expand Down
95 changes: 95 additions & 0 deletions api/genesis_builder/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package genesisbuilder

import (
"bytes"
"fmt"
"strings"

sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/primitives/hashing"
"github.com/LimeChain/gosemble/primitives/log"
primitives "github.com/LimeChain/gosemble/primitives/types"
"github.com/LimeChain/gosemble/utils"
)

const (
ApiModuleName = "GenesisBuilder"
apiVersion = 1
)

type GenesisBuilder interface {
CreateDefaultConfig() ([]byte, error)
BuildConfig(config []byte) error
}

type Module struct {
modules []primitives.Module
memUtils utils.WasmMemoryTranslator
}

func New(modules []primitives.Module) Module {
return Module{
modules: modules,
memUtils: utils.NewMemoryTranslator(),
}
}

func (m Module) Name() string {
return ApiModuleName
}

func (m Module) Item() primitives.ApiItem {
hash := hashing.MustBlake2b8([]byte(ApiModuleName))
return primitives.NewApiItem(hash, apiVersion)
}

func (m Module) CreateDefaultConfig() int64 {
gcs := []string{}
for _, m := range m.modules {
genesisBuilder, ok := m.(GenesisBuilder)
if !ok {
continue

Check warning on line 51 in api/genesis_builder/module.go

View check run for this annotation

Codecov / codecov/patch

api/genesis_builder/module.go#L51

Added line #L51 was not covered by tests
}

gcJsonBytes, err := genesisBuilder.CreateDefaultConfig()
if err != nil {
log.Critical(err.Error())
}

// gcJsonBytes[1:len(gcJsonBytes)-1] trims first and last characters which represent start and end of the json
// CreateDefaultConfig returns a valid json (e.g. {"system":{}}), and here we need it as a json field
gcs = append(gcs, string(gcJsonBytes[1:len(gcJsonBytes)-1]))
}

gcJson := []byte(fmt.Sprintf("{%s}", strings.Join(gcs, ",")))

return m.memUtils.BytesToOffsetAndSize(sc.BytesToSequenceU8(gcJson).Bytes())
}

func (m Module) BuildConfig(dataPtr int32, dataLen int32) int64 {
gcJsonBytes := m.memUtils.GetWasmMemorySlice(dataPtr, dataLen)
gcDecoded, err := sc.DecodeSequence[sc.U8](bytes.NewBuffer(gcJsonBytes))
if err != nil {
log.Critical(err.Error())
}

Check warning on line 74 in api/genesis_builder/module.go

View check run for this annotation

Codecov / codecov/patch

api/genesis_builder/module.go#L73-L74

Added lines #L73 - L74 were not covered by tests

gcDecodedBytes := sc.SequenceU8ToBytes(gcDecoded)

for _, m := range m.modules {
genesisBuilder, ok := m.(GenesisBuilder)
if !ok {
continue

Check warning on line 81 in api/genesis_builder/module.go

View check run for this annotation

Codecov / codecov/patch

api/genesis_builder/module.go#L81

Added line #L81 was not covered by tests
}

if err := genesisBuilder.BuildConfig(gcDecodedBytes); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that we unmarshal the decoded bytes every time in each module. Thoughts on unmarshalling only once here, and pass the struct to the modules?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about it, but I'm not sure if that's achievable without predefining all available genesis configs. I think we should keep it dynamic, especially for when we start supporting custom pallets. Let me know if you have any ideas

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every module configuration must be included in the bytes. Currently, you can execute the function without having balances

log.Critical(err.Error())
}
}

return m.memUtils.BytesToOffsetAndSize([]byte{0})
}

// todo metadata
// func (m Module) Metadata() primitives.RuntimeApiMetadata {
// return primitives.RuntimeApiMetadata{}
// }
92 changes: 92 additions & 0 deletions api/genesis_builder/module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package genesisbuilder

import (
"errors"
"testing"

"github.com/ChainSafe/gossamer/lib/common"
sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/mocks"
"github.com/LimeChain/gosemble/primitives/types"
"github.com/stretchr/testify/assert"
)

var (
genesis = []byte("{\"module\":{\"field\":[]}}")
genesisSequence = sc.BytesToSequenceU8(genesis).Bytes()
)

var (
target Module
mockModule *mocks.Module
mockMemoryUtils *mocks.MemoryTranslator
)

func setup() {
mockModule = new(mocks.Module)
mockMemoryUtils = new(mocks.MemoryTranslator)

target = New([]types.Module{mockModule})
target.memUtils = mockMemoryUtils
}

func Test_Module_Name(t *testing.T) {
setup()
result := target.Name()
assert.Equal(t, ApiModuleName, result)
}

func Test_Module_Item(t *testing.T) {
setup()
hexName := common.MustBlake2b8([]byte(ApiModuleName))
expect := types.NewApiItem(hexName, apiVersion)
result := target.Item()
assert.Equal(t, expect, result)
}

func Test_CreateDefaultConfig(t *testing.T) {
setup()
mockModule.On("CreateDefaultConfig").Return(genesis, nil)
mockMemoryUtils.On("BytesToOffsetAndSize", genesisSequence).Return(int64(0))

target.CreateDefaultConfig()

mockModule.AssertCalled(t, "CreateDefaultConfig")
mockMemoryUtils.AssertCalled(t, "BytesToOffsetAndSize", genesisSequence)
}

func Test_CreateDefaultConfig_Error(t *testing.T) {
setup()
mockModule.On("CreateDefaultConfig").Return(genesis, errors.New("err"))
mockMemoryUtils.On("BytesToOffsetAndSize", genesisSequence).Return(int64(0))

assert.PanicsWithValue(t,
errors.New("err").Error(),
func() { target.CreateDefaultConfig() },
)
}

func Test_BuildConfig(t *testing.T) {
setup()
mockModule.On("BuildConfig", genesis).Return(nil)
mockMemoryUtils.On("GetWasmMemorySlice", int32(0), int32(0)).Return(genesisSequence)
mockMemoryUtils.On("BytesToOffsetAndSize", []byte{0}).Return(int64(0))

target.BuildConfig(0, 0)

mockMemoryUtils.AssertCalled(t, "GetWasmMemorySlice", int32(0), int32(0))
mockModule.AssertCalled(t, "BuildConfig", genesis)
mockMemoryUtils.AssertCalled(t, "BytesToOffsetAndSize", []byte{0})
}

func Test_BuildConfig_Error(t *testing.T) {
setup()
mockModule.On("BuildConfig", genesis).Return(errors.New("err"))
mockMemoryUtils.On("GetWasmMemorySlice", int32(0), int32(0)).Return(genesisSequence)
mockMemoryUtils.On("BytesToOffsetAndSize", []byte{0}).Return(int64(0))

assert.PanicsWithValue(t,
errors.New("err").Error(),
func() { target.BuildConfig(0, 0) },
)
}
Binary file modified build/node_template_runtime.wasm
100644 → 100755
Binary file not shown.
Binary file modified build/runtime.wasm
Binary file not shown.
90 changes: 90 additions & 0 deletions frame/aura/genesis_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package aura

import (
"encoding/json"
"errors"

sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/primitives/types"
"github.com/vedhavyas/go-subkey"
)

var (
errAuthoritiesAlreadyInitialized = errors.New("Authorities are already initialized!")
errAuthoritiesExceedMaxAuthorities = errors.New("Initial authority set must be less than MaxAuthorities")
)

type GenesisConfig struct {
Authorities sc.Sequence[types.Sr25519PublicKey]
}

type genesisConfigJsonStruct struct {
AuraGenesisConfig struct {
Authorities []string `json:"authorities"`
} `json:"aura"`
}

func (gc *GenesisConfig) UnmarshalJSON(data []byte) error {
gcJson := genesisConfigJsonStruct{}

if err := json.Unmarshal(data, &gcJson); err != nil {
return err
}

Check warning on line 32 in frame/aura/genesis_builder.go

View check run for this annotation

Codecov / codecov/patch

frame/aura/genesis_builder.go#L31-L32

Added lines #L31 - L32 were not covered by tests

addrExists := map[string]bool{}
for _, a := range gcJson.AuraGenesisConfig.Authorities {
if addrExists[a] {
continue
}

_, pubKeyBytes, err := subkey.SS58Decode(a)
if err != nil {
return err
}

pubKey, err := types.NewSr25519PublicKey(sc.BytesToSequenceU8(pubKeyBytes)...)
if err != nil {
return err
}

Check warning on line 48 in frame/aura/genesis_builder.go

View check run for this annotation

Codecov / codecov/patch

frame/aura/genesis_builder.go#L47-L48

Added lines #L47 - L48 were not covered by tests

gc.Authorities = append(gc.Authorities, pubKey)
addrExists[a] = true
}

return nil
}

func (m Module) CreateDefaultConfig() ([]byte, error) {
gc := genesisConfigJsonStruct{}
gc.AuraGenesisConfig.Authorities = []string{}

return json.Marshal(gc)
}

func (m Module) BuildConfig(config []byte) error {
gc := GenesisConfig{}
if err := json.Unmarshal(config, &gc); err != nil {
return err
}

if len(gc.Authorities) == 0 {
return nil
}

totalAuthorities, err := m.storage.Authorities.DecodeLen()
if err != nil {
return err
}

if totalAuthorities.HasValue {
return errAuthoritiesAlreadyInitialized
}

if len(gc.Authorities) > int(m.config.MaxAuthorities) {
return errAuthoritiesExceedMaxAuthorities
}

m.storage.Authorities.Put(gc.Authorities)

return nil
}
Loading