diff --git a/components/app/app.go b/components/app/app.go index e0a19e375..1fb23901a 100644 --- a/components/app/app.go +++ b/components/app/app.go @@ -7,7 +7,6 @@ import ( "github.com/iotaledger/hive.go/app" "github.com/iotaledger/hive.go/app/components/profiling" "github.com/iotaledger/hive.go/app/components/shutdown" - "github.com/iotaledger/iota-core/components/dashboard" dashboardmetrics "github.com/iotaledger/iota-core/components/dashboard_metrics" "github.com/iotaledger/iota-core/components/debugapi" "github.com/iotaledger/iota-core/components/inx" @@ -50,7 +49,6 @@ Command line flags: metricstracker.Component, protocol.Component, dashboardmetrics.Component, - dashboard.Component, prometheus.Component, inx.Component, ), diff --git a/components/dashboard/build.sh b/components/dashboard/build.sh deleted file mode 100755 index c01d623af..000000000 --- a/components/dashboard/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# -# Builds both the node dashboard and dagvisualizer - -echo "::: Building /plugins/dashboard/frontend :::" -rm -rf /frontend/build - -docker run -it --rm \ - -u $(id -u ${USER}):$(id -g ${USER}) \ - --volume="/etc/group:/etc/group:ro" \ - --volume="/etc/passwd:/etc/passwd:ro" \ - --volume="/etc/shadow:/etc/shadow:ro" \ - -v $(pwd):/tmp/mnt \ - -e YARN_CACHE_FOLDER=/tmp/ \ - -e HOME=/tmp/ \ - -w /tmp/mnt/frontend node:14.0 bash -c "yarn install && yarn build" diff --git a/components/dashboard/component.go b/components/dashboard/component.go deleted file mode 100644 index 3cc127fa5..000000000 --- a/components/dashboard/component.go +++ /dev/null @@ -1,195 +0,0 @@ -package dashboard - -import ( - "context" - "fmt" - "net" - "net/http" - "runtime" - "time" - - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "github.com/libp2p/go-libp2p/core/host" - "go.uber.org/dig" - - "github.com/iotaledger/hive.go/app" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/components/metricstracker" - "github.com/iotaledger/iota-core/pkg/daemon" - "github.com/iotaledger/iota-core/pkg/network" - "github.com/iotaledger/iota-core/pkg/protocol" -) - -func init() { - Component = &app.Component{ - Name: "Dashboard", - DepsFunc: func(cDeps dependencies) { deps = cDeps }, - Params: params, - Configure: configure, - Run: run, - IsEnabled: func(*dig.Container) bool { - return ParamsDashboard.Enabled - }, - } -} - -var NodeStartupTimestamp = time.Now() - -var ( - Component *app.Component - deps dependencies - - server *echo.Echo -) - -type dependencies struct { - dig.In - - Host host.Host - Protocol *protocol.Protocol - AppInfo *app.Info - NetworkManager network.Manager - MetricsTracker *metricstracker.MetricsTracker -} - -func configure() error { - configureServer() - return nil -} - -func run() error { - runWebSocketStreams(Component) - runLiveFeed(Component) - runVisualizer(Component) - runSlotsLiveFeed(Component) - - if err := Component.Daemon().BackgroundWorker("Dashboard", func(ctx context.Context) { - Component.LogInfo("Starting Dashboard ... done") - - stopped := make(chan struct{}) - go func(ctx context.Context) { - server.Server.BaseContext = func(_ net.Listener) context.Context { - // set BaseContext to be the same as the plugin, so that requests being processed don't hang the shutdown procedure - return ctx - } - - Component.LogInfof("%s started, bind-address=%s, basic-auth=%v", Component.Name, ParamsDashboard.BindAddress, ParamsDashboard.BasicAuth.Enabled) - if err := server.Start(ParamsDashboard.BindAddress); err != nil { - if !ierrors.Is(err, http.ErrServerClosed) { - Component.LogErrorf("Error serving: %w", err) - } - close(stopped) - } - }(ctx) - - // stop if we are shutting down or the server could not be started - select { - case <-ctx.Done(): - case <-stopped: - } - - Component.LogInfof("Stopping %s ...", Component.Name) - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - if err := server.Shutdown(ctx); err != nil { - Component.LogWarnf("Error stopping: %s", err) - } - - Component.LogInfo("Stopping Dashboard ... done") - }, daemon.PriorityDashboard); err != nil { - Component.LogPanicf("failed to start worker: %s", err) - } - - return nil -} - -func configureServer() { - server = echo.New() - server.Use(middleware.CORSWithConfig(middleware.CORSConfig{ - Skipper: middleware.DefaultSkipper, - AllowOrigins: []string{"*"}, - AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete}, - })) - server.HideBanner = true - server.HidePort = true - server.Use(middleware.Recover()) - - if ParamsDashboard.BasicAuth.Enabled { - server.Use(middleware.BasicAuth(func(username, password string, _ echo.Context) (bool, error) { - if username == ParamsDashboard.BasicAuth.Username && - password == ParamsDashboard.BasicAuth.Password { - return true, nil - } - - return false, nil - })) - } - - setupRoutes(server) -} - -func currentNodeStatus() *nodestatus { - var m runtime.MemStats - runtime.ReadMemStats(&m) - status := &nodestatus{} - status.ID = deps.Host.ID().String() - - // node status - status.Version = deps.AppInfo.Version - status.Uptime = time.Since(NodeStartupTimestamp).Milliseconds() - - // memory metrics - status.Mem = &memmetrics{ - HeapSys: m.HeapSys, - HeapAlloc: m.HeapAlloc, - HeapIdle: m.HeapIdle, - HeapReleased: m.HeapReleased, - HeapObjects: m.HeapObjects, - NumGC: m.NumGC, - LastPauseGC: m.PauseNs[(m.NumGC+255)%256], - } - // get TangleTime - cl := deps.Protocol.Engines.Main.Get().Clock - syncStatus := deps.Protocol.Engines.Main.Get().SyncManager.SyncStatus() - - status.TangleTime = tangleTime{ - Synced: syncStatus.NodeSynced, - Bootstrapped: syncStatus.NodeBootstrapped, - AcceptedBlockSlot: int64(syncStatus.LastAcceptedBlockSlot), - ConfirmedBlockSlot: int64(syncStatus.LastConfirmedBlockSlot), - CommittedSlot: int64(syncStatus.LatestCommitment.Slot()), - ConfirmedSlot: int64(syncStatus.LatestFinalizedSlot), - ATT: cl.Accepted().Time().UnixNano(), - RATT: cl.Accepted().RelativeTime().UnixNano(), - CTT: cl.Confirmed().Time().UnixNano(), - RCTT: cl.Confirmed().RelativeTime().UnixNano(), - } - - return status -} - -func neighborMetrics() []neighbormetric { - if deps.NetworkManager == nil { - return []neighbormetric{} - } - - // gossip plugin might be disabled - neighbors := deps.NetworkManager.Neighbors() - if neighbors == nil { - return []neighbormetric{} - } - - stats := make([]neighbormetric, 0, len(neighbors)) - for _, neighbor := range neighbors { - stats = append(stats, neighbormetric{ - ID: neighbor.Peer().ID.String(), - Addresses: fmt.Sprintf("%s", neighbor.Peer().PeerAddresses), - PacketsRead: neighbor.PacketsRead(), - PacketsWritten: neighbor.PacketsWritten(), - }) - } - - return stats -} diff --git a/components/dashboard/explorer_routes.go b/components/dashboard/explorer_routes.go deleted file mode 100644 index 54cd0d540..000000000 --- a/components/dashboard/explorer_routes.go +++ /dev/null @@ -1,271 +0,0 @@ -package dashboard - -import ( - "encoding/json" - "net/http" - - "github.com/labstack/echo/v4" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/inx-app/pkg/httpserver" - "github.com/iotaledger/iota-core/pkg/model" - "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/api" - "github.com/iotaledger/iota.go/v4/hexutil" -) - -// SearchResult defines the struct of the SearchResult. -type SearchResult struct { - // Block is the *ExplorerBlock. - Block *ExplorerBlock `json:"block"` - // Address is the *ExplorerAddress. - Address *ExplorerAddress `json:"address"` -} - -func setupExplorerRoutes(routeGroup *echo.Group) { - routeGroup.GET("/block/:"+api.ParameterBlockID, func(c echo.Context) (err error) { - blockID, err := httpserver.ParseBlockIDParam(c, api.ParameterBlockID) - if err != nil { - return ierrors.Wrap(err, "failed to parse block ID") - } - - t, err := findBlock(blockID) - if err != nil { - return err - } - - return c.JSON(http.StatusOK, t) - }) - - routeGroup.GET("/transaction/:"+api.ParameterTransactionID, getTransaction) - routeGroup.GET("/transaction/:transactionID/metadata", getTransactionMetadata) - // routeGroup.GET("/transaction/:transactionID/attachments", ledgerstateAPI.GetTransactionAttachments) - routeGroup.GET("/output/:"+api.ParameterOutputID, getOutput) - // routeGroup.GET("/output/:outputID/metadata", ledgerstateAPI.GetOutputMetadata) - // routeGroup.GET("/output/:outputID/consumers", ledgerstateAPI.GetOutputConsumers) - routeGroup.GET("/slot/commitment/:"+api.ParameterCommitmentID, getSlotDetailsByID) - - routeGroup.GET("/search/:search", func(c echo.Context) error { - search := c.Param("search") - result := &SearchResult{} - - blockID, err := iotago.BlockIDFromHexString(search) - if err != nil { - return ierrors.WithMessagef(ErrInvalidParameter, "search ID %s", search) - } - - blk, err := findBlock(blockID) - if err != nil { - return err - } - result.Block = blk - - // addr, err := findAddress(search) - // if err != nil { - // return ierrors.Errorf("can't find address %s: %w", search, err) - // } - // result.Address = addr - - return c.JSON(http.StatusOK, result) - }) -} - -func findBlock(blockID iotago.BlockID) (explorerBlk *ExplorerBlock, err error) { - block, exists := deps.Protocol.Engines.Main.Get().Block(blockID) - if !exists { - return nil, ierrors.Errorf("block %s not found", blockID) - } - - cachedBlock, _ := deps.Protocol.Engines.Main.Get().BlockCache.Block(blockID) - - blockMetadata, err := deps.Protocol.Engines.Main.Get().BlockRetainer.BlockMetadata(blockID) - if err != nil { - return nil, ierrors.Wrapf(err, "failed to get block metadata for block %s", blockID) - } - - return createExplorerBlock(block, cachedBlock, blockMetadata), nil -} - -func createExplorerBlock(block *model.Block, cachedBlock *blocks.Block, blockMetadata *api.BlockMetadataResponse) *ExplorerBlock { - iotaBlk := block.ProtocolBlock() - - sigBytes, err := iotaBlk.Signature.Encode() - if err != nil { - return nil - } - - var payloadJSON []byte - basicBlock, isBasic := block.BasicBlock() - if isBasic { - payloadJSON, err = lo.PanicOnErr(deps.Protocol.APIForVersion(iotaBlk.Header.ProtocolVersion)).JSONEncode(basicBlock.Payload) - if err != nil { - return nil - } - } - - t := &ExplorerBlock{ - ID: block.ID().ToHex(), - NetworkID: iotaBlk.Header.NetworkID, - ProtocolVersion: iotaBlk.Header.ProtocolVersion, - SolidificationTimestamp: 0, - IssuanceTimestamp: iotaBlk.Header.IssuingTime.Unix(), - SequenceNumber: 0, - IssuerID: iotaBlk.Header.IssuerID.ToHex(), - Signature: hexutil.EncodeHex(sigBytes), - StrongParents: iotaBlk.Body.StrongParentIDs().ToHex(), - WeakParents: iotaBlk.Body.WeakParentIDs().ToHex(), - ShallowLikedParents: iotaBlk.Body.ShallowLikeParentIDs().ToHex(), - - PayloadType: func() iotago.PayloadType { - if isBasic && basicBlock.Payload != nil { - return basicBlock.Payload.PayloadType() - } - - return iotago.PayloadType(0) - }(), - Payload: func() json.RawMessage { - if isBasic && basicBlock.Payload != nil && basicBlock.Payload.PayloadType() == iotago.PayloadSignedTransaction { - tx, _ := basicBlock.Payload.(*iotago.SignedTransaction) - txResponse := NewTransaction(tx) - - //nolint:errchkjson - bytes, _ := json.Marshal(txResponse) - - return bytes - } - - return payloadJSON - }(), - TransactionID: func() string { - if isBasic && basicBlock.Payload != nil && basicBlock.Payload.PayloadType() == iotago.PayloadSignedTransaction { - tx, _ := basicBlock.Payload.(*iotago.SignedTransaction) - - return tx.MustID().ToHex() - } - - return "" - }(), - CommitmentID: iotaBlk.Header.SlotCommitmentID.ToHex(), - LatestConfirmedSlot: uint64(iotaBlk.Header.LatestFinalizedSlot), - } - - if cachedBlock != nil { - t.Solid = cachedBlock.IsSolid() - t.Booked = cachedBlock.IsBooked() - t.Acceptance = cachedBlock.IsAccepted() - t.Confirmation = cachedBlock.IsConfirmed() - t.Scheduled = cachedBlock.IsScheduled() - t.ObjectivelyInvalid = cachedBlock.IsInvalid() - t.StrongChildren = lo.Map(cachedBlock.StrongChildren(), func(childBlock *blocks.Block) string { - return childBlock.ID().ToHex() - }) - t.WeakChildren = lo.Map(cachedBlock.WeakChildren(), func(childBlock *blocks.Block) string { - return childBlock.ID().ToHex() - }) - t.LikedInsteadChildren = lo.Map(cachedBlock.ShallowLikeChildren(), func(childBlock *blocks.Block) string { - return childBlock.ID().ToHex() - }) - t.SpendIDs = lo.Map(cachedBlock.SpenderIDs().ToSlice(), func(spendID iotago.TransactionID) string { - return spendID.ToHex() - }) - } else { - switch blockMetadata.BlockState { - case api.BlockStateConfirmed, api.BlockStateFinalized: - t.Solid = true - t.Booked = true - t.Acceptance = true - t.Scheduled = true - t.Confirmation = true - case api.BlockStateDropped, api.BlockStateOrphaned: - t.ObjectivelyInvalid = true - } - } - - return t -} - -func getTransaction(c echo.Context) error { - txID, err := httpserver.ParseTransactionIDParam(c, api.ParameterTransactionID) - if err != nil { - return err - } - - // Get the first output of that transaction (using index 0) - outputID := iotago.OutputID{} - copy(outputID[:], txID[:]) - - output, err := deps.Protocol.Engines.Main.Get().Ledger.Output(outputID) - if err != nil { - return err - } - - block, exists := deps.Protocol.Engines.Main.Get().Block(output.BlockID()) - if !exists { - return ierrors.Errorf("block %s not found", output.BlockID().ToHex()) - } - - iotaTX, isTX := block.SignedTransaction() - if !isTX { - return ierrors.Errorf("block payload of block %s is not a signed transaction", output.BlockID().ToHex()) - } - - return httpserver.JSONResponse(c, http.StatusOK, NewTransaction(iotaTX)) -} - -func getTransactionMetadata(c echo.Context) error { - txID, err := httpserver.ParseTransactionIDParam(c, api.ParameterTransactionID) - if err != nil { - return err - } - - // Get the first output of that transaction (using index 0) - outputID := iotago.OutputID{} - copy(outputID[:], txID[:]) - txMetadata, exists := deps.Protocol.Engines.Main.Get().Ledger.MemPool().TransactionMetadata(txID) - if !exists { - return ierrors.Errorf("transaction metadata for transaction %s not found", txID.ToHex()) - } - - conflicts, _ := deps.Protocol.Engines.Main.Get().Ledger.SpendDAG().ConflictingSpenders(txID) - - return httpserver.JSONResponse(c, http.StatusOK, NewTransactionMetadata(txMetadata, conflicts)) -} - -func getOutput(c echo.Context) error { - outputID, err := httpserver.ParseOutputIDParam(c, api.ParameterOutputID) - if err != nil { - return err - } - - output, err := deps.Protocol.Engines.Main.Get().Ledger.Output(outputID) - if err != nil { - return err - } - - return httpserver.JSONResponse(c, http.StatusOK, NewOutputFromLedgerstateOutput(output)) -} - -func getSlotDetailsByID(c echo.Context) error { - commitmentID, err := httpserver.ParseCommitmentIDParam(c, api.ParameterCommitmentID) - if err != nil { - return err - } - - commitment, err := deps.Protocol.Engines.Main.Get().Storage.Commitments().Load(commitmentID.Slot()) - if err != nil { - return err - } - - if commitment.ID() != commitmentID { - return ierrors.Errorf("commitment in the store for slot %d does not match the given commitmentID (%s != %s)", commitmentID.Slot(), commitment.ID(), commitmentID) - } - - diffs, err := deps.Protocol.Engines.Main.Get().Ledger.SlotDiffs(commitmentID.Slot()) - if err != nil { - return err - } - - return httpserver.JSONResponse(c, http.StatusOK, NewSlotDetails(commitment, diffs)) -} diff --git a/components/dashboard/frontend/.gitignore b/components/dashboard/frontend/.gitignore deleted file mode 100644 index 0bd331b54..000000000 --- a/components/dashboard/frontend/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.vscode -.DS_STORE -node_modules -.module-cache -*.log* -dist \ No newline at end of file diff --git a/components/dashboard/frontend/.prettierrc b/components/dashboard/frontend/.prettierrc deleted file mode 100644 index f38b57120..000000000 --- a/components/dashboard/frontend/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "arrowParens": "always", - "semi": true, - "useTabs": false, - "tabWidth": 2, - "bracketSpacing": true, - "singleQuote": true -} diff --git a/components/dashboard/frontend/README.md b/components/dashboard/frontend/README.md deleted file mode 100644 index 1f0343ab3..000000000 --- a/components/dashboard/frontend/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# iota-core Dashboard - -Programmed using modern web technologies. - -### Dashboard in dev mode - -There are two ways to launch dashboard in development mode: one using Docker, which doesn't require native installation -of any tools, and the other is running dashboard using natively installed tools. Docker approach has only been tested on -Linux. - -#### Docker - -1. Make sure to set `dashboard.dev` to true, to enable iota-core to serve assets from the webpack-dev-server. -2. Run `scripts/dashboard_dev_docker.sh` script. The script should be run from repository root directory. It will - install all needed npm modules and run a webpack-dev-server instance. The script exposes dashboard on port `9090` by - default, so it might be necessary to change it if port is already in use. -3. Set `dashboard.devDashboardAddress` config to IP of container running dashboard in dev mode. IP can be fetched by - running: `docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' iota-core-dashboard-dev` -4. Run iota-core docker-network. Using default port config, you should now be able to access the dashboard under - http://127.0.0.1:8081 - -The Dashboard is hot-reload enabled. - -#### Without Docker - -1. Make sure to set `dashboard.dev` to true, to enable iota-core to serve assets from the webpack-dev-server. -2. Install all needed npm modules via `yarn install`. -3. Run a webpack-dev-server instance by running `yarn start` within the `frontend` directory. -4. Using default port config, you should now be able to access the dashboard under http://127.0.0.1:8081 - -The Dashboard is hot-reload enabled. - -### Pack your changes - -We are using [pkger](https://github.com/markbates/pkger) to wrap all built frontend files into Go files. - -#### Docker - -Docker will mount all necessary files and directories and will create all resulting files in the iota-core repository -folder. This approach has only been tested on Linux. - -In order to build and package dashboard, run `scripts/pkger_docker.sh` from repository root directory. - -#### Without Docker - -1. [Install `pkger`](https://github.com/markbates/pkger#installation) if not already done. -2. Build Dashboard by running `yarn build` within the `frontend` directory. -3. Run `pkger`. -4. `pkged.go` under root directory of iota-core should have been modified. -5. Done. Now you can build iota-core and your Dashboard changes will be included within the binary. - -Alternatively, all necessary commands are compiled into a script `script/pkger.sh` that builds and packages dashboard -using natively installed tools. \ No newline at end of file diff --git a/components/dashboard/frontend/build/a1e7322de4eb5154c0c3.js b/components/dashboard/frontend/build/a1e7322de4eb5154c0c3.js deleted file mode 100644 index 9d732ea03..000000000 --- a/components/dashboard/frontend/build/a1e7322de4eb5154c0c3.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){function r(r){for(var n,l,p=r[0],a=r[1],f=r[2],c=0,s=[];c
Id | \r\nPayload Type | \r\n
\r\n Search for addresses, blocks, transactions, outputs and spends.\r\n
\r\n \r\n