public void AddBlockContent(BlockMessage block)
        {
            transactionalResource.Enlist();

            byte[] blockHash = CryptoUtils.DoubleSha256(BitcoinStreamWriter.GetBytes(block.BlockHeader.Write));

            StoredBlock storedBlock = storage.FindBlockByHash(blockHash);
            if (storedBlock == null || storedBlock.HasContent)
            {
                return;
            }

            if (!BlockContentValidator.IsMerkleTreeValid(block))
            {
                // If merkle tree is invalid than we cannot rely on its header hash to mark block in storage as invalid.
                throw new BitcoinProtocolViolationException($"Merkle tree did not pass validation (block: {BitConverter.ToString(blockHash)}).");
            }

            // since block is already stored in storage we assume that its header is valid

            if (!BlockContentValidator.IsValid(storedBlock, block))
            {
                //todo: mark chain as broken
                throw new BitcoinProtocolViolationException($"Block content was invalid (block: {BitConverter.ToString(blockHash)}).");
            }

            StoredBlock updatedBlock = storage.AddBlockContent(storedBlock.Hash, BitcoinStreamWriter.GetBytes(block.Write));
            //todo: test state updates
            currentState = currentState.Update(storedBlock, updatedBlock);
        }
        private StoredBlock SetIsInBestHeaderChain(StoredBlock block, bool val)
        {
            StoredBlockBuilder builder = new StoredBlockBuilder(block);
            builder.IsInBestHeaderChain = val;
            StoredBlock newBlock = builder.Build();

            currentState = currentState.Update(block, newBlock);

            return newBlock;
        }
        private void UpdateBestHeadersChain(StoredBlock block)
        {
            if (!IsBetterHeaderThan(block, currentState.BestHeader))
            {
                return;
            }

            StoredBlock newHead = block;
            StoredBlock oldHead = currentState.BestHeader;

            //todo: this code should be wrapped in transaction, init should be called on failure
            while (!newHead.Hash.SequenceEqual(oldHead.Hash))
            {
                bool markNewHead = newHead.Height >= oldHead.Height;
                bool markOldHead = oldHead.Height >= newHead.Height;

                if (markNewHead)
                {
                    newHead = SetIsInBestHeaderChain(newHead, true);
                    storage.UpdateBlock(newHead);
                    newHead = storage.FindBlockByHash(newHead.Header.PrevBlock);
                }

                if (markOldHead)
                {
                    oldHead = SetIsInBestHeaderChain(oldHead, false);
                    storage.UpdateBlock(oldHead);
                    oldHead = storage.FindBlockByHash(oldHead.Header.PrevBlock);
                }
            }

            currentState = currentState.SetBestHeader(block);
        }
 private void OnRollback()
 {
     lock (stateLock)
     {
         currentState = commitedState;
     }
 }
 private void OnCommit()
 {
     lock (stateLock)
     {
         commitedState = currentState;
     }
 }
        public void Init()
        {
            transactionalResource.Enlist();

            StoredBlock genesisBlock = storage.FindBlockByHeight(0);
            if (genesisBlock == null)
            {
                AddGenesisBlock();
            }
            else if (!GenesisBlock.Hash.SequenceEqual(genesisBlock.Hash))
            {
                //todo: also check for situation when there is no genisis block in storage, but storage is not empty
                throw new InvalidOperationException("The genesis block in storage has wrong hash.");
            }
            var bestHeader = storage.FindFirst(BlockSelector.LastBestHeader);
            var bestChain = storage.FindFirst(BlockSelector.LastChainBlock);
            currentState = new BlockchainState(bestHeader, bestChain);
        }
        public bool Include(byte[] hash)
        {
            transactionalResource.Enlist();

            StoredBlock block = storage.FindBlockByHash(hash);
            if (block == null || !block.HasContent)
            {
                return false;
            }
            if (block.IsInBestBlockChain)
            {
                // todo: what to return in this scenario
                return true;
            }
            if (block.Height != currentState.BestChain.Height + 1 || !currentState.BestChain.Hash.SequenceEqual(block.Header.PrevBlock))
            {
                return false;
            }

            byte[] content = storage.GetBlockContent(block.Hash);

            BlockMessage blockMessage = BitcoinStreamReader.FromBytes(content, BlockMessage.Read);

            UnspentOutputsUpdate unspentOutputsUpdate;
            try
            {
                unspentOutputsUpdate = PrepareUnspentOutputsUpdate(block, blockMessage);
            }
            catch (BitcoinProtocolViolationException ex)
            {
                logger.Warn(ex, "The block with height {0} was not included to the blockchain because of validation errors.", block.Height);
                //todo: mark block as invalid and update best headers chain
                return false;
            }

            unspentOutputsUpdate.Persist();

            block = SetIsInBestBlockChain(block, true);
            storage.UpdateBlock(block);

            currentState = currentState.SetBestChain(block);

            return true;
        }
Пример #8
0
        private void CheckState()
        {
            BlockchainState newState = blockchain.CommitedState;

            bool stateChanged = false;
            lock (stateLock)
            {
                if (newState != state)
                {
                    state = newState;
                    stateChanged = true;
                }
            }

            if (stateChanged)
            {
                StateChanged?.Invoke();
            }
        }