Building a CLI Explorer for Your Own Blockchain

You’ve built a blockchain. It stores blocks, links them with hashes, and maybe even holds transactions or logs. But how do you interact with it easily?

Let’s fix that by building a CLI explorer — a command-line tool to inspect your blockchain data like a mini version of Etherscan or Blockstream Explorer.

In this article, we’ll:

  • Design a simple blockchain model in Go
  • Create a CLI tool to explore it
  • Add commands like list, block and verify

You will find the code for the CLI in the next section.

Go example

package main

import (
  "crypto/sha256"
  "encoding/hex"
  "flag"
  "fmt"
  "time"
)

// Block represents a single block in the blockchain
type Block struct {
  Index     int
  Timestamp string
  Data      string
  PrevHash  string
  Hash      string
}

// Blockchain is a slice of blocks
type Blockchain struct {
  Blocks []Block
}

// calculateHash creates a SHA-256 hash of a block's contents
func calculateHash(block Block) string {
  record := fmt.Sprintf("%d%s%s%s", block.Index, block.Timestamp, block.Data, block.PrevHash)
  hash := sha256.Sum256([]byte(record))
  return hex.EncodeToString(hash[:])
}

// generateGenesisBlock creates the first block in the chain
func generateGenesisBlock() Block {
  block := Block{
    Index:     0,
    Timestamp: time.Now().Format(time.RFC3339),
    Data:      "Genesis Block",
    PrevHash:  "",
  }
  block.Hash = calculateHash(block)
  return block
}

// generateBlock creates a new block from the previous one
func generateBlock(prevBlock Block, data string) Block {
  newBlock := Block{
    Index:     prevBlock.Index + 1,
    Timestamp: time.Now().Format(time.RFC3339),
    Data:      data,
    PrevHash:  prevBlock.Hash,
  }
  newBlock.Hash = calculateHash(newBlock)
  return newBlock
}

// verifyBlockchain checks that the blockchain is valid
func verifyBlockchain(chain []Block) bool {
  for i := 1; i < len(chain); i++ {
    if chain[i].PrevHash != chain[i-1].Hash {
      return false
    }
    if chain[i].Hash != calculateHash(chain[i]) {
      return false
    }
  }
  return true
}

func main() {
  // CLI flags
  list := flag.Bool("list", false, "List all blocks")
  blockIndex := flag.Int("block", -1, "Show a block by index")
  verify := flag.Bool("verify", false, "Verify the integrity of the blockchain")

  flag.Parse()

  // Simulate a blockchain with 3 blocks
  chain := Blockchain{Blocks: []Block{generateGenesisBlock()}}
  chain.Blocks = append(chain.Blocks, generateBlock(chain.Blocks[len(chain.Blocks)-1], "Alice pays Bob 5 BTC"))
  chain.Blocks = append(chain.Blocks, generateBlock(chain.Blocks[len(chain.Blocks)-1], "Bob pays Carol 2 BTC"))

  // CLI logic
  if *list {
    fmt.Println("Blockchain:")
    for _, b := range chain.Blocks {
      fmt.Printf("Index: %d | Data: %s | Hash: %.10s...\n", b.Index, b.Data, b.Hash)
    }
  } else if *blockIndex >= 0 && *blockIndex < len(chain.Blocks) {
    b := chain.Blocks[*blockIndex]
    fmt.Printf("Block %d\n", b.Index)
    fmt.Printf("Timestamp: %s\nData: %s\nHash: %s\nPrevHash: %s\n", b.Timestamp, b.Data, b.Hash, b.PrevHash)
  } else if *verify {
    if verifyBlockchain(chain.Blocks) {
      fmt.Println("✅ Blockchain is valid.")
    } else {
      fmt.Println("❌ Blockchain is INVALID.")
    }
  } else {
    fmt.Println("Usage:")
    fmt.Println("  -list            List all blocks")
    fmt.Println("  -block    Show details of a block")
    fmt.Println("  -verify          Check chain integrity")
  }
}

Example of usage:

$ go run explorer.go -list
Blockchain:
Index: 0 | Data: Genesis Block | Hash: 1a2b3c4d...
Index: 1 | Data: Alice pays Bob 5 BTC | Hash: 2b3c4d5e...
Index: 2 | Data: Bob pays Carol 2 BTC | Hash: 3c4d5e6f...

$ go run explorer.go -block 1
Block 1
Timestamp: 2025-04-14T17:00:00Z
Data: Alice pays Bob 5 BTC
Hash: ...
PrevHash: ...

$ go run explorer.go -verify
✅ Blockchain is valid.