github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/chain/chain.go (about)

     1  package chain
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math/big"
     7  	"sync"
     8  
     9  	"github.com/piotrnar/gocoin/lib/btc"
    10  	"github.com/piotrnar/gocoin/lib/utxo"
    11  )
    12  
    13  var AbortNow bool // set it to true to abort any activity
    14  
    15  type Chain struct {
    16  	Blocks  *BlockDB        // blockchain.dat and blockchain.idx
    17  	Unspent *utxo.UnspentDB // unspent folder
    18  
    19  	BlockTreeRoot   *BlockTreeNode
    20  	blockTreeEnd    *BlockTreeNode
    21  	blockTreeAccess sync.Mutex
    22  	Genesis         *btc.Uint256
    23  
    24  	BlockIndexAccess sync.Mutex
    25  	BlockIndex       map[[btc.Uint256IdxLen]byte]*BlockTreeNode
    26  
    27  	CB NewChanOpts // callbacks used by Unspent database
    28  
    29  	Consensus struct {
    30  		Window, EnforceUpgrade, RejectBlock uint
    31  		MaxPOWBits                          uint32
    32  		MaxPOWValue                         *big.Int
    33  		GensisTimestamp                     uint32
    34  		Enforce_CSV                         uint32 // if non zero CVS verifications will be enforced from this block onwards
    35  		Enforce_SEGWIT                      uint32 // if non zero SegWit verifications will be enforced from this block onwards
    36  		Enforce_Taproot                     uint32 // if non zero Taproot verifications will be enforced from this block onwards
    37  		BIP9_Treshold                       uint32 // It is not really used at this moment, but maybe one day...
    38  		BIP34Height                         uint32
    39  		BIP65Height                         uint32
    40  		BIP66Height                         uint32
    41  	}
    42  }
    43  
    44  type NewChanOpts struct {
    45  	UTXOVolatileMode bool
    46  	UndoBlocks       uint // undo this many blocks when opening the chain
    47  	UTXOCallbacks    utxo.CallbackFunctions
    48  	BlockMinedCB     func(*btc.Block) // used to remove mined txs from memory pool
    49  	BlockUndoneCB    func(*btc.Block) // used to put undone txs back into memory pool
    50  	DoNotRescan      bool             // when set UTXO will not be automatically updated with new block found on disk
    51  	CompressUTXO     bool
    52  	UTXOPrealloc     uint
    53  }
    54  
    55  // NewChainExt is the very first function one should call in order to use this package.
    56  func NewChainExt(dbrootdir string, genesis *btc.Uint256, rescan bool, opts *NewChanOpts, bdbopts *BlockDBOpts) (ch *Chain) {
    57  	ch = new(Chain)
    58  	ch.Genesis = genesis
    59  
    60  	if opts == nil {
    61  		opts = &NewChanOpts{}
    62  	}
    63  
    64  	ch.CB = *opts
    65  
    66  	ch.Consensus.GensisTimestamp = 1231006505
    67  	ch.Consensus.MaxPOWBits = 0x1d00ffff
    68  	ch.Consensus.MaxPOWValue, _ = new(big.Int).SetString("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)
    69  	if ch.testnet() {
    70  		ch.Consensus.BIP34Height = 21111
    71  		ch.Consensus.BIP65Height = 581885
    72  		ch.Consensus.BIP66Height = 330776
    73  		ch.Consensus.Enforce_CSV = 770112
    74  		ch.Consensus.Enforce_SEGWIT = 834624
    75  		ch.Consensus.Enforce_Taproot = 2011968
    76  		ch.Consensus.BIP9_Treshold = 1512
    77  	} else {
    78  		ch.Consensus.BIP34Height = 227931
    79  		ch.Consensus.BIP65Height = 388381
    80  		ch.Consensus.BIP66Height = 363725
    81  		ch.Consensus.Enforce_CSV = 419328
    82  		ch.Consensus.Enforce_SEGWIT = 481824
    83  		ch.Consensus.Enforce_Taproot = 709632
    84  		ch.Consensus.BIP9_Treshold = 1815
    85  	}
    86  
    87  	ch.Blocks = NewBlockDBExt(dbrootdir, bdbopts)
    88  
    89  	ch.Unspent = utxo.NewUnspentDb(&utxo.NewUnspentOpts{
    90  		Dir: dbrootdir, Rescan: rescan, VolatimeMode: opts.UTXOVolatileMode,
    91  		CB: opts.UTXOCallbacks, AbortNow: &AbortNow,
    92  		CompressRecords: opts.CompressUTXO, RecordsPrealloc: opts.UTXOPrealloc})
    93  
    94  	if AbortNow {
    95  		return
    96  	}
    97  
    98  	ch.loadBlockIndex()
    99  	if AbortNow {
   100  		return
   101  	}
   102  
   103  	if rescan {
   104  		ch.SetLast(ch.BlockTreeRoot)
   105  	}
   106  
   107  	if AbortNow {
   108  		return
   109  	}
   110  
   111  	if opts.UndoBlocks > 0 {
   112  		fmt.Println("Undo", opts.UndoBlocks, "block(s) and exit...")
   113  		for opts.UndoBlocks > 0 {
   114  			ch.UndoLastBlock()
   115  			opts.UndoBlocks--
   116  		}
   117  		return
   118  	}
   119  
   120  	// And now re-apply the blocks which you have just reverted :)
   121  	end, _ := ch.BlockTreeRoot.FindFarthestNode()
   122  	if end.Height > ch.LastBlock().Height {
   123  		if !opts.DoNotRescan {
   124  			ch.ParseTillBlock(end)
   125  		}
   126  	} else {
   127  		ch.Unspent.LastBlockHeight = end.Height
   128  	}
   129  
   130  	return
   131  }
   132  
   133  // RebuildGenesisHeader calculates an imaginary header of the genesis block (for Timestamp() and Bits() functions from chain_tree.go).
   134  func (ch *Chain) RebuildGenesisHeader() {
   135  	binary.LittleEndian.PutUint32(ch.BlockTreeRoot.BlockHeader[0:4], 1) // Version
   136  	// [4:36] - prev_block
   137  	// [36:68] - merkle_root
   138  	binary.LittleEndian.PutUint32(ch.BlockTreeRoot.BlockHeader[68:72], ch.Consensus.GensisTimestamp) // Timestamp
   139  	binary.LittleEndian.PutUint32(ch.BlockTreeRoot.BlockHeader[72:76], ch.Consensus.MaxPOWBits)      // Bits
   140  	// [76:80] - nonce
   141  }
   142  
   143  // Idle should be called periodically (i.e. each second)
   144  // when your client is idle, to defragment databases.
   145  func (ch *Chain) Idle() bool {
   146  	ch.Blocks.Idle()
   147  	return ch.Unspent.Idle()
   148  }
   149  
   150  // Stats returns blockchain stats in one string.
   151  func (ch *Chain) Stats() (s string) {
   152  	last := ch.LastBlock()
   153  	ch.BlockIndexAccess.Lock()
   154  	s = fmt.Sprintf("CHAIN: blocks:%d  Height:%d  MedianTime:%d\n",
   155  		len(ch.BlockIndex), last.Height, last.GetMedianTimePast())
   156  	ch.BlockIndexAccess.Unlock()
   157  	s += ch.Blocks.GetStats()
   158  	s += ch.Unspent.GetStats()
   159  	return
   160  }
   161  
   162  // Close closes the databases.
   163  func (ch *Chain) Close() {
   164  	ch.Blocks.Close()
   165  	ch.Unspent.Close()
   166  }
   167  
   168  // testnet returns true if we are on Testnet3 chain.
   169  func (ch *Chain) testnet() bool {
   170  	return ch.Genesis.Hash[0] == 0x43 // it's simple, but works
   171  }
   172  
   173  func (ch *Chain) LastBlock() (res *BlockTreeNode) {
   174  	ch.blockTreeAccess.Lock()
   175  	res = ch.blockTreeEnd
   176  	ch.blockTreeAccess.Unlock()
   177  	return
   178  }
   179  
   180  func (ch *Chain) SetLast(val *BlockTreeNode) {
   181  	ch.blockTreeAccess.Lock()
   182  	ch.blockTreeEnd = val
   183  	ch.blockTreeAccess.Unlock()
   184  	return
   185  }