github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/chain/chain_accept.go (about) 1 package chain 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "sync/atomic" 8 9 "github.com/piotrnar/gocoin/lib/btc" 10 "github.com/piotrnar/gocoin/lib/script" 11 "github.com/piotrnar/gocoin/lib/utxo" 12 ) 13 14 // TrustedTxChecker is meant to speed up verifying transactions that had 15 // been verified already by the client while being taken to its memory pool 16 var TrustedTxChecker func(*btc.Tx) bool 17 18 func (ch *Chain) ProcessBlockTransactions(bl *btc.Block, height, lknown uint32) (changes *utxo.BlockChanges, sigopscost uint32, e error) { 19 changes = new(utxo.BlockChanges) 20 changes.Height = height 21 changes.LastKnownHeight = lknown 22 changes.DeledTxs = make(map[[32]byte][]bool, bl.TotalInputs) 23 sigopscost, e = ch.commitTxs(bl, changes) 24 return 25 } 26 27 // AcceptBlock either appends a new block at the end of the existing chain 28 // in which case it also applies all the transactions to the unspent database. 29 // If the block does is not the heighest, it is added to the chain, but maked 30 // as an orphan - its transaction will be verified only if the chain would swap 31 // to its branch later on. 32 func (ch *Chain) AcceptBlock(bl *btc.Block) (e error) { 33 ch.BlockIndexAccess.Lock() 34 cur := ch.AcceptHeader(bl) 35 ch.BlockIndexAccess.Unlock() 36 return ch.CommitBlock(bl, cur) 37 } 38 39 // Make sure to call this function with ch.BlockIndexAccess locked 40 func (ch *Chain) AcceptHeader(bl *btc.Block) (cur *BlockTreeNode) { 41 prevblk, ok := ch.BlockIndex[btc.NewUint256(bl.ParentHash()).BIdx()] 42 if !ok { 43 panic("This should not happen") 44 } 45 46 // create new BlockTreeNode 47 cur = new(BlockTreeNode) 48 cur.BlockHash = bl.Hash 49 cur.Parent = prevblk 50 cur.Height = prevblk.Height + 1 51 copy(cur.BlockHeader[:], bl.Raw[:80]) 52 53 // Add this block to the block index 54 prevblk.addChild(cur) 55 ch.BlockIndex[cur.BlockHash.BIdx()] = cur 56 57 return 58 } 59 60 func (ch *Chain) CommitBlock(bl *btc.Block, cur *BlockTreeNode) (e error) { 61 cur.BlockSize = uint32(len(bl.Raw)) 62 cur.TxCount = uint32(bl.TxCount) 63 if ch.LastBlock() == cur.Parent { 64 // The head of out chain - apply the transactions 65 var changes *utxo.BlockChanges 66 var sigopscost uint32 67 changes, sigopscost, e = ch.ProcessBlockTransactions(bl, cur.Height, bl.LastKnownHeight) 68 if e != nil { 69 // ProcessBlockTransactions failed, so trash the block. 70 //println("ProcessBlockTransactionsA", cur.BlockHash.String(), cur.Height, e.Error()) 71 ch.BlockIndexAccess.Lock() 72 cur.Parent.delChild(cur) 73 delete(ch.BlockIndex, cur.BlockHash.BIdx()) 74 ch.BlockIndexAccess.Unlock() 75 } else { 76 cur.SigopsCost = sigopscost 77 // ProcessBlockTransactions succeeded, so save the block as "trusted". 78 bl.Trusted.Set() 79 ch.Blocks.BlockAdd(cur.Height, bl) 80 // Apply the block's trabnsactions to the unspent database: 81 ch.Unspent.CommitBlockTxs(changes, bl.Hash.Hash[:]) 82 ch.SetLast(cur) // Advance the head 83 if ch.CB.BlockMinedCB != nil { 84 ch.CB.BlockMinedCB(bl) 85 } 86 } 87 } else { 88 // The block's parent is not the current head of the chain... 89 90 // Save the block, though do not mark it as "trusted" just yet 91 ch.Blocks.BlockAdd(cur.Height, bl) 92 93 // If it has more POW than the current head, move the head to it 94 if cur.MorePOW(ch.LastBlock()) { 95 ch.MoveToBlock(cur) 96 if ch.LastBlock() != cur { 97 e = errors.New("CommitBlock: MoveToBlock failed") 98 } 99 } else { 100 println("Orphaned block", bl.Hash.String(), cur.Height) 101 } 102 } 103 104 return 105 } 106 107 // commitTxs is ususually the most time consuming process when applying a new block. 108 func (ch *Chain) commitTxs(bl *btc.Block, changes *utxo.BlockChanges) (sigopscost uint32, e error) { 109 sumblockin := btc.GetBlockReward(changes.Height) 110 var txoutsum, txinsum, sumblockout uint64 111 112 if changes.Height+ch.Unspent.UnwindBufLen >= changes.LastKnownHeight { 113 changes.UndoData = make(map[[32]byte]*utxo.UtxoRec) 114 } 115 116 blUnsp := make(map[[32]byte][]*btc.TxOut, len(bl.Txs)) 117 118 var wg sync.WaitGroup 119 var ver_err_cnt uint32 120 121 for i, tx := range bl.Txs { 122 txoutsum, txinsum = 0, 0 123 124 sigopscost += uint32(btc.WITNESS_SCALE_FACTOR * bl.Txs[i].GetLegacySigOpCount()) 125 126 // Check each tx for a valid input, except from the first one 127 if i > 0 { 128 129 tx_trusted := bl.Trusted.Get() 130 if !tx_trusted { 131 if TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i]) { 132 tx_trusted = true 133 } else { 134 tx.Spent_outputs = make([]*btc.TxOut, len(bl.Txs[i].TxIn)) 135 } 136 } 137 138 // first collect all the inputs, their amounts and spend scripts 139 for j := 0; j < len(bl.Txs[i].TxIn); j++ { 140 inp := &bl.Txs[i].TxIn[j].Input 141 spent_map, was_spent := changes.DeledTxs[inp.Hash] 142 if was_spent { 143 if int(inp.Vout) >= len(spent_map) { 144 println("txin", inp.String(), "did not have vout", inp.Vout) 145 e = errors.New("tx VOut too big") 146 return 147 } 148 149 if spent_map[inp.Vout] { 150 println("txin", inp.String(), "already spent in this block") 151 e = errors.New("double spend inside the block") 152 return 153 } 154 } 155 tout := ch.Unspent.UnspentGet(inp) 156 if tout == nil { 157 t, ok := blUnsp[inp.Hash] 158 if !ok { 159 e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String()) 160 return 161 } 162 163 if inp.Vout >= uint32(len(t)) { 164 println("Vout too big", len(t), inp.String()) 165 e = errors.New("vout too big") 166 return 167 } 168 169 if t[inp.Vout] == nil { 170 println("Vout already spent", inp.String()) 171 e = errors.New("vout already spent") 172 return 173 } 174 175 if t[inp.Vout].WasCoinbase { 176 e = errors.New("Cannot spend block's own coinbase in TxID: " + btc.NewUint256(inp.Hash[:]).String()) 177 return 178 } 179 180 tout = t[inp.Vout] 181 t[inp.Vout] = nil // and now mark it as spent: 182 } else { 183 if tout.WasCoinbase && changes.Height-tout.BlockHeight < COINBASE_MATURITY { 184 e = errors.New("Trying to spend prematured coinbase: " + btc.NewUint256(inp.Hash[:]).String()) 185 return 186 } 187 // it is confirmed already so delete it later 188 if !was_spent { 189 spent_map = make([]bool, tout.VoutCount) 190 changes.DeledTxs[inp.Hash] = spent_map 191 } 192 spent_map[inp.Vout] = true 193 194 if changes.UndoData != nil { 195 var urec *utxo.UtxoRec 196 urec = changes.UndoData[inp.Hash] 197 if urec == nil { 198 urec = new(utxo.UtxoRec) 199 urec.TxID = inp.Hash 200 urec.Coinbase = tout.WasCoinbase 201 urec.InBlock = tout.BlockHeight 202 urec.Outs = make([]*utxo.UtxoTxOut, tout.VoutCount) 203 changes.UndoData[inp.Hash] = urec 204 } 205 tmp := new(utxo.UtxoTxOut) 206 tmp.Value = tout.Value 207 tmp.PKScr = make([]byte, len(tout.Pk_script)) 208 copy(tmp.PKScr, tout.Pk_script) 209 urec.Outs[inp.Vout] = tmp 210 } 211 } 212 213 if !tx_trusted { 214 tx.Spent_outputs[j] = tout 215 } 216 217 if (bl.VerifyFlags & script.VER_P2SH) != 0 { 218 if btc.IsP2SH(tout.Pk_script) { 219 sigopscost += uint32(btc.WITNESS_SCALE_FACTOR * btc.GetP2SHSigOpCount(bl.Txs[i].TxIn[j].ScriptSig)) 220 } 221 } 222 223 if (bl.VerifyFlags & script.VER_WITNESS) != 0 { 224 sigopscost += uint32(bl.Txs[i].CountWitnessSigOps(j, tout.Pk_script)) 225 } 226 227 txinsum += tout.Value 228 } 229 230 // second, verify the scrips: 231 if !tx_trusted { // run VerifyTxScript() in a parallel task 232 for j := 0; j < len(bl.Txs[i].TxIn); j++ { 233 wg.Add(1) 234 go func(i int, tx *btc.Tx) { 235 if !script.VerifyTxScript(tx.Spent_outputs[i].Pk_script, &script.SigChecker{Amount: tx.Spent_outputs[i].Value, Idx: i, Tx: tx}, bl.VerifyFlags) { 236 atomic.AddUint32(&ver_err_cnt, 1) 237 } 238 wg.Done() 239 }(j, bl.Txs[i]) 240 } 241 } 242 243 } else { 244 // For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes 245 // (Previously we made sure in CheckBlock() that this was a coinbase type tx) 246 if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 { 247 e = errors.New(fmt.Sprint("Coinbase script has a wrong length ", len(bl.Txs[0].TxIn[0].ScriptSig))) 248 return 249 } 250 } 251 252 if e != nil { // this should not happen as every error has a return 253 return // If any input fails, do not continue 254 } 255 256 sumblockin += txinsum 257 258 for j := range bl.Txs[i].TxOut { 259 txoutsum += bl.Txs[i].TxOut[j].Value 260 } 261 sumblockout += txoutsum 262 263 if i > 0 { 264 bl.Txs[i].Fee = txinsum - txoutsum 265 if txoutsum > txinsum { 266 e = fmt.Errorf("more spent (%.8f) than at the input (%.8f) in TX %s", 267 float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String()) 268 return 269 } 270 } 271 272 // Add each tx outs from the currently executed TX to the temporary pool 273 outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut)) 274 copy(outs, bl.Txs[i].TxOut) 275 blUnsp[bl.Txs[i].Hash.Hash] = outs 276 } 277 278 if !bl.Trusted.Get() { 279 wg.Wait() 280 if ver_err_cnt > 0 { 281 println("VerifyScript failed", ver_err_cnt, "time (s)") 282 e = errors.New(fmt.Sprint("VerifyScripts failed ", ver_err_cnt, "time (s)")) 283 return 284 } 285 } 286 287 if sumblockin < sumblockout { 288 e = fmt.Errorf("out:%d > in:%d", sumblockout, sumblockin) 289 return 290 } 291 292 if sigopscost > btc.MAX_BLOCK_SIGOPS_COST { 293 e = errors.New("commitTxs(): too many sigops - RPC_Result:bad-blk-sigops") 294 return 295 } 296 297 var rec *utxo.UtxoRec 298 changes.AddList = make([]*utxo.UtxoRec, 0, len(blUnsp)) 299 for k, v := range blUnsp { 300 for i := range v { 301 if v[i] != nil { 302 if rec == nil { 303 rec = new(utxo.UtxoRec) 304 rec.TxID = k 305 rec.Coinbase = v[i].WasCoinbase 306 rec.InBlock = changes.Height 307 rec.Outs = make([]*utxo.UtxoTxOut, len(v)) 308 } 309 rec.Outs[i] = &utxo.UtxoTxOut{Value: v[i].Value, PKScr: v[i].Pk_script} 310 } 311 } 312 if rec != nil { 313 changes.AddList = append(changes.AddList, rec) 314 rec = nil 315 } 316 } 317 318 return 319 } 320 321 // CheckTransactions checks transactions for consistency and finality. 322 // Return nil if OK, otherwise a descripive error. 323 func CheckTransactions(txs []*btc.Tx, height, btime uint32) (res error) { 324 var wg sync.WaitGroup 325 326 res_chan := make(chan error, 1) 327 328 for i := 0; len(res_chan) == 0 && i < len(txs); i++ { 329 wg.Add(1) 330 331 go func(tx *btc.Tx) { 332 defer wg.Done() // call wg.Done() before returning from this goroutine 333 334 if len(res_chan) > 0 { 335 return // abort checking if a parallel error has already been reported 336 } 337 338 er := tx.CheckTransaction() 339 340 if len(res_chan) > 0 { 341 return // abort checking if a parallel error has already been reported 342 } 343 344 if er == nil && !tx.IsFinal(height, btime) { 345 er = errors.New("CheckTransactions() : not-final transaction - RPC_Result:bad-txns-nonfinal") 346 } 347 348 if er != nil { 349 select { // this is a non-blocking write to channel 350 case res_chan <- er: 351 default: 352 } 353 } 354 }(txs[i]) 355 } 356 357 wg.Wait() // wait for all the goroutines to complete 358 359 if len(res_chan) > 0 { 360 res = <-res_chan 361 } 362 363 return 364 }