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