Skip to content

Commit

Permalink
feat(gnodev): add more configuration flags for server usage
Browse files Browse the repository at this point in the history
Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com>
  • Loading branch information
gfanton committed Feb 7, 2024
1 parent 50d426a commit df590cf
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 46 deletions.
89 changes: 81 additions & 8 deletions contribs/gnodev/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"time"

"github.com/fsnotify/fsnotify"
Expand All @@ -32,14 +33,27 @@ const (
)

type devCfg struct {
webListenerAddr string
minimal bool
verbose bool
noWatch bool
webListenerAddr string
nodeRPCListenerAddr string
nodeP2PListenerAddr string
nodeProxyAppListenerAddr string

minimal bool
verbose bool
noWatch bool
noReplay bool
maxGas int64
}

var defaultDevOptions = &devCfg{
webListenerAddr: "127.0.0.1:8888",
maxGas: 10_000_000_000,
webListenerAddr: "127.0.0.1:8888",
nodeRPCListenerAddr: "127.0.0.1:36657",

// As we have no reason to configure this yet, set this to random port
// to avoid potential conflict with other app
nodeP2PListenerAddr: "tcp://127.0.0.1:0",
nodeProxyAppListenerAddr: "tcp://127.0.0.1:0",
}

func main() {
Expand Down Expand Up @@ -71,6 +85,13 @@ func (c *devCfg) RegisterFlags(fs *flag.FlagSet) {
"web server listening address",
)

fs.StringVar(
&c.nodeRPCListenerAddr,
"node-rpc-listener",
defaultDevOptions.nodeRPCListenerAddr,
"gnoland rpc node listening address",
)

fs.BoolVar(
&c.minimal,
"minimal",
Expand All @@ -91,6 +112,20 @@ func (c *devCfg) RegisterFlags(fs *flag.FlagSet) {
defaultDevOptions.noWatch,
"do not watch for files change",
)

fs.BoolVar(
&c.noReplay,
"no-replay",
defaultDevOptions.noWatch,
"do not replay previous transactions on reload",
)

fs.Int64Var(
&c.maxGas,
"max-gas",
defaultDevOptions.maxGas,
"set the maximum gas by block",
)
}

func execDev(cfg *devCfg, args []string, io commands.IO) error {
Expand Down Expand Up @@ -126,7 +161,7 @@ func execDev(cfg *devCfg, args []string, io commands.IO) error {

// Setup Dev Node
// XXX: find a good way to export or display node logs
devNode, err := setupDevNode(ctx, rt, pkgpaths)
devNode, err := setupDevNode(ctx, cfg, rt, pkgpaths)
if err != nil {
return err
}
Expand Down Expand Up @@ -301,12 +336,26 @@ func setupRawTerm(io commands.IO) (rt *rawterm.RawTerm, restore func() error, er
}

// setupDevNode initializes and returns a new DevNode.
func setupDevNode(ctx context.Context, rt *rawterm.RawTerm, pkgspath []string) (*gnodev.Node, error) {
func setupDevNode(ctx context.Context, cfg *devCfg, rt *rawterm.RawTerm, pkgspath []string) (*gnodev.Node, error) {
nodeOut := rt.NamespacedWriter("Node")

zapLogger := log.NewZapConsoleLogger(nodeOut, zapcore.ErrorLevel)

return gnodev.NewDevNode(ctx, log.ZapLoggerToSlog(zapLogger), pkgspath)
gnoroot := gnoenv.RootDir()

// configure gnoland node
config := gnodev.DefaultNodeConfig(gnoroot)
config.PackagesPathList = pkgspath
config.TMConfig.RPC.ListenAddress = resolveUnixOrTCPAddr(cfg.nodeRPCListenerAddr)
config.NoReplay = cfg.noReplay
config.SkipFailingGenesisTxs = true
config.MaxGasPerBlock = cfg.maxGas

// other listeners
config.TMConfig.P2P.ListenAddress = defaultDevOptions.nodeP2PListenerAddr
config.TMConfig.ProxyApp = defaultDevOptions.nodeProxyAppListenerAddr

return gnodev.NewDevNode(ctx, log.ZapLoggerToSlog(zapLogger), config)
}

// setupGnowebServer initializes and starts the Gnoweb server.
Expand Down Expand Up @@ -375,3 +424,27 @@ func checkForError(w io.Writer, err error) {

fmt.Fprintln(w, "[DONE]")
}

func resolveUnixOrTCPAddr(in string) (out string) {
var err error
var addr net.Addr

if strings.HasPrefix(in, "unix://") {
in = strings.TrimPrefix(in, "unix://")
if addr, err := net.ResolveUnixAddr("unix", in); err == nil {
return fmt.Sprintf("%s://%s", addr.Network(), addr.String())
}

err = fmt.Errorf("unable to resolve unix address `unix://%s`: %w", in, err)
} else { // don't bother to checking prefix
in = strings.TrimPrefix(in, "tcp://")
if addr, err = net.ResolveTCPAddr("tcp", in); err == nil {
return fmt.Sprintf("%s://%s", addr.Network(), addr.String())
}

err = fmt.Errorf("unable to resolve tcp address `tcp://%s`: %w", in, err)
}

panic(err)

}
124 changes: 87 additions & 37 deletions contribs/gnodev/pkg/dev/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/gno.land/pkg/integration"
vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
"github.com/gnolang/gno/tm2/pkg/amino"
tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config"
"github.com/gnolang/gno/tm2/pkg/bft/node"
"github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
bft "github.com/gnolang/gno/tm2/pkg/bft/types"
Expand All @@ -23,13 +23,35 @@ import (

const gnoDevChainID = "tendermint_test" // XXX: this is hardcoded and cannot be change bellow

type NodeConfig struct {
PackagesPathList []string
TMConfig *tmcfg.Config
SkipFailingGenesisTxs bool
NoReplay bool
MaxGasPerBlock int64
}

func DefaultNodeConfig(rootdir string) *NodeConfig {
tmc := gnoland.NewDefaultTMConfig(rootdir)
tmc.Consensus.SkipTimeoutCommit = false // avoid time drifting, see issue #1507

return &NodeConfig{
PackagesPathList: []string{},
TMConfig: tmc,
SkipFailingGenesisTxs: true,
MaxGasPerBlock: 10_000_000_000,
}
}

// Node is not thread safe
type Node struct {
*node.Node

config *NodeConfig
client client.Client
logger *slog.Logger
pkgs PkgsMap // path -> pkg

// keep track of number of loaded package to be able to skip them on restore
loadedPackages int
}
Expand All @@ -45,8 +67,8 @@ var (
}
)

func NewDevNode(ctx context.Context, logger *slog.Logger, pkgslist []string) (*Node, error) {
mpkgs, err := newPkgsMap(pkgslist)
func NewDevNode(ctx context.Context, logger *slog.Logger, cfg *NodeConfig) (*Node, error) {
mpkgs, err := newPkgsMap(cfg.PackagesPathList)
if err != nil {
return nil, fmt.Errorf("unable map pkgs list: %w", err)
}
Expand All @@ -62,7 +84,7 @@ func NewDevNode(ctx context.Context, logger *slog.Logger, pkgslist []string) (*N
Txs: pkgsTxs,
}

node, err := newNode(logger, genesis)
node, err := newNode(logger, cfg, genesis)
if err != nil {
return nil, fmt.Errorf("unable to create the node: %w", err)
}
Expand All @@ -82,6 +104,7 @@ func NewDevNode(ctx context.Context, logger *slog.Logger, pkgslist []string) (*N
return &Node{
Node: node,

config: cfg,
client: client,
pkgs: mpkgs,
logger: logger,
Expand Down Expand Up @@ -128,31 +151,6 @@ func (d *Node) UpdatePackages(paths ...string) error {
return nil
}

// Reset stops the node, if running, and reloads it with a new genesis state,
// effectively ignoring the current state.
func (d *Node) Reset(ctx context.Context) error {
// Stop the node if it's currently running.
if d.Node.IsRunning() {
if err := d.Node.Stop(); err != nil {
return fmt.Errorf("unable to stop the node: %w", err)
}
}

// Generate a new genesis state based on the current packages
txs, err := d.pkgs.Load(DefaultCreator, DefaultFee, nil)
if err != nil {
return fmt.Errorf("unable to load pkgs: %w", err)
}

genesis := gnoland.GnoGenesisState{
Balances: DefaultBalance,
Txs: txs,
}

// Reset the node with the new genesis state.
return d.reset(ctx, genesis)
}

// ReloadAll updates all currently known packages and then reloads the node.
func (d *Node) ReloadAll(ctx context.Context) error {
pkgs := d.ListPkgs()
Expand All @@ -173,6 +171,12 @@ func (d *Node) ReloadAll(ctx context.Context) error {
// If any transaction, including 'addpkg', fails, it will be ignored.
// Use 'Reset' to completely reset the node's state in case of persistent errors.
func (d *Node) Reload(ctx context.Context) error {
if d.config.NoReplay {
// If NoReplay is true, reload as the same effect as reset
d.logger.Warn("replay disable")
return d.Reset(ctx)
}

// Get current blockstore state
state, err := d.getBlockStoreState(ctx)
if err != nil {
Expand Down Expand Up @@ -209,6 +213,31 @@ func (d *Node) Reload(ctx context.Context) error {
return nil
}

// Reset stops the node, if running, and reloads it with a new genesis state,
// effectively ignoring the current state.
func (d *Node) Reset(ctx context.Context) error {
// Stop the node if it's currently running.
if d.Node.IsRunning() {
if err := d.Node.Stop(); err != nil {
return fmt.Errorf("unable to stop the node: %w", err)
}
}

// Generate a new genesis state based on the current packages
txs, err := d.pkgs.Load(DefaultCreator, DefaultFee, nil)
if err != nil {
return fmt.Errorf("unable to load pkgs: %w", err)
}

genesis := gnoland.GnoGenesisState{
Balances: DefaultBalance,
Txs: txs,
}

// Reset the node with the new genesis state.
return d.reset(ctx, genesis)
}

func (d *Node) reset(ctx context.Context, genesis gnoland.GnoGenesisState) error {
var err error

Expand All @@ -227,7 +256,7 @@ func (d *Node) reset(ctx context.Context, genesis gnoland.GnoGenesisState) error
createNode := func() {
defer recoverError()

node, nodeErr := newNode(d.logger, genesis)
node, nodeErr := newNode(d.logger, d.config, genesis)
if nodeErr != nil {
err = fmt.Errorf("unable to create node: %w", nodeErr)
return
Expand Down Expand Up @@ -411,13 +440,34 @@ func (pm PkgsMap) Load(creator bft.Address, fee std.Fee, deposit std.Coins) ([]s
return txs, nil
}

func newNode(logger *slog.Logger, genesis gnoland.GnoGenesisState) (*node.Node, error) {
rootdir := gnoenv.RootDir()
func newNode(logger *slog.Logger, cfg *NodeConfig, genesis gnoland.GnoGenesisState) (*node.Node, error) {
nodeConfig := newNodeConfig(cfg.TMConfig, genesis)
nodeConfig.SkipFailingGenesisTxs = cfg.SkipFailingGenesisTxs
nodeConfig.Genesis.ConsensusParams.Block.MaxGas = cfg.MaxGasPerBlock
return gnoland.NewInMemoryNode(logger, nodeConfig)
}

nodeConfig := gnoland.NewDefaultInMemoryNodeConfig(rootdir)
nodeConfig.SkipFailingGenesisTxs = true
nodeConfig.TMConfig.Consensus.SkipTimeoutCommit = false // avoid time drifting, see issue #1507
func newNodeConfig(tmc *tmcfg.Config, appstate gnoland.GnoGenesisState) *gnoland.InMemoryNodeConfig {
// Create Mocked Identity
pv := gnoland.NewMockedPrivValidator()
genesis := gnoland.NewDefaultGenesisConfig(pv.GetPubKey(), tmc.ChainID())
genesis.AppState = appstate

nodeConfig.Genesis.AppState = genesis
return gnoland.NewInMemoryNode(logger, nodeConfig)
// Add self as validator
self := pv.GetPubKey()
genesis.Validators = []bft.GenesisValidator{
{
Address: self.Address(),
PubKey: self,
Power: 10,
Name: "self",
},
}

return &gnoland.InMemoryNodeConfig{
PrivValidator: pv,
TMConfig: tmc,
Genesis: genesis,
GenesisMaxVMCycles: 10_000_000,
}
}
2 changes: 1 addition & 1 deletion gno.land/pkg/gnoland/node_inmemory.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewDefaultGenesisConfig(pk crypto.PubKey, chainid string) *bft.GenesisDoc {
Block: &abci.BlockParams{
MaxTxBytes: 1_000_000, // 1MB,
MaxDataBytes: 2_000_000, // 2MB,
MaxGas: 10_0000_000, // 10M gas
MaxGas: 100_000_000, // 10M gas

Check warning on line 45 in gno.land/pkg/gnoland/node_inmemory.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoland/node_inmemory.go#L45

Added line #L45 was not covered by tests
TimeIotaMS: 100, // 100ms
},
},
Expand Down

0 comments on commit df590cf

Please sign in to comment.