示例#1
0
// 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
}