From d82b2dad92e386b9a1d53414e740a843b085b3d0 Mon Sep 17 00:00:00 2001
From: noot <36753753+noot@users.noreply.github.com>
Date: Tue, 23 Mar 2021 10:00:56 -0400
Subject: [PATCH] feat: cmd: implement import-runtime subcommand (#1483)
---
cmd/gossamer/import_runtime.go | 48 +++++++++++++++++++++++++++
cmd/gossamer/import_runtime_test.go | 50 +++++++++++++++++++++++++++++
cmd/gossamer/main.go | 34 ++++++++++----------
lib/genesis/genesis.go | 2 +-
lib/genesis/helpers.go | 34 +++++++++++++-------
5 files changed, 138 insertions(+), 30 deletions(-)
create mode 100644 cmd/gossamer/import_runtime.go
create mode 100644 cmd/gossamer/import_runtime_test.go
diff --git a/cmd/gossamer/import_runtime.go b/cmd/gossamer/import_runtime.go
new file mode 100644
index 0000000000..bc1b53a68a
--- /dev/null
+++ b/cmd/gossamer/import_runtime.go
@@ -0,0 +1,48 @@
+// Copyright 2019 ChainSafe Systems (ON) Corp.
+// This file is part of gossamer.
+//
+// The gossamer library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The gossamer library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the gossamer library. If not, see .
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+
+ "github.com/ChainSafe/gossamer/lib/genesis"
+)
+
+var defaultGenesisSpecPath = "./chain/gssmr/genesis.json"
+
+func createGenesisWithRuntime(fp string) (string, error) {
+ runtime, err := ioutil.ReadFile(filepath.Clean(fp))
+ if err != nil {
+ return "", err
+ }
+
+ genesis, err := genesis.NewGenesisSpecFromJSON(defaultGenesisSpecPath)
+ if err != nil {
+ return "", err
+ }
+
+ genesis.Genesis.Runtime["system"]["code"] = fmt.Sprintf("0x%x", runtime)
+ bz, err := json.MarshalIndent(genesis, "", "\t")
+ if err != nil {
+ return "", err
+ }
+
+ return string(bz), nil
+}
diff --git a/cmd/gossamer/import_runtime_test.go b/cmd/gossamer/import_runtime_test.go
new file mode 100644
index 0000000000..c1434a9fe6
--- /dev/null
+++ b/cmd/gossamer/import_runtime_test.go
@@ -0,0 +1,50 @@
+// Copyright 2019 ChainSafe Systems (ON) Corp.
+// This file is part of gossamer.
+//
+// The gossamer library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The gossamer library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the gossamer library. If not, see .
+
+package main
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/ChainSafe/gossamer/lib/common"
+ "github.com/ChainSafe/gossamer/lib/genesis"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestCreateGenesisWithRuntime(t *testing.T) {
+ defaultGenesisSpecPath = "../../chain/gssmr/genesis.json"
+
+ testCode := []byte("somecode")
+ testHex := common.BytesToHex(testCode)
+ testFile, err := ioutil.TempFile("", "testcode-*.wasm")
+ require.NoError(t, err)
+ defer os.Remove(testFile.Name())
+
+ err = ioutil.WriteFile(testFile.Name(), testCode, 0777)
+ require.NoError(t, err)
+
+ out, err := createGenesisWithRuntime(testFile.Name())
+ require.NoError(t, err)
+
+ g := new(genesis.Genesis)
+ err = json.Unmarshal([]byte(out), g)
+ require.NoError(t, err)
+ require.Equal(t, testHex, g.Genesis.Runtime["system"]["code"].(string))
+}
diff --git a/cmd/gossamer/main.go b/cmd/gossamer/main.go
index e22b432e8d..101027b676 100644
--- a/cmd/gossamer/main.go
+++ b/cmd/gossamer/main.go
@@ -19,9 +19,7 @@ package main
import (
"errors"
"fmt"
- "io/ioutil"
"os"
- "path/filepath"
"github.com/ChainSafe/gossamer/dot"
"github.com/ChainSafe/gossamer/lib/keystore"
@@ -71,7 +69,7 @@ var (
"\tTo import a keystore file: gossamer account --import=path/to/file\n" +
"\tTo list keys: gossamer account --list",
}
- // initCommand defines the "init" subcommand (ie, `gossamer init`)
+ // buildSpecCommand creates a raw genesis file from a human readable genesis file.
buildSpecCommand = cli.Command{
Action: FixFlagOrder(buildSpecAction),
Name: "build-spec",
@@ -81,18 +79,20 @@ var (
Category: "BUILD-SPEC",
Description: "The build-spec command outputs current genesis JSON data.\n" +
"\tUsage: gossamer build-spec\n" +
- "\tTo generate raw genesis file: gossamer build-spec --raw",
+ "\tTo generate raw genesis file from default: gossamer build-spec --raw > genesis-raw.json" +
+ "\tTo generate raw genesis file from specific genesis file: gossamer build-spec --raw --genesis genesis.json > genesis-raw.json",
}
- // TODO: update this to put the wasm into a genesis file
- wasmToHexCommand = cli.Command{
- Action: FixFlagOrder(wasmToHexAction),
- Name: "convert-wasm",
- Usage: "Converts a .wasm file to a hex string to be used in a genesis file",
+
+ // importRuntime generates a genesis file given a .wasm runtime binary.
+ importRuntimeCommand = cli.Command{
+ Action: FixFlagOrder(importRuntimeAction),
+ Name: "import-runtime",
+ Usage: "Generates a genesis file given a .wasm runtime binary",
ArgsUsage: "",
Flags: RootFlags,
- Category: "CONVERT-WASM",
- Description: "The convert-wasm command converts a .wasm file to a hex string to be used in a genesis file.\n" +
- "\tUsage: gossamer convert-wasm runtime.wasm\n",
+ Category: "IMPORT-RUNTIME",
+ Description: "The import-runtime command generates a genesis file given a .wasm runtime binary.\n" +
+ "\tUsage: gossamer import-runtime runtime.wasm > genesis.json\n",
}
importStateCommand = cli.Command{
@@ -121,7 +121,7 @@ func init() {
initCommand,
accountCommand,
buildSpecCommand,
- wasmToHexCommand,
+ importRuntimeCommand,
importStateCommand,
}
app.Flags = RootFlags
@@ -163,20 +163,20 @@ func importStateAction(ctx *cli.Context) error {
return dot.ImportState(cfg.Global.BasePath, stateFP, headerFP, uint64(firstSlot))
}
-// wasmToHexAction converts a .wasm file to a hex string and outputs it to stdout
-func wasmToHexAction(ctx *cli.Context) error {
+// importRuntimeAction generates a genesis file given a .wasm runtime binary.
+func importRuntimeAction(ctx *cli.Context) error {
arguments := ctx.Args()
if len(arguments) == 0 {
return fmt.Errorf("no args provided, please provide wasm file")
}
fp := arguments[0]
- bytes, err := ioutil.ReadFile(filepath.Clean(fp))
+ out, err := createGenesisWithRuntime(fp)
if err != nil {
return err
}
- fmt.Printf("0x%x", bytes)
+ fmt.Println(out)
return nil
}
diff --git a/lib/genesis/genesis.go b/lib/genesis/genesis.go
index 13fea973e5..2f0355f9f8 100644
--- a/lib/genesis/genesis.go
+++ b/lib/genesis/genesis.go
@@ -49,7 +49,7 @@ type Data struct {
// Fields stores genesis raw data, and human readable runtime data
type Fields struct {
- Raw map[string]map[string]string `json:"raw"`
+ Raw map[string]map[string]string `json:"raw,omitempty"`
Runtime map[string]map[string]interface{} `json:"runtime,omitempty"`
}
diff --git a/lib/genesis/helpers.go b/lib/genesis/helpers.go
index b47842b12e..ea52aa5ee3 100644
--- a/lib/genesis/helpers.go
+++ b/lib/genesis/helpers.go
@@ -111,37 +111,47 @@ func trimGenesisAuthority(g *Genesis, authCount int) {
// NewGenesisFromJSON parses Human Readable JSON formatted genesis file.Name. If authCount > 0,
// then it keeps only `authCount` number of authorities for babe and grandpa.
func NewGenesisFromJSON(file string, authCount int) (*Genesis, error) {
- fp, err := filepath.Abs(file)
+ g, err := NewGenesisSpecFromJSON(file)
if err != nil {
return nil, err
}
- data, err := ioutil.ReadFile(filepath.Clean(fp))
+ if authCount > 0 {
+ trimGenesisAuthority(g, authCount)
+ }
+
+ grt := g.Genesis.Runtime
+ res, err := buildRawMap(grt)
if err != nil {
return nil, err
}
- g := new(Genesis)
+ g.Genesis.Raw = make(map[string]map[string]string)
+ g.Genesis.Raw["top"] = res
- err = json.Unmarshal(data, g)
+ return g, err
+}
+
+// NewGenesisSpecFromJSON returns a new Genesis (without raw fields) from a human-readable genesis file
+func NewGenesisSpecFromJSON(file string) (*Genesis, error) {
+ fp, err := filepath.Abs(file)
if err != nil {
return nil, err
}
- if authCount > 0 {
- trimGenesisAuthority(g, authCount)
+ data, err := ioutil.ReadFile(filepath.Clean(fp))
+ if err != nil {
+ return nil, err
}
- grt := g.Genesis.Runtime
- res, err := buildRawMap(grt)
+ g := new(Genesis)
+
+ err = json.Unmarshal(data, g)
if err != nil {
return nil, err
}
- g.Genesis.Raw = make(map[string]map[string]string)
- g.Genesis.Raw["top"] = res
-
- return g, err
+ return g, nil
}
// keyValue struct to hold data regarding entry