From 9f389984878e5ddddd7f70cbbf8f7f0ca15f5f86 Mon Sep 17 00:00:00 2001 From: gfanton <8671905+gfanton@users.noreply.github.com> Date: Thu, 25 Jan 2024 14:20:05 +0100 Subject: [PATCH] fix: cleanup events reload Signed-off-by: gfanton <8671905+gfanton@users.noreply.github.com> --- contribs/gnodev/go.mod | 2 +- contribs/gnodev/main.go | 13 +- contribs/gnodev/pkg/dev/node.go | 156 +++++++++--------- contribs/gnodev/pkg/events/events.go | 47 +++++- .../gnodev/pkg/{dev => events}/middleware.go | 20 +-- contribs/gnodev/pkg/{dev => events}/server.go | 35 ++-- .../pkg/{dev => events}/static/hotreload.js | 0 contribs/gnodev/pkg/watcher/watch.go | 94 +++++------ 8 files changed, 199 insertions(+), 168 deletions(-) rename contribs/gnodev/pkg/{dev => events}/middleware.go (78%) rename contribs/gnodev/pkg/{dev => events}/server.go (64%) rename contribs/gnodev/pkg/{dev => events}/static/hotreload.js (100%) diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index bbf2fb9c7d2..0097bca6b61 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -7,8 +7,8 @@ replace github.com/gnolang/gno => ../.. require ( github.com/fsnotify/fsnotify v1.7.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 - golang.org/x/term v0.16.0 github.com/gorilla/websocket v1.5.1 + golang.org/x/term v0.16.0 ) require ( diff --git a/contribs/gnodev/main.go b/contribs/gnodev/main.go index f04d20f5c7a..31de8c826a0 100644 --- a/contribs/gnodev/main.go +++ b/contribs/gnodev/main.go @@ -304,16 +304,7 @@ func setupRawTerm(io commands.IO) (rt *rawterm.RawTerm, restore func() error, er // correctly format output for terminal io.SetOut(commands.WriteNopCloser(rt)) - - restoreWithRecover := func() error { - if r := recover(); r != nil { - rt.Taskf("panic", "%v\n", r) - } - - return restore() - } - - return rt, restoreWithRecover, nil + return rt, restore, nil } // setupDevNode initializes and returns a new DevNode. @@ -322,7 +313,7 @@ func setupDevNode(ctx context.Context, emitter events.Emitter, rt *rawterm.RawTe logger := tmlog.NewTMLogger(nodeOut) logger.SetLevel(tmlog.LevelError) - return gnodev.NewDevNode(ctx, emitter, logger, pkgspath) + return gnodev.NewDevNode(ctx, logger, emitter, pkgspath) } // setupGnowebServer initializes and starts the Gnoweb server. diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index 7ca4fa766ed..41534adea7c 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -16,6 +16,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/node" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" + tm2events "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/std" //backup "github.com/gnolang/tx-archive/backup/client" @@ -28,9 +29,9 @@ const gnoDevChainID = "tendermint_test" // XXX: this is hardcoded and cannot be type Node struct { *node.Node - events events.Emitter - logger log.Logger - pkgs PkgsMap // path -> pkg + emitter events.Emitter + logger log.Logger + pkgs PkgsMap // path -> pkg } var ( @@ -44,7 +45,7 @@ var ( } ) -func NewDevNode(ctx context.Context, emitter events.Emitter, logger log.Logger, pkgslist []string) (*Node, error) { +func NewDevNode(ctx context.Context, logger log.Logger, emitter events.Emitter, pkgslist []string) (*Node, error) { mpkgs, err := newPkgsMap(pkgslist) if err != nil { return nil, fmt.Errorf("unable map pkgs list: %w", err) @@ -61,27 +62,16 @@ func NewDevNode(ctx context.Context, emitter events.Emitter, logger log.Logger, Txs: txs, } - node, err := newNode(logger, genesis) + node, err := newNode(ctx, logger, emitter, genesis) if err != nil { return nil, fmt.Errorf("unable to create the node: %w", err) } - if err := node.Start(); err != nil { - return nil, fmt.Errorf("unable to start node: %w", err) - } - - // Wait for readiness - select { - case <-gnoland.GetNodeReadiness(node): // ok - case <-ctx.Done(): - return nil, ctx.Err() - } - return &Node{ - events: emitter, - Node: node, - pkgs: mpkgs, - logger: logger, + emitter: emitter, + Node: node, + pkgs: mpkgs, + logger: logger, }, nil } @@ -146,11 +136,13 @@ func (d *Node) Reset(ctx context.Context) error { } // Reset the node with the new genesis state. - if err := d.reset(ctx, genesis); err != nil { + node, err := newNode(ctx, d.logger, d.emitter, genesis) + if err != nil { return fmt.Errorf("unable to reset the node: %w", err) } - d.events.Emit(events.NewEventReset()) + d.Node = node + d.emitter.Emit(events.NewEventReset()) return nil } @@ -199,7 +191,8 @@ func (d *Node) Reload(ctx context.Context) error { } // Reset the node with the new genesis state. - if err := d.reset(ctx, genesis); err != nil { + node, err := newNode(ctx, d.logger, d.emitter, genesis) + if err != nil { return fmt.Errorf("unable to reset the node: %w", err) } @@ -210,7 +203,7 @@ func (d *Node) Reload(ctx context.Context) error { } if err := d.SendTransaction(&tx); err != nil { - return fmt.Errorf("unable to send transaction: %w", err) + d.logger.Error("unable to send transaction", "error", err) } } @@ -218,58 +211,11 @@ func (d *Node) Reload(ctx context.Context) error { return fmt.Errorf("unable to reload the node: %w", err) } - d.events.Emit(events.NewEventReload()) + d.Node = node + d.emitter.Emit(events.NewEventReload()) return nil } -func (d *Node) reset(ctx context.Context, genesis gnoland.GnoGenesisState) error { - var err error - - // recoverError handles panics and converts them to errors. - recoverError := func() { - if r := recover(); r != nil { - panicErr, ok := r.(error) - if !ok { - panic(r) // Re-panic if not an error. - } - - err = panicErr - } - } - - createNode := func() { - defer recoverError() - - node, nodeErr := newNode(d.logger, genesis) - if nodeErr != nil { - err = fmt.Errorf("unable to create node: %w", nodeErr) - return - } - - if startErr := node.Start(); startErr != nil { - err = fmt.Errorf("unable to start the node: %w", startErr) - return - } - - d.Node = node - } - - // Execute node creation and handle any errors. - createNode() - if err != nil { - return err - } - - // Wait for the node to be ready - select { - case <-d.GetNodeReadiness(): // Ok - case <-ctx.Done(): - return ctx.Err() - } - - return err -} - // GetBlockTransactions returns the transactions contained // within the specified block, if any func (d *Node) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) { @@ -416,13 +362,71 @@ func (pm PkgsMap) Load(creator bft.Address, fee std.Fee, deposit std.Coins) ([]s return txs, nil } -func newNode(logger log.Logger, genesis gnoland.GnoGenesisState) (*node.Node, error) { +func newNode(ctx context.Context, logger log.Logger, emitter events.Emitter, genesis gnoland.GnoGenesisState) (*node.Node, error) { rootdir := gnoenv.RootDir() + // Setup node config nodeConfig := gnoland.NewDefaultInMemoryNodeConfig(rootdir) nodeConfig.SkipFailingGenesisTxs = true nodeConfig.TMConfig.Consensus.SkipTimeoutCommit = false // avoid time drifting, see issue #1507 - nodeConfig.Genesis.AppState = genesis - return gnoland.NewInMemoryNode(logger, nodeConfig) + + // Generate a new node + var err error + + // recoverError handles panics and converts them to errors. + recoverError := func() { + if r := recover(); r != nil { + panicErr, ok := r.(error) + if !ok { + panic(r) // Re-panic if not an error. + } + + err = panicErr + } + } + + createNode := func() *node.Node { + defer recoverError() + + node, nodeErr := gnoland.NewInMemoryNode(logger, nodeConfig) + if nodeErr != nil { + err = fmt.Errorf("unable to create node: %w", nodeErr) + return nil + } + + node.EventSwitch().AddListener("dev-emitter", func(evt tm2events.Event) { + switch data := evt.(type) { + case bft.EventTx: + evtTx, err := events.NewTxEventResult(data.Result) + if err != nil { + logger.Error("unable to create an event from tx result", "error", err) + return + } + + emitter.Emit(evtTx) + } + }) + + if startErr := node.Start(); startErr != nil { + err = fmt.Errorf("unable to start the node: %w", startErr) + return nil + } + + return node + } + + // Execute node creation and handle any errors. + node := createNode() + if err != nil { + return nil, err + } + + // Wait for the node to be ready + select { + case <-gnoland.GetNodeReadiness(node): // Ok + return node, nil + case <-ctx.Done(): + return nil, ctx.Err() + } } diff --git a/contribs/gnodev/pkg/events/events.go b/contribs/gnodev/pkg/events/events.go index f1564e640cc..fb6cc46bf93 100644 --- a/contribs/gnodev/pkg/events/events.go +++ b/contribs/gnodev/pkg/events/events.go @@ -1,16 +1,26 @@ package events +import ( + "fmt" + + "github.com/gnolang/gno/tm2/pkg/amino" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/std" +) + type EventType string const ( - EvtReload EventType = "NODE_RELOAD" - EvtReset EventType = "NODE_RESET" - EvtPackagesUpdate EventType = "PACKAGES_UPDATE" + EvtReload EventType = "EVENT_NODE_RELOAD" + EvtReset EventType = "EVENT_NODE_RESET" + EvtPackagesUpdate EventType = "EVENT_PACKAGES_UPDATE" + EvtTxResult EventType = "EVENT_TX_RESULT" ) type Event struct { - Type EventType `json:"type"` - Data interface{} `json:"data"` + Type EventType `json:"type"` + Data any `json:"data"` } // Event Reload @@ -30,7 +40,7 @@ type EventReset struct{} func NewEventReset() *Event { return &Event{ - Type: EvtReload, + Type: EvtReset, Data: &EventReset{}, } } @@ -54,3 +64,28 @@ func NewPackagesUpdateEvent(pkgs []PackageUpdate) *Event { }, } } + +// Event Tx is an alias to TxResult + +type EventTxResult struct { + Height int64 `json:"height"` + Index uint32 `json:"index"` + Tx std.Tx `json:"tx"` + Response abci.ResponseDeliverTx `json:"response"` +} + +func NewTxEventResult(result types.TxResult) (*Event, error) { + evt := &EventTxResult{ + Height: result.Height, + Index: result.Index, + Response: result.Response, + } + if err := amino.Unmarshal(result.Tx, &evt.Tx); err != nil { + return nil, fmt.Errorf("unable unmarshal tx: %w", err) + } + + return &Event{ + Type: EvtTxResult, + Data: evt, + }, nil +} diff --git a/contribs/gnodev/pkg/dev/middleware.go b/contribs/gnodev/pkg/events/middleware.go similarity index 78% rename from contribs/gnodev/pkg/dev/middleware.go rename to contribs/gnodev/pkg/events/middleware.go index 4e04200af8f..01d2d48c344 100644 --- a/contribs/gnodev/pkg/dev/middleware.go +++ b/contribs/gnodev/pkg/events/middleware.go @@ -1,4 +1,4 @@ -package dev +package events import ( "bytes" @@ -7,8 +7,6 @@ import ( "strings" "sync" "text/template" - - "github.com/gnolang/gno/contribs/gnodev/pkg/events" ) //go:embed static/hotreload.js @@ -55,7 +53,7 @@ func (m *Middleware) UpdateRemote(remote string) { type data struct { Remote string - ReloadEvents []events.EventType + ReloadEvents []EventType } func (m *Middleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) { @@ -75,7 +73,8 @@ func (m *Middleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) { m.next.ServeHTTP(mw, req) // Check for any "text/html" answer - if content := mw.ResponseWriter.Header().Get("Content-Type"); !strings.Contains(content, "text/html") { + content := mw.ResponseWriter.Header().Get("Content-Type") + if !strings.Contains(content, "text/html") { rw.Write(buffer.Bytes()) return } @@ -85,7 +84,7 @@ func (m *Middleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) { script.WriteString(`