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 }