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;
        }
示例#5
0
 /// <summary>
 /// Initialize an empty instance of CTxIn class
 /// </summary>
 public CTxIn()
 {
     prevout = new COutPoint();
     scriptSig = new CScript();
 }
示例#6
0
 /// <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;
 }