/// <summary> /// Returns the block referenced by the given index. /// If the block is not in memory, reads the block file. /// </summary> public Block GetBlock(int blockIndex) { if (!_blocks.TryGetValue(blockIndex, out var block)) { int fileIndex = blockIndex / BlockFile.BlocksPerFile; if (!_blockFileByIndex.TryGetValue(fileIndex, out var blockFile)) { blockFile = BlockFile.Read(blockIndex); if (blockFile == null) { return(null); } _blockFileByIndex.Add(fileIndex, blockFile); } for (int i = 0, blkIdx = blockFile.StartIndex, c = blockFile.Blocks.Count; i < c; ++i, ++blkIdx) { var blk = blockFile.Blocks[i]; if (!_blocks.ContainsKey(blkIdx)) { _blocks.Add(blkIdx, blk); } if (blk.Index == blockIndex) { block = blk; } } } return(block); }
/// <summary> /// Writes the given file to disk. /// </summary> public static void Write(BlockFile file) { int fileIndex = file.StartIndex / BlocksPerFile; var fileName = string.Format(FileNameFormat, fileIndex); var filePath = Config.GetBlockFilePath(fileName); Write(filePath, file); }
/// <summary> /// Saves index and current blocks to file. /// </summary> /// <returns>True if succeeds.</returns> public bool Save() { DbFile.Write(Config.GetBlockIndexFilePath(), _indexFile); foreach (var p in _blockFileByIndex) { var blockFile = p.Value; if (blockFile.IsDirty()) { BlockFile.Write(blockFile); blockFile.MarkBlocksSaved(); } } return(true); }
/// <summary> /// Replaces current blocks in the chain with received blocks. /// This method may add new blocks when needed. /// </summary> /// <param name="receivedBlocks">The list containing the new blocks.</param> /// <param name="receivedStartIndex">Index in the given list to get new blocks.</param> /// <param name="removedBlocks">Call-site-provided container for old blocks that are removed from the chain.</param> public void ReplaceBlocks(IList <Block> receivedBlocks, int receivedStartIndex, List <Block> removedBlocks) { for (int i = receivedStartIndex, c = receivedBlocks.Count; i < c; ++i) { Block receivedBlock = receivedBlocks[i]; int blockIndex = receivedBlock.Index; if (blockIndex < _blockIndices.Count) { Block localBlock = GetBlock(blockIndex); removedBlocks.Add(localBlock); } int fileIndex = blockIndex / BlockFile.BlocksPerFile; if (_blockFileByIndex.ContainsKey(fileIndex)) { var blockFile = _blockFileByIndex[fileIndex]; if (receivedBlock.Index > blockFile.EndIndex) { blockFile.AddBlock(receivedBlock); } else { blockFile.ReplaceBlock(receivedBlock); } } else { var blockFile = BlockFile.Read(blockIndex); if (blockFile != null) { if (receivedBlock.Index > blockFile.EndIndex) { blockFile.AddBlock(receivedBlock); } else { blockFile.ReplaceBlock(receivedBlock); } } else { int startIndexInFile = fileIndex * BlockFile.BlocksPerFile; if (blockIndex != startIndexInFile) { throw new System.InvalidOperationException("Attempting to add a block in the middle of a non-existing block file."); } blockFile = new BlockFile(receivedBlock); } _blockFileByIndex.Add(fileIndex, blockFile); } if (blockIndex < _blockIndices.Count) { _blockIndices[blockIndex].Hash = receivedBlock.Hash; } else { _blockIndices.Add(new BlockIndex { Index = blockIndex, Hash = receivedBlock.Hash }); } _blocks[blockIndex] = receivedBlock; } Save(); }
/// <summary> /// Adds a new block to the chain. /// </summary> /// <param name="block">The block to add.</param> /// <param name="needSave">If true, the index file and block file will be saved immediately.</param> /// <returns>Index to the new block.</returns> /// <remarks> /// In scenarios when there are multiple calls to this method, /// passing false to <paramref name="needSave"/> and then calling /// <see cref="Save"/> in the end would be a more efficient approach. /// </remarks> public BlockIndex AddBlock(Block block, bool needSave = true) { if (_blockIndices.Count > 0 && block.Index != LatestBlock.Index + 1) { throw new System.ArgumentException("Given block is not right after latest block."); } int fileIndex = block.Index / BlockFile.BlocksPerFile; if (_blockFileByIndex.ContainsKey(fileIndex)) { var blockFile = _blockFileByIndex[fileIndex]; blockFile.AddBlock(block); if (needSave) { BlockFile.Write(blockFile); } } else { var blockFile = BlockFile.Read(block.Index); if (blockFile != null) { blockFile.AddBlock(block); } else { blockFile = new BlockFile(block); } _blockFileByIndex.Add(fileIndex, blockFile); if (needSave) { BlockFile.Write(blockFile); } } if (needSave) { block.IsSaved = true; } var blockIndex = new BlockIndex { Index = block.Index, Hash = block.Hash, }; _blockIndices.Add(blockIndex); _blocks.Add(block.Index, block); if (needSave) { DbFile.Write(Config.GetBlockIndexFilePath(), _indexFile); } return(blockIndex); }