private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck = false) { // Check it again in case a previous version let a bad block in, but skip BlockSig checking if (!block.CheckBlock(!fJustCheck, !fJustCheck, false)) { return false; // Invalid block found. } bool fScriptChecks = cursor.nHeight >= HashCheckpoints.TotalBlocksEstimate; var scriptFlags = scriptflag.SCRIPT_VERIFY_NOCACHE | scriptflag.SCRIPT_VERIFY_P2SH; long nFees = 0; long nValueIn = 0; long nValueOut = 0; uint nSigOps = 0; var queuedMerkleNodes = new Dictionary<uint256, CMerkleNode>(); var queuedOutputs = new Dictionary<COutPoint, TxOutItem>(); for (var nTx = 0; nTx < block.vtx.Length; nTx++) { var tx = block.vtx[nTx]; var hashTx = tx.Hash; if (!queuedMerkleNodes.ContainsKey(hashTx)) { var nTxPos = cursor.nBlockPos + block.GetTxOffset(nTx); var mNode = new CMerkleNode(cursor.ItemID, nTxPos, tx); queuedMerkleNodes.Add(hashTx, mNode); } Dictionary<COutPoint, TxOutItem> txouts; if (GetOutputs(hashTx, out txouts)) { // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. return false; } nSigOps += tx.LegacySigOpCount; if (nSigOps > CBlock.nMaxSigOps) { return false; // too many sigops } var inputs = new Dictionary<COutPoint, TxOutItem>(); if (tx.IsCoinBase) { nValueOut += tx.nValueOut; } else { bool Invalid; if (!FetchInputs(ref tx, ref queuedOutputs, ref inputs, true, out Invalid)) { return false; // Unable to fetch some inputs. } // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(ref inputs); if (nSigOps > CBlock.nMaxSigOps) { return false; // too many sigops } long nTxValueIn = tx.GetValueIn(ref inputs); long nTxValueOut = tx.nValueOut; nValueIn += nTxValueIn; nValueOut += nTxValueOut; if (!tx.IsCoinStake) { nFees += nTxValueIn - nTxValueOut; } if (!ConnectInputs(ref tx, ref inputs, ref queuedOutputs, ref cursor, true, fScriptChecks, scriptFlags)) { return false; } } for (var i = 0u; i < tx.vout.Length; i++) { var outKey = new COutPoint(hashTx, i); var outData = new TxOutItem() { nMerkleNodeID = -1, nValue = tx.vout[i].nValue, scriptPubKey = tx.vout[i].scriptPubKey, IsSpent = false, nOut = i }; queuedOutputs.Add(outKey, outData); } } if (!block.IsProofOfStake) { long nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees); // Check coinbase reward if (block.vtx[0].nValueOut > nBlockReward) { return false; // coinbase reward exceeded } } cursor.nMint = nValueOut - nValueIn + nFees; cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + nValueOut - nValueIn; if (!UpdateDBCursor(ref cursor)) { return false; // Unable to commit changes } if (fJustCheck) { return true; } // Flush merkle nodes. var savedMerkleNodes = new Dictionary<uint256, CMerkleNode>(); foreach (var merklePair in queuedMerkleNodes) { var merkleNode = merklePair.Value; if (!SaveMerkleNode(ref merkleNode)) { // Unable to save merkle tree cursor. return false; } savedMerkleNodes.Add(merklePair.Key, merkleNode); } // Write queued transaction changes var newOutpointItems = new List<TxOutItem>(); var updatedOutpointItems = new List<TxOutItem>(); foreach (var outPair in queuedOutputs) { var outItem = outPair.Value; if (outItem.nMerkleNodeID == -1) { // This outpoint doesn't exist yet, adding to insert list. outItem.nMerkleNodeID = savedMerkleNodes[outPair.Key.hash].nMerkleNodeID; newOutpointItems.Add(outItem); } else { // This outpount already exists, adding to update list. updatedOutpointItems.Add(outItem); } } if (updatedOutpointItems.Count != 0 && !UpdateOutpoints(ref updatedOutpointItems)) { return false; // Unable to update outpoints } if (newOutpointItems.Count != 0 && !InsertOutpoints(ref newOutpointItems)) { return false; // Unable to insert outpoints } return true; }
public bool GetTxOutCursor(COutPoint outpoint, out TxOutItem txOutCursor) { var queryResults = dbConn.Query<TxOutItem>("select o.* from [Outputs] o left join [MerkleNodes] m on (m.nMerkleNodeID = o.nMerkleNodeID) where m.[TransactionHash] = ?", (byte[])outpoint.hash); if (queryResults.Count == 1) { txOutCursor = queryResults[0]; return true; } // Tx not found txOutCursor = null; return false; }
public bool GetOutputs(uint256 transactionHash, out Dictionary<COutPoint, TxOutItem> txouts, bool fUnspentOnly=true) { txouts = null; var queryParams = new object[] { (byte[])transactionHash, fUnspentOnly ? OutputFlags.AVAILABLE : (OutputFlags.AVAILABLE | OutputFlags.SPENT) }; var queryResult = dbConn.Query<TxOutItem>("select o.* from [Outputs] o left join [MerkleNodes] m on m.[nMerkleNodeID] = o.[nMerkleNodeID] where m.[TransactionHash] = ? and outputFlags = ?", queryParams); if (queryResult.Count != 0) { txouts = new Dictionary<COutPoint, TxOutItem>(); foreach (var o in queryResult) { var outpointKey = new COutPoint(transactionHash, o.nOut); var outpointData = o; txouts.Add(outpointKey, outpointData); } // There are some unspent inputs. return true; } // This transaction has been spent completely. return false; }
public bool FetchInputs(ref CTransaction tx, ref Dictionary<COutPoint, TxOutItem> queued, ref Dictionary<COutPoint, TxOutItem> inputs, bool IsBlock, out bool Invalid) { Invalid = false; if (tx.IsCoinBase) { // Coinbase transactions have no inputs to fetch. return true; } StringBuilder queryBuilder = new StringBuilder(); queryBuilder.Append("select o.*, m.[TransactionHash] from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where "); for (var i = 0; i < tx.vin.Length; i++) { queryBuilder.AppendFormat(" {0} (m.[TransactionHash] = x'{1}' and o.[OutputNumber] = x'{2}')", (i > 0 ? "or" : string.Empty), Interop.ToHex(tx.vin[i].prevout.hash), Interop.ToHex(VarInt.EncodeVarInt(tx.vin[i].prevout.n) )); } var queryResults = dbConn.Query<InputsJoin>(queryBuilder.ToString()); foreach (var item in queryResults) { if (item.IsSpent) { return false; // Already spent } var inputsKey = new COutPoint(item.TransactionHash, item.nOut); // Add output data to dictionary inputs.Add(inputsKey, item.getTxOutItem()); } if (queryResults.Count < tx.vin.Length) { if (IsBlock) { // It seems that some transactions are being spent in the same block. foreach (var txin in tx.vin) { var outPoint = txin.prevout; if (inputs.ContainsKey(outPoint)) { continue; // We have already seen this input. } if (!queued.ContainsKey(outPoint)) { return false; // No such transaction } // Add output data to dictionary inputs.Add(outPoint, queued[outPoint]); // Mark output as spent // queued[outPoint].IsSpent = true; } } else { // Unconfirmed transaction foreach (var txin in tx.vin) { var outPoint = txin.prevout; CTransaction txPrev; if (!mapUnconfirmedTx.TryGetValue(outPoint.hash, out txPrev)) { return false; // No such transaction } if (outPoint.n > txPrev.vout.Length) { Invalid = true; return false; // nOut is out of range } // TODO: return inputs from map throw new NotImplementedException(); } return false; } } return true; }
/// <summary> /// Initialize an empty instance of CTxIn class /// </summary> public CTxIn() { prevout = new COutPoint(); scriptSig = new CScript(); }
/// <summary> /// Initialize new CTxIn instance as copy of another one. /// </summary> /// <param name="i">CTxIn instance.</param> public CTxIn(CTxIn i) { prevout = new COutPoint(i.prevout); scriptSig = i.scriptSig; nSequence = i.nSequence; }