Skip to content

Commit

Permalink
Merge pull request #3713 from oasisprotocol/tjanez/canonical-genesis-…
Browse files Browse the repository at this point in the history
…file-20.12.x

[BACKPORT/20.12.x] Improvement to genesis file handling
  • Loading branch information
tjanez authored Feb 19, 2021
2 parents 4aa654e + 1846a51 commit fbdf4eb
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 41 deletions.
4 changes: 4 additions & 0 deletions .changelog/3709.feature.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
go/genesis/api: Update `WriteFileJSON()` to create files in the canonical form

Consequentially, all its users (most notably the dump genesis halt hook) now
produce genesis files in the canonical form.
4 changes: 4 additions & 0 deletions .changelog/3709.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
go/genesis/api: Add `CanonicalJSON()` method to `Document` type

It can be used to obtain the canonical form of the genesis document
serialized into a file.
9 changes: 4 additions & 5 deletions docs/consensus/genesis.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ where:

## Genesis File

A genesis file is a JSON document corresponding to a serialized genesis
document.
A genesis file is a JSON file corresponding to a serialized genesis document.

{% hint style="info" %}
For a high-level overview of the genesis file, its various sections and
parameters and the parameter values that will be used for Oasis Network Mainnet
launch, see: [Genesis File Overview].
For a high-level overview of the genesis file, its sections, parameters and
the parameter values that are used for the Oasis Network, see:
[Genesis File Overview].
{% endhint %}

[Genesis File Overview]:
Expand Down
19 changes: 16 additions & 3 deletions go/genesis/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,27 @@ func (d *Document) SetChainContext() {
signature.SetChainContext(d.ChainContext())
}

// WriteFileJSON writes the genesis document into a JSON file.
// CanonicalJSON returns the canonical form of the genesis document serialized
// into a file.
//
// This is a pretty-printed JSON file with 2-space indents following Go
// encoding/json package's JSON marshalling rules.
func (d *Document) CanonicalJSON() ([]byte, error) {
canonJSON, err := json.MarshalIndent(d, "", " ")
if err != nil {
return []byte{}, fmt.Errorf("CanonicalJSON: failed to marshal genesis document: %w", err)
}
return canonJSON, nil
}

// WriteFileJSON writes the canonical form of genesis document into a file.
func (d *Document) WriteFileJSON(filename string) error {
docJSON, err := json.Marshal(d)
canonJSON, err := d.CanonicalJSON()
if err != nil {
return err
}

if err = ioutil.WriteFile(filename, docJSON, filePerm); err != nil {
if err = ioutil.WriteFile(filename, canonJSON, filePerm); err != nil {
return fmt.Errorf("WriteFileJSON: failed to write genesis file: %w", err)
}
return nil
Expand Down
9 changes: 5 additions & 4 deletions go/oasis-node/cmd/debug/fixgenesis/fixgenesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,15 @@ func doFixGenesis(cmd *cobra.Command, args []string) {
if shouldClose {
defer w.Close()
}
if raw, err = json.Marshal(newDoc); err != nil {
logger.Error("failed to marshal fixed genesis document into JSON",
canonJSON, err := newDoc.CanonicalJSON()
if err != nil {
logger.Error("failed to get canonical form of fixed genesis file",
"err", err,
)
os.Exit(1)
}
if _, err = w.Write(raw); err != nil {
logger.Error("failed to write new genesis file",
if _, err = w.Write(canonJSON); err != nil {
logger.Error("failed to write fixed genesis file",
"err", err,
)
os.Exit(1)
Expand Down
58 changes: 30 additions & 28 deletions go/oasis-node/cmd/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,15 @@ func doInitGenesis(cmd *cobra.Command, args []string) {
return
}

b, _ := json.MarshalIndent(doc, "", " ")
if err := ioutil.WriteFile(f, b, 0o600); err != nil {
logger.Error("failed to save generated genesis document",
canonJSON, err := doc.CanonicalJSON()
if err != nil {
logger.Error("failed to get canonical form of genesis file",
"err", err,
)
return
}
if err := ioutil.WriteFile(f, canonJSON, 0o600); err != nil {
logger.Error("failed to write genesis file",
"err", err,
)
return
Expand Down Expand Up @@ -581,14 +587,14 @@ func doDumpGenesis(cmd *cobra.Command, args []string) {
defer w.Close()
}

data, err := json.MarshalIndent(doc, "", " ")
canonJSON, err := doc.CanonicalJSON()
if err != nil {
logger.Error("failed to marshal genesis document into JSON",
logger.Error("failed to get canonical form of genesis file",
"err", err,
)
os.Exit(1)
}
if _, err = w.Write(data); err != nil {
if _, err = w.Write(canonJSON); err != nil {
logger.Error("failed to write genesis file",
"err", err,
)
Expand All @@ -607,46 +613,42 @@ func doCheckGenesis(cmd *cobra.Command, args []string) {
logger.Error("failed to open genesis file", "err", err)
os.Exit(1)
}
// NOTE: The genesis document sanity checks are performed inside the
// NewFileProvider() function.
doc, err := provider.GetGenesisDocument()
if err != nil {
logger.Error("failed to get genesis document", "err", err)
os.Exit(1)
}

err = doc.SanityCheck()
if err != nil {
logger.Error("genesis document sanity check failed", "err", err)
os.Exit(1)
}

// Load raw genesis file.
rawFile, err := ioutil.ReadFile(filename)
// Load genesis file to check if it is in the canonical form.
actualGenesis, err := ioutil.ReadFile(filename)
if err != nil {
logger.Error("failed to read genesis file:", "err", err)
os.Exit(1)
}
// Create a marshalled genesis document in the canonical form with 2 space indents.
rawCanonical, err := json.MarshalIndent(doc, "", " ")
// Get canonical form of the genesis document serialized into a file.
canonicalJSON, err := doc.CanonicalJSON()
if err != nil {
logger.Error("failed to marshal genesis document", "err", err)
logger.Error("failed to get canonical form of genesis file", "err", err)
os.Exit(1)
}
// Genesis file should equal the canonical form.
if !bytes.Equal(rawFile, rawCanonical) {
fileLines := strings.Split(string(rawFile), "\n")
if len(fileLines) > checkNotCanonicalLines {
fileLines = fileLines[:checkNotCanonicalLines]
// Actual genesis file should equal the canonical form.
if !bytes.Equal(actualGenesis, canonicalJSON) {
actualLines := strings.Split(string(actualGenesis), "\n")
if len(actualLines) > checkNotCanonicalLines {
actualLines = actualLines[:checkNotCanonicalLines]
}
canonicalLines := strings.Split(string(rawCanonical), "\n")
canonicalLines := strings.Split(string(canonicalJSON), "\n")
if len(canonicalLines) > checkNotCanonicalLines {
canonicalLines = canonicalLines[:checkNotCanonicalLines]
}
logger.Error("genesis document is not marshalled in the canonical form")
logger.Error("genesis file is not in the canonical form")
fmt.Fprintf(os.Stderr,
"Error: genesis document is not marshalled in the canonical form:\n"+
"\nActual marshalled genesis document (trimmed):\n%s\n\n... trimmed ...\n"+
"\nExpected marshalled genesis document (trimmed):\n%s\n\n... trimmed ...\n",
strings.Join(fileLines, "\n"), strings.Join(canonicalLines, "\n"),
"Error: genesis file is not in the canonical form:\n"+
"\nActual genesis file (trimmed):\n%s\n\n... trimmed ...\n"+
"\nExpected genesis file (trimmed):\n%s\n\n... trimmed ...\n",
strings.Join(actualLines, "\n"), strings.Join(canonicalLines, "\n"),
)
os.Exit(1)
}
Expand Down
2 changes: 1 addition & 1 deletion go/oasis-test-runner/scenario/e2e/genesis_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (s *genesisFileImpl) Run(childEnv *env.Env) error {
return fmt.Errorf("e2e/genesis-file: creating uncanonical genesis file failed: %w", err)
}
err = s.runGenesisCheckCmd(childEnv, uncanonicalPath)
expectedError := "genesis document is not marshalled in the canonical form"
expectedError := "genesis file is not in the canonical form"
switch {
case err == nil:
return fmt.Errorf("e2e/genesis-file: running genesis check for an uncanonical genesis file should fail")
Expand Down

0 comments on commit fbdf4eb

Please sign in to comment.