private bool ConnectInputs(ref CTransaction tx, ref Dictionary<COutPoint, TxOutItem> inputs, ref Dictionary<COutPoint, TxOutItem> queued, ref CBlockStoreItem cursorBlock, bool fBlock, bool fScriptChecks, scriptflag scriptFlags) { // Take over previous transactions' spent items // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain if (!tx.IsCoinBase) { long nValueIn = 0; long nFees = 0; for (uint i = 0; i < tx.vin.Length; i++) { var prevout = tx.vin[i].prevout; Contract.Assert(inputs.ContainsKey(prevout)); var input = inputs[prevout]; CBlockStoreItem parentBlockCursor; if (input.nMerkleNodeID == -1) { // This input seems as is confirmed by the same block. if (!queued.ContainsKey(prevout)) { return false; // No such output has been queued by this block. } // TODO: Ensure that neither coinbase nor coinstake outputs are // available for spending in the generation block. } else { // This input has been confirmed by one of the earlier accepted blocks. var merkleItem = GetMerkleCursor(input, out parentBlockCursor); if (merkleItem == null) { return false; // Unable to find merkle node } // If prev is coinbase or coinstake, check that it's matured if (merkleItem.IsCoinBase || merkleItem.IsCoinStake) { if (cursorBlock.nHeight - parentBlockCursor.nHeight < NetInfo.nGeneratedMaturity) { return false; // tried to spend non-matured generation input. } } // check transaction timestamp if (merkleItem.nTime > tx.nTime) { return false; // transaction timestamp earlier than input transaction } } // Check for negative or overflow input values nValueIn += input.nValue; if (!CTransaction.MoneyRange(input.nValue) || !CTransaction.MoneyRange(nValueIn)) { return false; // txin values out of range } } // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. for (int i = 0; i < tx.vin.Length; i++) { var prevout = tx.vin[i].prevout; Contract.Assert(inputs.ContainsKey(prevout)); var input = inputs[prevout]; // Check for conflicts (double-spend) if (input.IsSpent) { return false; } // Skip ECDSA signature verification when connecting blocks (fBlock=true) // before the last blockchain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (fScriptChecks) { // Verify signature if (!ScriptCode.VerifyScript(tx.vin[i].scriptSig, input.scriptPubKey, tx, i, (int)scriptflag.SCRIPT_VERIFY_P2SH, 0)) { return false; // VerifyScript failed. } } // Mark outpoint as spent input.IsSpent = true; inputs[prevout] = input; // Write back if (fBlock) { if (input.nMerkleNodeID != -1) { // Input has been confirmed earlier. queued.Add(prevout, input); } else { // Input has been confirmed by current block. queued[prevout] = input; } } } if (tx.IsCoinStake) { if (HashCheckpoints.LastCheckpointTime < tx.nTime) { // Coin stake tx earns reward instead of paying fee long nCoinAge; if (!tx.GetCoinAge(ref inputs, out nCoinAge)) { return false; // unable to get coin age for coinstake } long nReward = tx.nValueOut - nValueIn; long nCalculatedReward = CBlock.GetProofOfStakeReward(nCoinAge, cursorBlock.nBits, tx.nTime) - tx.GetMinFee(1, false, CTransaction.MinFeeMode.GMF_BLOCK) + CTransaction.nCent; if (nReward > nCalculatedReward) { return false; // coinstake pays too much } } } else { if (nValueIn < tx.nValueOut) { return false; // value in < value out } // Tally transaction fees long nTxFee = nValueIn - tx.nValueOut; if (nTxFee < 0) { return false; // nTxFee < 0 } nFees += nTxFee; if (!CTransaction.MoneyRange(nFees)) { return false; // nFees out of range } } } return true; }