Skip to content

Commit

Permalink
feat: add genesis txs subcommand (#1278)
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkovicmilos authored Oct 25, 2023
1 parent 347f3c0 commit 505ed60
Show file tree
Hide file tree
Showing 8 changed files with 739 additions and 0 deletions.
1 change: 1 addition & 0 deletions gno.land/cmd/genesis/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func newRootCmd(io *commands.IO) *commands.Command {
newGenerateCmd(io),
newValidatorCmd(io),
newVerifyCmd(io),
newTxsCmd(io),
)

return cmd
Expand Down
43 changes: 43 additions & 0 deletions gno.land/cmd/genesis/txs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"flag"

"github.com/gnolang/gno/tm2/pkg/commands"
)

type txsCfg struct {
genesisPath string
}

// newTxsCmd creates the genesis txs subcommand
func newTxsCmd(io *commands.IO) *commands.Command {
cfg := &txsCfg{}

cmd := commands.NewCommand(
commands.Metadata{
Name: "txs",
ShortUsage: "txs <subcommand> [flags]",
ShortHelp: "Manages the initial genesis transactions",
LongHelp: "Manages genesis transactions through input files",
},
cfg,
commands.HelpExec,
)

cmd.AddSubCommands(
newTxsAddCmd(cfg, io),
newTxsRemoveCmd(cfg, io),
)

return cmd
}

func (c *txsCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.genesisPath,
"genesis-path",
"./genesis.json",
"the path to the genesis.json",
)
}
146 changes: 146 additions & 0 deletions gno.land/cmd/genesis/txs_add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package main

import (
"bufio"
"context"
"errors"
"flag"
"fmt"
"io"
"os"

"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/std"
)

var (
errInvalidTxsFile = errors.New("unable to open transactions file")
errTxsParsingAborted = errors.New("transaction parsing aborted")
)

type txsAddCfg struct {
rootCfg *txsCfg

parseExport string
}

// newTxsAddCmd creates the genesis txs add subcommand
func newTxsAddCmd(txsCfg *txsCfg, io *commands.IO) *commands.Command {
cfg := &txsAddCfg{
rootCfg: txsCfg,
}

return commands.NewCommand(
commands.Metadata{
Name: "add",
ShortUsage: "txs add [flags]",
ShortHelp: "Imports transactions into the genesis.json",
LongHelp: "Imports the transactions from a tx-archive backup to the genesis.json",
},
cfg,
func(ctx context.Context, _ []string) error {
return execTxsAdd(ctx, cfg, io)
},
)
}

func (c *txsAddCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.parseExport,
"parse-export",
"",
"the path to the transactions export containing a list of transactions",
)
}

func execTxsAdd(ctx context.Context, cfg *txsAddCfg, io *commands.IO) error {
// Load the genesis
genesis, loadErr := types.GenesisDocFromFile(cfg.rootCfg.genesisPath)
if loadErr != nil {
return fmt.Errorf("unable to load genesis, %w", loadErr)
}

// Open the transactions file
file, loadErr := os.Open(cfg.parseExport)
if loadErr != nil {
return fmt.Errorf("%w, %w", errInvalidTxsFile, loadErr)
}

txs, err := getTransactionsFromFile(ctx, file)
if err != nil {
return fmt.Errorf("unable to read file, %w", err)
}

// Initialize the app state if it's not present
if genesis.AppState == nil {
genesis.AppState = gnoland.GnoGenesisState{}
}

state := genesis.AppState.(gnoland.GnoGenesisState)

// Left merge the transactions
fileTxStore := txStore(txs)
genesisTxStore := txStore(state.Txs)

// The genesis transactions have preference with the order
// in the genesis.json
if err := genesisTxStore.leftMerge(fileTxStore); err != nil {
return err
}

// Save the state
state.Txs = genesisTxStore
genesis.AppState = state

// Save the updated genesis
if err := genesis.SaveAs(cfg.rootCfg.genesisPath); err != nil {
return fmt.Errorf("unable to save genesis.json, %w", err)
}

io.Printfln(
"Saved %d transactions to genesis.json",
len(txs),
)

return nil
}

// getTransactionsFromFile fetches the transactions from the
// specified reader
func getTransactionsFromFile(ctx context.Context, reader io.Reader) ([]std.Tx, error) {
txs := make([]std.Tx, 0)

scanner := bufio.NewScanner(reader)

for scanner.Scan() {
select {
case <-ctx.Done():
return nil, errTxsParsingAborted
default:
// Parse the amino JSON
var tx std.Tx

if err := amino.UnmarshalJSON(scanner.Bytes(), &tx); err != nil {
return nil, fmt.Errorf(
"unable to unmarshal amino JSON, %w",
err,
)
}

txs = append(txs, tx)
}
}

// Check for scanning errors
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf(
"error encountered while reading file, %w",
err,
)
}

return txs, nil
}
Loading

0 comments on commit 505ed60

Please sign in to comment.