Skip to content

Commit

Permalink
feat(gnodev): allow gnodev to load txs (#2281)
Browse files Browse the repository at this point in the history
This is useful for testing an app; it allows gnodev to be started each
time with a particular state -- the result of the provided transactions.
<!-- please provide a detailed description of the changes made in this
pull request. -->

<details><summary>Contributors' checklist...</summary>

- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>

---------

Co-authored-by: Guilhem Fanton <8671905+gfanton@users.noreply.github.com>
  • Loading branch information
deelawn and gfanton committed Jun 6, 2024
1 parent 2e5ac4a commit 4dafb8a
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 45 deletions.
8 changes: 8 additions & 0 deletions contribs/gnodev/cmd/gnodev/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type devCfg struct {
root string
premineAccounts varPremineAccounts
balancesFile string
txsFile string

// Node Configuration
minimal bool
Expand Down Expand Up @@ -136,6 +137,13 @@ func (c *devCfg) RegisterFlags(fs *flag.FlagSet) {
"load the provided balance file (refer to the documentation for format)",
)

fs.StringVar(
&c.txsFile,
"txs-file",
defaultDevOptions.txsFile,
"load the provided transactions file (refer to the documentation for format)",
)

fs.StringVar(
&c.deployKey,
"deploy-key",
Expand Down
17 changes: 15 additions & 2 deletions contribs/gnodev/cmd/gnodev/setup_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
gnodev "github.com/gnolang/gno/contribs/gnodev/pkg/dev"
"github.com/gnolang/gno/contribs/gnodev/pkg/emitter"
"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/tm2/pkg/std"
)

// setupDevNode initializes and returns a new DevNode.
Expand All @@ -21,19 +22,31 @@ func setupDevNode(
balances gnoland.Balances,
pkgspath []gnodev.PackagePath,
) (*gnodev.Node, error) {
config := setupDevNodeConfig(cfg, balances, pkgspath)
// Load transactions.
txs, err := parseTxs(cfg.txsFile)
if err != nil {
return nil, fmt.Errorf("unable to load transactions: %w", err)
}

config := setupDevNodeConfig(cfg, balances, pkgspath, txs)
return gnodev.NewDevNode(ctx, logger, remitter, config)
}

// setupDevNodeConfig creates and returns a new dev.NodeConfig.
func setupDevNodeConfig(cfg *devCfg, balances gnoland.Balances, pkgspath []gnodev.PackagePath) *gnodev.NodeConfig {
func setupDevNodeConfig(
cfg *devCfg,
balances gnoland.Balances,
pkgspath []gnodev.PackagePath,
txs []std.Tx,
) *gnodev.NodeConfig {
config := gnodev.DefaultNodeConfig(cfg.root)
config.BalancesList = balances.List()
config.PackagesPathList = pkgspath
config.TMConfig.RPC.ListenAddress = resolveUnixOrTCPAddr(cfg.nodeRPCListenerAddr)
config.NoReplay = cfg.noReplay
config.MaxGasPerBlock = cfg.maxGas
config.ChainID = cfg.chainId
config.Txs = txs

// other listeners
config.TMConfig.P2P.ListenAddress = defaultDevOptions.nodeP2PListenerAddr
Expand Down
23 changes: 23 additions & 0 deletions contribs/gnodev/cmd/gnodev/txs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"context"
"fmt"
"os"

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

func parseTxs(txFile string) ([]std.Tx, error) {
if txFile == "" {
return nil, nil
}

file, loadErr := os.Open(txFile)
if loadErr != nil {
return nil, fmt.Errorf("unable to open tx file %s: %w", txFile, loadErr)
}
defer file.Close()

return std.ParseTxs(context.Background(), file)
}
3 changes: 3 additions & 0 deletions contribs/gnodev/pkg/dev/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type NodeConfig struct {
NoReplay bool
MaxGasPerBlock int64
ChainID string
Txs []std.Tx
}

func DefaultNodeConfig(rootdir string) *NodeConfig {
Expand Down Expand Up @@ -107,6 +108,8 @@ func NewDevNode(ctx context.Context, logger *slog.Logger, emitter emitter.Emitte
Txs: pkgsTxs,
}

genesis.Txs = append(genesis.Txs, cfg.Txs...)

if err := devnode.rebuildNode(ctx, genesis); err != nil {
return nil, fmt.Errorf("unable to initialize the node: %w", err)
}
Expand Down
15 changes: 15 additions & 0 deletions docs/gno-tooling/cli/gnodev.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=10000000000000ugnot # test2
# ...
```

### Transactions file

You can specify a transactions file using `--txs-file`. The file should contain a list of signed transactions
that will be applied when starting the in-memory node.
```
{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post1","First post","Lorem Ipsum","2022-05-20T13:17:22Z","","tag1,tag2"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}
{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post2","Second post","Lorem Ipsum","2022-05-20T13:17:23Z","","tag1,tag3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}
```

#### Construction a transaction
`gnokey maketx ... >> "tx-file.json"`

#### Signing the transaction
`gnokey sign -tx-path tx-file.json ...`

### Deploy

All realms and packages will be deployed to the in-memory node by the address passed in with the
Expand Down
43 changes: 1 addition & 42 deletions gno.land/cmd/gnoland/genesis_txs_add.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package main

import (
"bufio"
"context"
"errors"
"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"
Expand All @@ -18,7 +15,6 @@ import (
var (
errInvalidTxsFile = errors.New("unable to open transactions file")
errNoTxsFileSpecified = errors.New("no txs file specified")
errTxsParsingAborted = errors.New("transaction parsing aborted")
)

// newTxsAddCmd creates the genesis txs add subcommand
Expand Down Expand Up @@ -61,7 +57,7 @@ func execTxsAdd(
return fmt.Errorf("%w, %w", errInvalidTxsFile, loadErr)
}

txs, err := getTransactionsFromFile(ctx, file)
txs, err := std.ParseTxs(ctx, file)
if err != nil {
return fmt.Errorf("unable to read file, %w", err)
}
Expand Down Expand Up @@ -102,40 +98,3 @@ func execTxsAdd(

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
}
43 changes: 42 additions & 1 deletion tm2/pkg/std/tx.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package std

import (
"bufio"
"context"
"fmt"
"io"

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/multisig"
"github.com/gnolang/gno/tm2/pkg/errors"
)

var maxGasWanted = int64((1 << 60) - 1) // something smaller than math.MaxInt64
var (
maxGasWanted = int64((1 << 60) - 1) // something smaller than math.MaxInt64

ErrTxsLoadingAborted = errors.New("transaction loading aborted")
)

// Tx is a standard way to wrap a Msg with Fee and Signatures.
// NOTE: the first signature is the fee payer (Signatures must not be nil).
Expand Down Expand Up @@ -136,3 +144,36 @@ func (fee Fee) Bytes() []byte {
}
return bz
}

func ParseTxs(ctx context.Context, reader io.Reader) ([]Tx, error) {
var txs []Tx
scanner := bufio.NewScanner(reader)

for scanner.Scan() {
select {
case <-ctx.Done():
return nil, ErrTxsLoadingAborted
default:
// Parse the amino JSON
var tx 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
}

0 comments on commit 4dafb8a

Please sign in to comment.