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 ProcessBlock(ref CBlock block) { var blockHash = block.header.Hash; if (blockMap.ContainsKey(blockHash)) { // We already have this block. return false; } if (orphanMap.ContainsKey(blockHash)) { // We already have block in the list of orphans. return false; } // TODO: Limited duplicity on stake and reserialization of block signature if (!block.CheckBlock(true, true, true)) { // Preliminary checks failure. return false; } if (block.IsProofOfStake) { uint256 hashProofOfStake = 0, targetProofOfStake = 0; if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, out hashProofOfStake, out targetProofOfStake)) { return false; // do not error here as we expect this during initial block download } if (!mapProofOfStake.ContainsKey(blockHash)) { // add to mapProofOfStake mapProofOfStake.TryAdd(blockHash, hashProofOfStake); } } // TODO: difficulty verification // If don't already have its previous block, shunt it off to holding area until we get it if (!blockMap.ContainsKey(block.header.prevHash)) { if (block.IsProofOfStake) { var proof = block.ProofOfStake; // Limited duplicity on stake: prevents block flood attack // Duplicate stake allowed only when there is orphan child block if (mapStakeSeenOrphan.ContainsKey(proof) && !orphanMapByPrev.ContainsKey(blockHash)) { return false; // duplicate proof-of-stake } else { mapStakeSeenOrphan.TryAdd(proof, blockHash); } } orphanMap.TryAdd(blockHash, block); orphanMapByPrev.TryAdd(blockHash, block); return true; } // Store block to disk if (!AcceptBlock(ref block)) { // Accept failed return false; } if (orphanMapByPrev.Count > 0) { // Recursively process any orphan blocks that depended on this one var orphansQueue = new List<uint256>(); orphansQueue.Add(blockHash); for (int i = 0; i < orphansQueue.Count; i++) { var hashPrev = orphansQueue[i]; foreach (var pair in orphanMapByPrev) { var orphanBlock = pair.Value; if (orphanBlock.header.prevHash == blockHash) { if (AcceptBlock(ref orphanBlock)) { orphansQueue.Add(pair.Key); } CBlock dummy1; orphanMap.TryRemove(pair.Key, out dummy1); uint256 dummyHash; mapStakeSeenOrphan.TryRemove(orphanBlock.ProofOfStake, out dummyHash); } } CBlock dummy2; orphanMapByPrev.TryRemove(hashPrev, out dummy2); } } return true; }