// This isusually the most time consuming process when applying a new block func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) { sumblockin := btc.GetBlockReward(changes.Height) var txoutsum, txinsum, sumblockout uint64 if changes.Height+ch.Unspent.UnwindBufLen >= changes.LastKnownHeight { changes.UndoData = make(map[[32]byte]*QdbRec) } blUnsp := make(map[[32]byte][]*btc.TxOut, 4*len(bl.Txs)) var wg sync.WaitGroup var ver_err_cnt uint32 for i := range bl.Txs { txoutsum, txinsum = 0, 0 bl.SigopsCost += uint32(btc.WITNESS_SCALE_FACTOR * bl.Txs[i].GetLegacySigOpCount()) // Check each tx for a valid input, except from the first one if i > 0 { tx_trusted := bl.Trusted if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) { tx_trusted = true } for j := 0; j < len(bl.Txs[i].TxIn); j++ { inp := &bl.Txs[i].TxIn[j].Input spendrec, waspent := changes.DeledTxs[inp.Hash] if waspent && spendrec[inp.Vout] { println("txin", inp.String(), "already spent in this block") e = errors.New("Input spent more then once in same block") return } tout := ch.PickUnspent(inp) if tout == nil { t, ok := blUnsp[inp.Hash] if !ok { e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String()) return } if inp.Vout >= uint32(len(t)) { println("Vout too big", len(t), inp.String()) e = errors.New("Vout too big") return } if t[inp.Vout] == nil { println("Vout already spent", inp.String()) e = errors.New("Vout already spent") return } if t[inp.Vout].WasCoinbase { e = errors.New("Cannot spend block's own coinbase in TxID: " + btc.NewUint256(inp.Hash[:]).String()) return } tout = t[inp.Vout] t[inp.Vout] = nil // and now mark it as spent: } else { if tout.WasCoinbase && changes.Height-tout.BlockHeight < COINBASE_MATURITY { e = errors.New("Trying to spend prematured coinbase: " + btc.NewUint256(inp.Hash[:]).String()) return } // it is confirmed already so delete it later if !waspent { spendrec = make([]bool, tout.VoutCount) changes.DeledTxs[inp.Hash] = spendrec } spendrec[inp.Vout] = true if changes.UndoData != nil { var urec *QdbRec urec = changes.UndoData[inp.Hash] if urec == nil { urec = new(QdbRec) urec.TxID = inp.Hash urec.Coinbase = tout.WasCoinbase urec.InBlock = tout.BlockHeight urec.Outs = make([]*QdbTxOut, tout.VoutCount) changes.UndoData[inp.Hash] = urec } tmp := new(QdbTxOut) tmp.Value = tout.Value tmp.PKScr = make([]byte, len(tout.Pk_script)) copy(tmp.PKScr, tout.Pk_script) urec.Outs[inp.Vout] = tmp } } if !tx_trusted { // run VerifyTxScript() in a parallel task wg.Add(1) go func(prv []byte, amount uint64, i int, tx *btc.Tx) { if !script.VerifyTxScript(prv, amount, i, tx, bl.VerifyFlags) { atomic.AddUint32(&ver_err_cnt, 1) } wg.Done() }(tout.Pk_script, tout.Value, j, bl.Txs[i]) } if btc.IsP2SH(tout.Pk_script) { bl.SigopsCost += uint32(btc.WITNESS_SCALE_FACTOR * btc.GetP2SHSigOpCount(bl.Txs[i].TxIn[j].ScriptSig)) } bl.SigopsCost += uint32(bl.Txs[i].CountWitnessSigOps(j, tout.Pk_script)) txinsum += tout.Value } if !tx_trusted { wg.Wait() if ver_err_cnt > 0 { println("VerifyScript failed", ver_err_cnt, "time(s)") return errors.New(fmt.Sprint("VerifyScripts failed ", ver_err_cnt, "time(s)")) } } } else { // For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes // (Previously we made sure in CheckBlock() that this was a coinbase type tx) if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 { return errors.New(fmt.Sprint("Coinbase script has a wrong length ", len(bl.Txs[0].TxIn[0].ScriptSig))) } } sumblockin += txinsum for j := range bl.Txs[i].TxOut { txoutsum += bl.Txs[i].TxOut[j].Value } sumblockout += txoutsum if e != nil { return // If any input fails, do not continue } if i > 0 { bl.Txs[i].Fee = txinsum - txoutsum if txoutsum > txinsum { return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s", float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String())) } } // Add each tx outs from the currently executed TX to the temporary pool outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut)) copy(outs, bl.Txs[i].TxOut) blUnsp[bl.Txs[i].Hash.Hash] = outs } if sumblockin < sumblockout { return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin)) } if bl.SigopsCost > btc.MAX_BLOCK_SIGOPS_COST { return errors.New("commitTxs(): too many sigops - RPC_Result:bad-blk-sigops") } var rec *QdbRec for k, v := range blUnsp { for i := range v { if v[i] != nil { if rec == nil { rec = new(QdbRec) rec.TxID = k rec.Coinbase = v[i].WasCoinbase rec.InBlock = changes.Height rec.Outs = make([]*QdbTxOut, len(v)) } rec.Outs[i] = &QdbTxOut{Value: v[i].Value, PKScr: v[i].Pk_script} } } if rec != nil { changes.AddList = append(changes.AddList, rec) rec = nil } } return nil }