// Copyright (c) 2017-2026 Lethean (https://lt.hn) // // Licensed under the European Union Public Licence (EUPL) version 1.2. // SPDX-License-Identifier: EUPL-1.2 package blockchain import ( "context" "os" "os/signal" "path/filepath" "sync" "syscall" coreerr "dappco.re/go/core/log" cli "dappco.re/go/core/cli/pkg/cli" store "dappco.re/go/core/store" "dappco.re/go/core/blockchain/chain" "dappco.re/go/core/blockchain/tui" "github.com/spf13/cobra" ) const chainExplorerFrameTitle = "Chain Explorer" // newChainExplorerCommand builds the interactive `chain explorer` command. // // Example: // // core-chain chain explorer --data-dir ~/.lethean/chain // // Use it alongside `AddChainCommands` to expose the TUI node view. func newChainExplorerCommand(dataDir, seed *string, testnet *bool) *cobra.Command { return &cobra.Command{ Use: chainExplorerCommandName, Short: chainExplorerCommandShort, Long: chainExplorerCommandLong, Example: chainExplorerExample, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return runChainExplorer(*dataDir, *seed, *testnet) }, } } func runChainExplorer(dataDir, seed string, testnet bool) error { if err := validateChainRuntimeInputs(dataDir, seed); err != nil { return err } if err := ensureChainDataDirExists(dataDir); err != nil { return err } dbPath := filepath.Join(dataDir, "chain.db") chainStore, err := store.New(dbPath) if err != nil { return coreerr.E("runChainExplorer", "open store", err) } defer chainStore.Close() blockchain := chain.New(chainStore) chainConfig, hardForks, resolvedSeed := chainConfigForSeed(testnet, seed) ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() runChainSyncLoop(ctx, blockchain, &chainConfig, hardForks, resolvedSeed) }() node := tui.NewNode(blockchain) status := tui.NewStatusModel(node) explorer := tui.NewExplorerModel(blockchain) hints := tui.NewKeyHintsModel() explorerFrame := cli.NewFrame(chainExplorerFrameTitle) explorerFrame.Header(status) explorerFrame.Content(explorer) explorerFrame.Footer(hints) explorerFrame.Run() cancel() // Signal the sync loop to stop. wg.Wait() // Wait for it before closing store. return nil }