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;
        }