Exemplo n.º 1
0
 /// <summary>
 /// Processes any queued blocks which were set to be processed for a future time when added.
 /// </summary>
 public void ProcessQueuedBlocks()
 {
     // If we have queued items, we pop and process all queued items which are ready.
     while (QueuedBlocks.Count > 0 && QueuedBlocks.Peek().Header.Timestamp <= Configuration.CurrentTimestamp)
     {
         AddBlock(QueuedBlocks.Pop());
     }
 }
Exemplo n.º 2
0
        /// <summary>
        /// Adds a block to the chain, or queues it to be added later if the block's timestamp has not passed yet.
        /// </summary>
        /// <param name="block">The block to add or queue to be added to the chain.</param>
        /// <param name="newState">An optional parameter which implies the new state after executing the block.</param>
        /// <returns>Returns true if the block was processed immediately, otherwise returns false if it was queued to be added later or was seen as problematic and will not be added at all.</returns>
        public bool AddBlock(Block.Block block, State.State newState = null)
        {
            // If it isn't time for the block to be added yet, we instead add it to our queue.
            if (block.Header.Timestamp > Configuration.CurrentTimestamp)
            {
                QueuedBlocks.Push(block);
                return(false);
            }

            // Check if the block is being added to the head of the chain
            if (block.Header.PreviousHash.ValuesEqual(HeadBlockHash))
            {
                // Apply block
                if (newState != null)
                {
                    State = newState;
                }
                else
                {
                    try
                    {
                        State.ApplyBlock(block);
                    }
                    catch (Exception exception)
                    {
                        // Record the exception that occured for this block.
                        Configuration.DebugConfiguration.RecordException(exception, false);
                        return(false);
                    }
                }

                // Set block hash for block number
                SetBlockHashForBlockNumber(block.Header.BlockNumber, block.Header.GetHash());

                // Get block score so it is cached in the database
                GetScore(block);

                // Set the head to this block
                HeadBlockHash = block.Header.GetHash();

                // For every transaction, set the block number and index
                for (int i = 0; i < block.Transactions.Length; i++)
                {
                    SetTransactionPosition(block.Transactions[i].GetHash(), block.Header.BlockNumber, i);
                }
            }
            else if (ContainsBlock(block.Header.PreviousHash))
            {
                // If the previous hash is in the chain, but it's not the head, we process it to see if it will get a better score
                Data_Types.State.State state = GetPostBlockState(block.Header.PreviousHash);

                // Apply block
                try
                {
                    state.ApplyBlock(block);
                }
                catch (Exception exception)
                {
                    // Record the exception that occured for this block.
                    Configuration.DebugConfiguration.RecordException(exception, false);
                    return(false);
                }

                // Obtain our block score
                BigInteger newScore     = GetScore(block);
                BigInteger currentScore = GetScore(GetHeadBlock());

                // If our new score is better than our head score, we replace the head.
                if (newScore > currentScore)
                {
                    // Set the head as the new block head
                    HeadBlockHash = block.Header.GetHash();
                    State         = state;

                    // We find a common ancestor with our existing head while obtaining our new chain.
                    Block.Block currentBlock = block;
                    Dictionary <BigInteger, Block.Block> newChain = new Dictionary <BigInteger, Block.Block>();
                    while (currentBlock != null && currentBlock.Header.BlockNumber >= Configuration.GenesisBlock.Header.BlockNumber)
                    {
                        // Add our new block to the new chain
                        newChain[currentBlock.Header.BlockNumber] = currentBlock;

                        // Obtain the original at this block number
                        byte[] originalBlockHash = GetBlockHashFromBlockNumber(currentBlock.Header.BlockNumber);

                        // If this is a common ancestor or it doesn't exist in our database, we stop
                        if (originalBlockHash == null || originalBlockHash.ValuesEqual(currentBlock.Header.GetHash()))
                        {
                            break;
                        }

                        currentBlock = GetParentBlock(currentBlock);
                    }

                    // Current block by now is a common ancestor or it's null (presumably if we looked past genesis without finding a common ancestor, which shouldn't happen unless our two heads have differing genesis blocks).

                    // We loop from the common ancestor forward while we have old blocks to remove, and new blocks to add (replacing part of our chain).
                    for (BigInteger i = currentBlock.Header.BlockNumber; true; i++)
                    {
                        // Obtain our block hash for this index
                        byte[] originalBlockHash = GetBlockHashFromBlockNumber(i);

                        // If this old chain exists at this index.
                        bool originalChainExists = originalBlockHash != null;
                        bool newChainExists      = newChain.ContainsKey(i);
                        if (originalChainExists)
                        {
                            // Obtain our original block.
                            Block.Block originalBlock = GetBlock(originalBlockHash);

                            // Remove the block hash for this index.
                            RemoveBlockHashForBlockNumber(i);

                            // Remove all transaction index lookup items.
                            foreach (Transactions.Transaction transaction in originalBlock.Transactions)
                            {
                                RemoveTransactionPosition(transaction.GetHash());
                            }
                        }

                        // If our new chain exists at this index.
                        if (newChainExists)
                        {
                            // Obtain our new block.
                            Block.Block newBlock = newChain[i];

                            // Set our block hash for this index
                            SetBlockHashForBlockNumber(i, newBlock.Header.GetHash());

                            // Set all transaction indexes for each transaction
                            for (int transactionIndex = 0; transactionIndex < newBlock.Transactions.Length; transactionIndex++)
                            {
                                SetTransactionPosition(newBlock.Transactions[transactionIndex].GetHash(), newBlock.Header.BlockNumber, transactionIndex);
                            }
                        }

                        // If neither chain exists here, we have no work to be doing
                        if (!newChainExists && !originalChainExists)
                        {
                            break;
                        }
                    }
                }
            }
            else
            {
                // Block has no parent yet, we make sure we have an orphan list set up to queue this block for later.
                if (!OrphanBlocks.TryGetValue(block.Header.PreviousHash, out var orphanList))
                {
                    orphanList = new List <Block.Block>();
                    OrphanBlocks[block.Header.PreviousHash] = orphanList;
                }

                // Add this block to it.
                orphanList.Add(block);

                return(false);
            }

            // Add this block to the child lookup to allow us to look both directions on the chain.
            AddChild(block);

            // Set our block in our database.
            SetBlock(block);

            // Update our head hash since we finished applying the block
            Configuration.Database.Set(DB_HEAD_HASH, HeadBlockHash);

            // Check if there are any orphaned blocks waiting for this parent.
            byte[] blockHash = block.Header.GetHash();
            if (OrphanBlocks.TryGetValue(blockHash, out var orphanBlocks))
            {
                // Process all orphans now that their parent arrived.
                foreach (Block.Block orphanBlock in orphanBlocks)
                {
                    AddBlock(orphanBlock);
                }

                // Clear the list.
                OrphanBlocks.Remove(blockHash);
            }

            return(true);
        }