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; }
private void CheckState() { BlockchainState newState = blockchain.CommitedState; bool stateChanged = false; lock (stateLock) { if (newState != state) { state = newState; stateChanged = true; } } if (stateChanged) { StateChanged?.Invoke(); } }