github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/chain/chain_tree.go (about) 1 package chain 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "sort" 7 "time" 8 9 "github.com/piotrnar/gocoin/lib/btc" 10 "github.com/piotrnar/gocoin/lib/others/sys" 11 ) 12 13 type BlockTreeNode struct { 14 BlockHash *btc.Uint256 15 Height uint32 16 Parent *BlockTreeNode 17 Childs []*BlockTreeNode 18 19 BlockSize uint32 // if this is zero, only header is known so far 20 TxCount uint32 21 SigopsCost uint32 22 23 BlockHeader [80]byte 24 25 Trusted sys.SyncBool 26 } 27 28 func (ch *Chain) ParseTillBlock(end *BlockTreeNode) { 29 var crec *BlckCachRec 30 var er error 31 var trusted bool 32 var tot_bytes uint64 33 34 last := ch.LastBlock() 35 var total_size_to_process uint64 36 fmt.Print("Calculating size of blockchain overhead...") 37 for n := end; n != nil && n != last; n = n.Parent { 38 l, _ := ch.Blocks.BlockLength(n.BlockHash, false) 39 total_size_to_process += uint64(l) 40 } 41 fmt.Println("\rApplying", total_size_to_process>>20, "MB of transactions data from", end.Height-last.Height, "blocks to UTXO.db") 42 sta := time.Now() 43 prv := sta 44 for !AbortNow && last != end { 45 cur := time.Now() 46 if cur.Sub(prv) >= 10*time.Second { 47 mbps := float64(tot_bytes) / float64(cur.Sub(sta)/1e3) 48 sec_left := int64(float64(total_size_to_process) / 1e6 / mbps) 49 fmt.Printf("ParseTillBlock %d / %d ... %.2f MB/s - %d:%02d:%02d left (%d)\n", last.Height, 50 end.Height, mbps, sec_left/3600, (sec_left/60)%60, sec_left%60, cur.Unix()-sta.Unix()) 51 prv = cur 52 } 53 54 nxt := last.FindPathTo(end) 55 if nxt == nil { 56 break 57 } 58 59 if nxt.BlockSize == 0 { 60 println("ParseTillBlock: ", nxt.Height, nxt.BlockHash.String(), "- not yet commited") 61 break 62 } 63 64 crec, trusted, er = ch.Blocks.BlockGetInternal(nxt.BlockHash, true) 65 if er != nil { 66 panic("Db.BlockGet(): " + er.Error()) 67 } 68 tot_bytes += uint64(len(crec.Data)) 69 l, _ := ch.Blocks.BlockLength(nxt.BlockHash, false) 70 total_size_to_process -= uint64(l) 71 72 bl, er := btc.NewBlock(crec.Data) 73 if er != nil { 74 ch.DeleteBranch(nxt, nil) 75 break 76 } 77 bl.Height = nxt.Height 78 79 // Recover the flags to be used when verifying scripts for non-trusted blocks (stored orphaned blocks) 80 ch.ApplyBlockFlags(bl) 81 82 // Do not recover MedianPastTime as it is only checked in PostCheckBlock() 83 // that had to be done before the block was stored on disk. 84 85 er = bl.BuildTxList() 86 if er != nil { 87 ch.DeleteBranch(nxt, nil) 88 break 89 } 90 91 bl.Trusted.Store(trusted) 92 93 changes, sigopscost, er := ch.ProcessBlockTransactions(bl, nxt.Height, end.Height) 94 if er != nil { 95 println("ProcessBlockTransactionsB", nxt.BlockHash.String(), nxt.Height, er.Error()) 96 ch.DeleteBranch(nxt, nil) 97 break 98 } 99 nxt.SigopsCost = sigopscost 100 if !trusted { 101 ch.Blocks.BlockTrusted(bl.Hash.Hash[:]) 102 } 103 104 ch.Unspent.CommitBlockTxs(changes, bl.Hash.Hash[:]) 105 106 ch.SetLast(nxt) 107 last = nxt 108 109 if ch.CB.BlockMinedCB != nil { 110 bl.Height = nxt.Height 111 bl.LastKnownHeight = end.Height 112 ch.CB.BlockMinedCB(bl) 113 } 114 } 115 116 if !AbortNow && last != end { 117 end, _ = ch.BlockTreeRoot.FindFarthestNode() 118 fmt.Println("ParseTillBlock failed - now go to", end.Height) 119 ch.MoveToBlock(end) 120 } 121 } 122 123 func (n *BlockTreeNode) BlockVersion() uint32 { 124 return binary.LittleEndian.Uint32(n.BlockHeader[0:4]) 125 } 126 127 func (n *BlockTreeNode) Timestamp() uint32 { 128 return binary.LittleEndian.Uint32(n.BlockHeader[68:72]) 129 } 130 131 func (n *BlockTreeNode) Bits() uint32 { 132 return binary.LittleEndian.Uint32(n.BlockHeader[72:76]) 133 } 134 135 // GetMedianTimePast returns the median time of the last 11 blocks. 136 func (pindex *BlockTreeNode) GetMedianTimePast() uint32 { 137 var pmedian [MedianTimeSpan]int 138 pbegin := MedianTimeSpan 139 pend := MedianTimeSpan 140 for i := 0; i < MedianTimeSpan && pindex != nil; i++ { 141 pbegin-- 142 pmedian[pbegin] = int(pindex.Timestamp()) 143 pindex = pindex.Parent 144 } 145 sort.Ints(pmedian[pbegin:pend]) 146 return uint32(pmedian[pbegin+((pend-pbegin)/2)]) 147 } 148 149 // FindFarthestNode looks for the farthest node. 150 func (n *BlockTreeNode) FindFarthestNode() (*BlockTreeNode, int) { 151 //fmt.Println("FFN:", n.Height, "kids:", len(n.Childs)) 152 if len(n.Childs) == 0 { 153 return n, 0 154 } 155 res, depth := n.Childs[0].FindFarthestNode() 156 if len(n.Childs) > 1 { 157 for i := 1; i < len(n.Childs); i++ { 158 _re, _dept := n.Childs[i].FindFarthestNode() 159 if _dept > depth { 160 res = _re 161 depth = _dept 162 } 163 } 164 } 165 return res, depth + 1 166 } 167 168 // FindPathTo returns the next node that leads to the given destination. 169 func (n *BlockTreeNode) FindPathTo(end *BlockTreeNode) *BlockTreeNode { 170 if n == end { 171 return nil 172 } 173 174 if end.Height <= n.Height { 175 panic("FindPathTo: End block is not higher then current") 176 } 177 178 if len(n.Childs) == 0 { 179 panic("FindPathTo: Unknown path to block " + end.BlockHash.String()) 180 } 181 182 if len(n.Childs) == 1 { 183 return n.Childs[0] // if there is only one child, do it fast 184 } 185 186 for { 187 // more then one children: go from the end until you reach the current node 188 if end.Parent == n { 189 return end 190 } 191 end = end.Parent 192 } 193 } 194 195 // HasAllParents checks whether the given node has all its parent blocks already comitted. 196 func (ch *Chain) HasAllParents(dst *BlockTreeNode) bool { 197 for { 198 dst = dst.Parent 199 if ch.OnActiveBranch(dst) { 200 return true 201 } 202 if dst == nil || dst.TxCount == 0 { 203 return false 204 } 205 } 206 } 207 208 // OnActiveBranch returns true if the given node is on the active branch. 209 func (ch *Chain) OnActiveBranch(dst *BlockTreeNode) bool { 210 top := ch.LastBlock() 211 for { 212 if dst == top { 213 return true 214 } 215 if dst.Height >= top.Height { 216 return false 217 } 218 top = top.Parent 219 } 220 } 221 222 // MoveToBlock performs a channel reorg. 223 func (ch *Chain) MoveToBlock(dst *BlockTreeNode) { 224 cur := dst 225 lastblock := ch.LastBlock() 226 for cur.Height > lastblock.Height { 227 cur = cur.Parent 228 // if cur.TxCount is zero, it means we dont yet have this block's data 229 if cur.TxCount == 0 { 230 fmt.Println("MoveToBlock cannot continue A1") 231 fmt.Println("Trying to go:", dst.BlockHash.String(), dst.Height) 232 fmt.Println("Cannot go at:", cur.BlockHash.String(), cur.Height) 233 return 234 } 235 } 236 237 for lastblock.Height > cur.Height { // this is a rare case when the new branch has less blocks but more POW 238 lastblock = lastblock.Parent 239 // if cur.TxCount is zero, it means we dont yet have this block's data 240 if lastblock.TxCount == 0 { 241 fmt.Println("MoveToBlock cannot continue A2") 242 fmt.Println("Trying to go:", dst.BlockHash.String(), dst.Height) 243 fmt.Println("Cannot go at:", cur.BlockHash.String(), cur.Height) 244 return 245 } 246 } 247 248 // At this point both "ch.blockTreeEnd" and "cur" should be at the same height 249 for tmp := lastblock; tmp != cur; tmp = tmp.Parent { 250 if cur.Parent.TxCount == 0 { 251 fmt.Println("MoveToBlock cannot continue B") 252 fmt.Println("Trying to go:", dst.BlockHash.String(), dst.Height) 253 fmt.Println("Cannot go at:", cur.Parent.BlockHash.String(), cur.Parent.Height) 254 return 255 } 256 cur = cur.Parent 257 } 258 259 // At this point "cur" is at the highest common block 260 for ch.LastBlock() != cur { 261 if AbortNow { 262 return 263 } 264 ch.UndoLastBlock() 265 } 266 ch.ParseTillBlock(dst) 267 } 268 269 func (ch *Chain) UndoLastBlock() { 270 last := ch.LastBlock() 271 fmt.Println("Undo block", last.Height, last.BlockHash.String(), last.BlockSize>>10, "KB") 272 273 crec, _, er := ch.Blocks.BlockGetInternal(last.BlockHash, true) 274 if er != nil { 275 panic(er.Error()) 276 } 277 278 bl, er := btc.NewBlock(crec.Data) 279 if er != nil { 280 panic("UndoLastBlock: NewBlock() should not fail with block from disk") 281 } 282 283 er = bl.BuildTxList() 284 if er != nil { 285 panic("UndoLastBlock: BuildTxList() should not fail with block from disk") 286 } 287 288 ch.Unspent.UndoBlockTxs(bl, last.Parent.BlockHash.Hash[:]) 289 if ch.CB.BlockUndoneCB != nil { 290 ch.CB.BlockUndoneCB(bl) 291 } 292 ch.SetLast(last.Parent) 293 } 294 295 // make sure ch.BlockIndexAccess is locked before calling it 296 func (cur *BlockTreeNode) delAllChildren(ch *Chain, deleteCallback func(*btc.Uint256)) { 297 for i := range cur.Childs { 298 if deleteCallback != nil { 299 deleteCallback(cur.Childs[i].BlockHash) 300 } 301 cur.Childs[i].delAllChildren(ch, deleteCallback) 302 delete(ch.BlockIndex, cur.Childs[i].BlockHash.BIdx()) 303 ch.Blocks.BlockInvalid(cur.BlockHash.Hash[:]) 304 } 305 cur.Childs = nil 306 } 307 308 func (ch *Chain) DeleteBranch(cur *BlockTreeNode, deleteCallback func(*btc.Uint256)) { 309 // first disconnect it from the Parent 310 ch.Blocks.BlockInvalid(cur.BlockHash.Hash[:]) 311 ch.BlockIndexAccess.Lock() 312 delete(ch.BlockIndex, cur.BlockHash.BIdx()) 313 cur.Parent.delChild(cur) 314 cur.delAllChildren(ch, deleteCallback) 315 ch.BlockIndexAccess.Unlock() 316 } 317 318 func (n *BlockTreeNode) addChild(c *BlockTreeNode) { 319 n.Childs = append(n.Childs, c) 320 } 321 322 func (n *BlockTreeNode) delChild(c *BlockTreeNode) { 323 newChds := make([]*BlockTreeNode, len(n.Childs)-1) 324 xxx := 0 325 for i := range n.Childs { 326 if n.Childs[i] != c { 327 newChds[xxx] = n.Childs[i] 328 xxx++ 329 } 330 } 331 if xxx != len(n.Childs)-1 { 332 panic("Child not found") 333 } 334 n.Childs = newChds 335 }