Home / Cases / Public/private chain + block explorer development

Public chain/private chain + block explorer development source code full analysis

📅 Last updated: May 2025 ⏱ Reading time: about 20 minutes 👤 By NovaLinkR technical team

BlockchainIt is a distributed ledger technology that realizes decentralized data storage and verification through cryptography, consensus algorithms, and P2P networks. The public chain is open to the world, and anyone can participate. Private chains serve within enterprises or alliance scenarios. Block explorers are visual query tools for on-chain data and are the standard infrastructure for every chain. This article will provide an in-depth analysis of the underlying implementation of public chains/private chains and the complete development process of block explorers from the source code level.

What is a public/private chain?

Public chain and block explorer architecture

Public BlockchainIt is a fully open blockchain network where anyone can participate in node operation, transaction submission, and data verification, representing projects including Bitcoin, Ethereum, Solana, Avalanche, and more. Its core features are permissionless, decentralized, and censorship-resistant.

Private BlockchainIt is a permissioned blockchain network, and the joining and exiting of nodes is controlled by the manager, which is suitable for scenarios such as internal data sharing, supply chain traceability, and financial clearing and settlement. Representative frameworks include Hyperledger Fabric, Quorum, FISCO BCOS, and more.

Comparison of core features

🌐 Public chain — decentralized

Nodes are globally distributed, permissionless to participate, network security is maintained through economic incentives (mining/staking), and the code is fully open-source.

🏢 Private Chain — High performance

The number of nodes is controllable, the consensus efficiency is high, and the TPS can reach thousands to tens of thousands, making it suitable for enterprise-level high-frequency trading scenarios.

🔐 Public chain — censorship resistant

Any transaction is irreversible once confirmed, and no entity can alone prevent the execution of a legitimate transaction.

📋 Private Chain — Compliant and controllable

Meet regulatory requirements such as KYC/AML, and support permission hierarchy, data privacy protection, and audit traceability.

Comparison of public and private chain technology

Contrast dimensions Public Chain Private Chain Consortium
How to participateAnyone can join without permissionRequires authorization, single organization managementMulti-organization co-governance
Consensus mechanismPoW / PoS / DPoSPBFT / Raft / PoAPBFT / HotStuff
TPS15-65000 (depending on the chain)1000-100000+1000-10000
Confirmation timeSeconds to minutesMilliseconds to secondsseconds
Data visibilityCompletely transparentVisible only with authorizationMembers are visible
Incentive mechanismToken rewardsNo token incentives requiredOptional tokens
Typical representativeEthereum, SolanaHyperledger FabricFISCO BCOS, R3 Corda

Detailed explanation of the consensus mechanism

The consensus mechanism is the core engine of blockchain, determining how the network agrees on the state of the ledger. Different scenarios require different consensus algorithm trade-offs.

Mainstream consensus algorithms

⛏️ PoW (Proof of Work)

Computational hashing challenges compete for block power, which is the most secure but consumes a lot of energy. Bitcoin, Ethereum Classic adopted. Block time: ~10 minutes (BTC) / ~13 seconds (ETH old).

🥩 PoS (Proof of Stake)

The block probability is allocated according to the number of staked tokens and time, with low energy consumption and high efficiency. Ethereum 2.0, Cardano adopted. Minimum stake of 32 ETH to become a validator.

🗳️ DPoS (Delegated Proof of Stake)

Coin holders vote for supernodes to produce blocks on their behalf, with high TPS but low decentralization. EOS (21 nodes), TRON adopted.

🤝 PBFT (Byzantine Fault Tolerance)

Multiple rounds of voting among nodes to reach consensus, tolerating 1/3 of malicious nodes. Suitable for consortium chains, Hyperledger Fabric adoption. Low latency but limited number of nodes.

Consensus algorithm source code structure (PoS example)

// validator_set.go - 验证者集合管理
type ValidatorSet struct {
    Validators []*Validator
    TotalStake uint64
}

type Validator struct {
    Address    common.Address
    Stake      uint64
    Commission float64
    Active     bool
}

// 按权重选择出块者(加权随机)
func (vs *ValidatorSet) SelectProposer(seed []byte) *Validator {
    hash := crypto.Keccak256(seed)
    target := new(big.Int).SetBytes(hash)
    target.Mod(target, big.NewInt(int64(vs.TotalStake)))

    cumulative := uint64(0)
    for _, v := range vs.Validators {
        if !v.Active { continue }
        cumulative += v.Stake
        if cumulative > target.Uint64() {
            return v
        }
    }
    return vs.Validators[0]
}

// 验证区块签名
func (vs *ValidatorSet) VerifyBlockSignature(block *Block) error {
    proposer := vs.GetValidator(block.ProposerAddress)
    if proposer == nil {
        return ErrUnknownValidator
    }
    if !crypto.VerifySignature(proposer.PublicKey, block.Hash(), block.Signature) {
        return ErrInvalidSignature
    }
    return nil
}

Chain architecture design

A complete blockchain can be divided into the following technical levels from the bottom to the top:

Application layer (DApp/Explorer)
WalletBlock explorerDeFi protocolsNFT marketplaceGovernance panel
Interface Layer (RPC/WebSocket)
JSON-RPCGraphQLWebSocket subscriptionREST API
Consensus
PoS / PoW / PBFTValidator managementEpoch rotationSlashing punishment
Execution Layer (EVM/WASM)
Trade executionState transitionsGas meteringPrecompiled contracts
Network Layer (P2P)
Node discoveryBlock broadcastingTransaction communicationState synchronization
Storage Layer (State/Block)
LevelDB / RocksDBMerkle Patricia TrieBlock storageStatus snapshot

node development core modules

Blockchain nodes are the fundamental building blocks of the network, responsible for receiving transactions, validating blocks, maintaining state, and participating in consensus. The following is the source code structure of the node core module:

Block and transaction data structures

// block.go - 区块结构定义
type BlockHeader struct {
    ParentHash   common.Hash    `json:"parentHash"`
    StateRoot    common.Hash    `json:"stateRoot"`
    TxRoot       common.Hash    `json:"transactionsRoot"`
    ReceiptRoot  common.Hash    `json:"receiptsRoot"`
    Coinbase     common.Address `json:"miner"`
    Difficulty   *big.Int       `json:"difficulty"`
    Number       *big.Int       `json:"number"`
    GasLimit     uint64         `json:"gasLimit"`
    GasUsed      uint64         `json:"gasUsed"`
    Timestamp    uint64         `json:"timestamp"`
    Nonce        [8]byte        `json:"nonce"`
}

type Block struct {
    Header       *BlockHeader
    Transactions []*Transaction
    Uncles       []*BlockHeader
}

type Transaction struct {
    Nonce    uint64          `json:"nonce"`
    GasPrice *big.Int        `json:"gasPrice"`
    Gas      uint64          `json:"gas"`
    To       *common.Address `json:"to"`
    Value    *big.Int        `json:"value"`
    Data     []byte          `json:"input"`
    V, R, S  *big.Int        // 签名数据
}

// 计算区块哈希
func (b *Block) Hash() common.Hash {
    return crypto.Keccak256Hash(rlp.Encode(b.Header))
}

// 验证区块有效性
func (b *Block) Validate(parent *Block) error {
    if b.Header.ParentHash != parent.Hash() {
        return ErrInvalidParentHash
    }
    if b.Header.Number.Uint64() != parent.Header.Number.Uint64()+1 {
        return ErrInvalidBlockNumber
    }
    if b.Header.Timestamp <= parent.Header.Timestamp {
        return ErrInvalidTimestamp
    }
    return nil
}

Trading Pool (TxPool)

// txpool.go - 交易池管理
type TxPool struct {
    pending map[common.Address]*txList  // 可执行交易
    queue   map[common.Address]*txList  // 等待 nonce 连续
    mu      sync.RWMutex
    maxSize int
}

func (pool *TxPool) AddTransaction(tx *Transaction) error {
    pool.mu.Lock()
    defer pool.mu.Unlock()

    // 1. 验证签名
    sender, err := tx.RecoverSender()
    if err != nil { return ErrInvalidSignature }

    // 2. 验证 nonce
    currentNonce := pool.stateDB.GetNonce(sender)
    if tx.Nonce < currentNonce { return ErrNonceTooLow }

    // 3. 验证余额
    cost := new(big.Int).Mul(tx.GasPrice, new(big.Int).SetUint64(tx.Gas))
    cost.Add(cost, tx.Value)
    if pool.stateDB.GetBalance(sender).Cmp(cost) < 0 {
        return ErrInsufficientFunds
    }

    // 4. 加入交易池
    if tx.Nonce == currentNonce {
        pool.pending[sender].Add(tx)
    } else {
        pool.queue[sender].Add(tx)
    }
    return nil
}

Smart contract virtual machines

Smart contract virtual machines (VMs) are the execution environment for on-chain code. EVM (Ethereum Virtual Machine) is the most mainstream implementation, with almost all EVM-compatible chains based on the same bytecode specification.

EVM execution model

  • Stacked architecture: Maximum operand stack depth of 1024, 256 bits (32 bytes) per element
  • Deterministic execution: The same input must produce the same output to ensure the same state of the whole network
  • gas mechanism: Each instruction consumes fixed gas to prevent infinite loops and resource abuse
  • State isolation: Each contract has independent storage space, read and write through SSTORE/SLOAD

EVM core instruction set implementation

// evm/interpreter.go - EVM 解释器核心循环
func (evm *EVM) Execute(contract *Contract, input []byte) ([]byte, error) {
    var (
        stack   = newStack()
        memory  = newMemory()
        pc      uint64 = 0
        gas     uint64 = contract.Gas
    )

    code := contract.Code
    for pc < uint64(len(code)) {
        op := OpCode(code[pc])
        operation := evm.jumpTable[op]

        // Gas 扣费
        if gas < operation.gasCost {
            return nil, ErrOutOfGas
        }
        gas -= operation.gasCost

        // 执行指令
        switch op {
        case ADD:
            a, b := stack.Pop(), stack.Pop()
            stack.Push(new(big.Int).Add(a, b))
        case SSTORE:
            key, value := stack.Pop(), stack.Pop()
            evm.StateDB.SetState(contract.Address, key, value)
        case CALL:
            // 跨合约调用
            addr, value, inOffset, inSize := stack.Pop4()
            ret, err := evm.Call(contract, addr, input[inOffset:inOffset+inSize], gas/64, value)
            stack.Push(ret)
        case RETURN:
            offset, size := stack.Pop(), stack.Pop()
            return memory.GetSlice(offset, size), nil
        }
        pc++
    }
    return nil, nil
}

Custom precompiled contracts

Precompiled contracts are high-performance contracts built into nodes for cryptographic operations, cross-chain bridging, and more:

// precompiles.go - 自定义预编译合约注册
var PrecompiledContracts = map[common.Address]PrecompiledContract{
    common.HexToAddress("0x01"): &ecRecover{},     // 签名恢复
    common.HexToAddress("0x02"): &sha256hash{},    // SHA256
    common.HexToAddress("0x03"): &ripemd160hash{}, // RIPEMD160
    common.HexToAddress("0x09"): &blake2F{},       // Blake2
    // 自定义:跨链验证
    common.HexToAddress("0x20"): &crossChainVerifier{},
}

P2P network layer

P2P networks are the communication foundation of blockchain, responsible for node discovery, block broadcasting, transaction propagation, and state synchronization.

Node Discovery Protocol (Kademlia DHT)

// p2p/discovery.go - 节点发现
type DiscoveryProtocol struct {
    table    *KademliaTable
    udpConn  *net.UDPConn
    bootNodes []*Node
}

func (d *DiscoveryProtocol) FindNeighbors(target NodeID) []*Node {
    closest := d.table.FindClosest(target, 16)
    var results []*Node

    for _, node := range closest {
        // 发送 FindNode 请求
        resp, err := d.sendFindNode(node, target)
        if err != nil { continue }
        results = append(results, resp.Nodes...)
    }

    // 更新路由表
    for _, n := range results {
        d.table.AddNode(n)
    }
    return d.table.FindClosest(target, 16)
}

// 区块广播
func (srv *Server) BroadcastBlock(block *Block) {
    peers := srv.GetPeers()
    // 对 sqrt(N) 个节点发送完整区块
    fullBroadcast := int(math.Sqrt(float64(len(peers))))
    for i, peer := range peers {
        if i < fullBroadcast {
            peer.SendBlock(block)
        } else {
            // 其余节点仅发送区块哈希
            peer.SendBlockHash(block.Hash())
        }
    }
}

Block explorer overview

Block ExplorerIt is the "search engine" of the blockchain network, providing users with visual query capabilities for on-chain data. It is the infrastructure that must be supported by every public/private chain after it is launched.

Core features:

🔍 Block query

Query block information by block height/hash: timestamp, number of transactions, gas consumption, block reward, validator, etc.

💳 Transaction tracking

Query the complete execution details of any transaction: sender, receiver, gas usage, contract call chain, event log.

📊 Address portrait

Display the address's balance, transaction history, token holdings, and contract interaction records, and support the tag system to mark known addresses.

📝 Contract validation

Submit the contract source code for bytecode matching verification, and after verification, the source code and ABI will be disclosed to support online interaction.

📈 Network statistics

Macro indicators such as network TPS, number of active addresses, gas price trends, token distribution, and DeFi TVL are displayed in real time.

🔔 Address monitoring

Users can subscribe to transaction notifications at specified addresses, and support email/webhook push, which is suitable for whale monitoring and security alerts.

Block explorer system architecture

Front-end display layer
React / Next.jsReal-time data panelTransaction visualizationContract interface
API service layer
RESTful APIGraphQLWebSocket pushRate limiting
Data indexing layer
Block synchronizerTransaction parserEvent decoderContract Analyzer
Storage tier
PostgreSQLElasticsearchRedis cacheTimescaleDB
Data source layer
Full node RPCArchive NodeEvent LogTrace API

Block explorer source code implementation

Block synchronization engine

// indexer/sync_engine.go - 区块同步核心
type SyncEngine struct {
    rpcClient   *ethclient.Client
    db          *gorm.DB
    redis       *redis.Client
    currentHead uint64
    batchSize   int
}

func (s *SyncEngine) Start(ctx context.Context) error {
    // 获取已索引的最新高度
    s.currentHead = s.db.GetLatestBlockNumber()
    log.Info("Starting sync from block", "number", s.currentHead)

    for {
        select {
        case <-ctx.Done():
            return nil
        default:
            chainHead, _ := s.rpcClient.BlockNumber(ctx)
            if s.currentHead >= chainHead {
                // 已追上最新区块,切换为实时模式
                s.subscribeNewBlocks(ctx)
                continue
            }
            // 批量同步历史区块
            s.syncBatch(ctx, s.currentHead+1, min(s.currentHead+uint64(s.batchSize), chainHead))
        }
    }
}

func (s *SyncEngine) syncBatch(ctx context.Context, from, to uint64) {
    var wg sync.WaitGroup
    blocks := make([]*BlockData, to-from+1)

    for i := from; i <= to; i++ {
        wg.Add(1)
        go func(num uint64) {
            defer wg.Done()
            block, _ := s.rpcClient.BlockByNumber(ctx, big.NewInt(int64(num)))
            receipts, _ := s.rpcClient.BatchGetReceipts(ctx, block.Transactions())
            blocks[num-from] = &BlockData{Block: block, Receipts: receipts}
        }(i)
    }
    wg.Wait()

    // 批量写入数据库
    s.db.BatchInsertBlocks(blocks)
    s.currentHead = to
}

Transaction parser

// indexer/tx_parser.go - 交易解析与分类
type TxParser struct {
    abiRegistry map[common.Address]*abi.ABI
}

func (p *TxParser) ParseTransaction(tx *types.Transaction, receipt *types.Receipt) *ParsedTx {
    result := &ParsedTx{
        Hash:      tx.Hash(),
        From:      getSender(tx),
        To:        tx.To(),
        Value:     tx.Value(),
        GasUsed:   receipt.GasUsed,
        Status:    receipt.Status == 1,
        Timestamp: time.Now(),
    }

    // 识别交易类型
    if tx.To() == nil {
        result.Type = "contract_creation"
        result.ContractAddress = receipt.ContractAddress
    } else if len(tx.Data()) >= 4 {
        result.Type = "contract_call"
        methodID := tx.Data()[:4]
        // 解码合约方法
        if abi, ok := p.abiRegistry[*tx.To()]; ok {
            method, _ := abi.MethodById(methodID)
            result.MethodName = method.Name
            result.DecodedInput, _ = method.Inputs.Unpack(tx.Data()[4:])
        }
    } else {
        result.Type = "transfer"
    }

    // 解析事件日志
    for _, log := range receipt.Logs {
        event := p.decodeEvent(log)
        result.Events = append(result.Events, event)
    }
    return result
}

Front-end data presentation (React component)

// components/BlockList.tsx - 实时区块列表
export function BlockList() {
  const [blocks, setBlocks] = useState<Block[]>([]);

  useEffect(() => {
    // WebSocket 实时推送新区块
    const ws = new WebSocket('wss://explorer-api.example.com/ws');
    ws.onmessage = (event) => {
      const newBlock = JSON.parse(event.data);
      setBlocks(prev => [newBlock, ...prev.slice(0, 19)]);
    };
    return () => ws.close();
  }, []);

  return (
    <div className="block-list">
      {blocks.map(block => (
        <div key={block.number} className="block-card">
          <span className="block-number">#{block.number}</span>
          <span className="block-txs">{block.txCount} txs</span>
          <span className="block-time">{timeAgo(block.timestamp)}</span>
          <span className="block-validator">{shortAddr(block.validator)}</span>
        </div>
      ))}
    </div>
  );
}

RPC interface design

RPC (Remote Procedure Call) interfaces serve as a bridge for external applications to interact with blockchain nodes. The standard JSON-RPC 2.0 protocol is the industry's common solution.

Core RPC method

Method nameFunctionParameters
eth_blockNumberGet the latest block heightNone
eth_getBlockByNumberGet blocks by heightblockNumber, fullTx
eth_getTransactionByHashPress the hash to get the transactiontxHash
eth_getBalanceCheck the address balanceaddress, blockNumber
eth_callRead-only call contractcallData, blockNumber
eth_sendRawTransactionBroadcast signature transactionssignedTxData
eth_getLogsQuery the event logfilter (address, topics, range)
eth_subscribeSubscribe to live eventstype (newHeads, logs, pendingTx)

Custom extended RPC

// rpc/custom_api.go - 自定义浏览器专用 RPC
type ExplorerAPI struct {
    db    *gorm.DB
    cache *redis.Client
}

// 获取地址完整画像
func (api *ExplorerAPI) GetAddressProfile(address common.Address) (*AddressProfile, error) {
    profile := &AddressProfile{
        Address:     address,
        Balance:     api.getBalance(address),
        TxCount:     api.db.CountTransactions(address),
        TokenHoldings: api.getTokenBalances(address),
        IsContract:  api.isContract(address),
    }
    if profile.IsContract {
        profile.ContractInfo = api.getContractMeta(address)
        profile.Creator = api.getContractCreator(address)
    }
    return profile, nil
}

// 获取网络实时统计
func (api *ExplorerAPI) GetNetworkStats() *NetworkStats {
    return &NetworkStats{
        TPS:            api.cache.Get("current_tps"),
        ActiveAddresses: api.cache.Get("daily_active_addresses"),
        TotalTx:        api.db.CountAllTransactions(),
        GasPrice:       api.getAvgGasPrice(),
        ValidatorCount: api.getActiveValidators(),
    }
}

Deployment and Operations

Node deployment architecture

  • Full Node: Stores complete blockchain data, verifies all transactions and blocks, and participates in P2P networks
  • Archive Node: Preserves all historical states, supports status queries at any block height, and is a must-have for browsers
  • Light Node: Only store block headers, verify data through Merkle Proof, suitable for mobile wallets
  • Validator Node: Participating in consensus block production requires high availability and security

O&M monitoring points

📡 Node health monitoring

Block synchronization latency, number of peers, memory/disk usage, RPC response time. Visualize with Prometheus + Grafana.

🔄 Automatic failover

Node crashes are automatically restarted, snapshot resumed, and standby nodes are switched. Docker + Kubernetes orchestration.

💾 Data backup strategy

Regular snapshots, incremental backups, and off-site disaster recovery. If the data volume of the archive node is large (TB), you need to plan storage expansion.

🔒 Safety reinforcement

RPC port restricted access, DDoS protection, key HSM hosting, signature service isolation.

Recommended technology stack

moduleTechnical selectionDescription
Chain coreGo / RustGo ecosystem maturity (Geth), Rust high performance (Substrate/Reth)
Consensus engineTendermint / HotStuffBFT is similar to consensus and has a strong endgame
Virtual machinesEVM / WASMEVM compatibility is the largest, and WASM performance is better
P2P networkinglibp2p / devp2plibp2p is modular, devp2p is native to Ethereum
storage engineLevelDB / RocksDB / PebbleThe LSM-Tree structure is suitable for write-intensive scenarios
Browser backendGo / Node.js + PostgreSQLHigh concurrency index + relational query
Browser front-endNext.js + TailwindCSSSSR boosts SEO, Tailwind develops quickly
Search engineElasticsearchTransaction/address/contract full text search
MonitoringPrometheus + Grafana + PagerDutyMetric collection + visualization + alarm
deploymentDocker + Kubernetes + TerraformContainerization + Orchestration + Infrastructure as Code

Development process

Based on the NovaLinkR team's experience in delivering public chain and browser projects, the complete development process is as follows:

01

Requirements definition and technology selection

Determine the chain type (public/private/consortium chain), consensus mechanism, VM type, target TPS, tokenomics model. Output technical white paper.

02

Core protocol development

Implement underlying modules such as block structure, transaction processing, consensus engine, P2P network, and state storage to build a testnet.

03

Smart contract VM integration

Integrate EVM or WASM virtual machines for gas metering, precompiled contracts, cross-contract calls, and state management.

04

Block explorer development

Build data indexing services, API gateways, and front-end interfaces to achieve a complete query experience for blocks, transactions, addresses, and contracts.

05

Safety audit and stress testing

Formal verification of consensus security, smart contract auditing, P2P network attack simulation, and high concurrency stress testing.

06

Mainnet launch and ecological construction

Mainnet deployment, browser launch, wallet adaptation, developer documentation, faucet (Faucet) construction, community operation.

💡 Need professional public/private chain development services?

The NovaLinkR team has full-stack development capabilities from the underlying protocol to the block explorer and has successfully delivered multiple mainnet projects.Contact us todayGet free technical consultation and customized solutions.