public static byte[] EncodeBlockTxNode(BlockTxNode blockTx) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodeBlockTxNode(writer, blockTx); return(stream.ToArray()); } }
public static void EncodeBlockTxNode(BinaryWriter writer, BlockTxNode blockTx) { writer.WriteInt32(blockTx.Index); writer.WriteInt32(blockTx.Depth); writer.WriteUInt256(blockTx.Hash); writer.WriteBool(blockTx.Pruned); if (!blockTx.Pruned) { writer.WriteBytes(blockTx.TxBytes.ToArray()); } }
// IBlockTxesStorage.PruneElements private void TestPruneElements(ITestStorageProvider provider) { // run 4 iterations of pruning: no adjacent blocks, one adjacent block on either side, and two adjacent blocks for (var iteration = 0; iteration < 4; iteration++) { provider.TestCleanup(); using (var kernel = new StandardKernel()) using (var storageManager = provider.OpenStorageManager()) { // add logging module kernel.Load(new ConsoleLoggingModule(LogLevel.Debug)); var blockTxesStorage = storageManager.BlockTxesStorage; // create a block var block = CreateFakeBlock(); var txCount = block.Transactions.Length; // determine expected merkle root node when fully pruned var expectedFinalDepth = (int)Math.Ceiling(Math.Log(txCount, 2)); var expectedFinalElement = new BlockTxNode(index: 0, depth: expectedFinalDepth, hash: block.Header.MerkleRoot, pruned: true, encodedTx: null); // pick a random pruning order var random = new Random(); var pruneOrderSource = Enumerable.Range(0, txCount).ToList(); var pruneOrder = new List<int>(txCount); while (pruneOrderSource.Count > 0) { var randomIndex = random.Next(pruneOrderSource.Count); pruneOrder.Add(pruneOrderSource[randomIndex]); pruneOrderSource.RemoveAt(randomIndex); } // add preceding block if (iteration == 1 || iteration == 3) blockTxesStorage.TryAddBlockTransactions(new UInt256(block.Hash.ToBigInteger() - 1), block.BlockTxes); // add the block to be pruned blockTxesStorage.TryAddBlockTransactions(block.Hash, block.BlockTxes); // add proceeding block if (iteration == 2 || iteration == 3) blockTxesStorage.TryAddBlockTransactions(new UInt256(block.Hash.ToBigInteger() + 1), block.BlockTxes); // prune the block var count = 0; foreach (var pruneIndex in pruneOrder) { Debug.WriteLine(count++); // prune a transaction blockTxesStorage.PruneElements(new[] { new KeyValuePair<UInt256, IEnumerable<int>>(block.Hash, new[] { pruneIndex }) }); // read block transactions IEnumerator<BlockTxNode> blockTxNodes; Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(block.Hash, out blockTxNodes)); // verify block transactions, exception will be fired if invalid MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, blockTxNodes.UsingAsEnumerable()).Count(); } // read fully pruned block and verify IEnumerator<BlockTxNode> finalBlockTxNodes; Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(block.Hash, out finalBlockTxNodes)); var finalNodes = MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, finalBlockTxNodes.UsingAsEnumerable()).ToList(); Assert.AreEqual(1, finalNodes.Count); Assert.AreEqual(expectedFinalElement, finalNodes[0]); // verify preceding block was not affected if (iteration == 1 || iteration == 3) { Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(new UInt256(block.Hash.ToBigInteger() - 1), out finalBlockTxNodes)); // verify block transactions, exception will be fired if invalid Assert.AreEqual(block.Transactions.Length, MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, finalBlockTxNodes.UsingAsEnumerable()).Count()); } // verify proceeding block was not affected if (iteration == 2 || iteration == 3) { Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(new UInt256(block.Hash.ToBigInteger() + 1), out finalBlockTxNodes)); // verify block transactions, exception will be fired if invalid Assert.AreEqual(block.Transactions.Length, MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, finalBlockTxNodes.UsingAsEnumerable()).Count()); } } } }
// IBlockTxesStorage.PruneElements private void TestPruneElements(ITestStorageProvider provider) { // run 4 iterations of pruning: no adjacent blocks, one adjacent block on either side, and two adjacent blocks for (var iteration = 0; iteration < 4; iteration++) { provider.TestCleanup(); using (var kernel = new StandardKernel()) using (var storageManager = provider.OpenStorageManager()) { // add logging module kernel.Load(new ConsoleLoggingModule(LogLevel.Debug)); var blockTxesStorage = storageManager.BlockTxesStorage; // create a block var block = CreateFakeBlock(); var txCount = block.Transactions.Length; // determine expected merkle root node when fully pruned var expectedFinalDepth = (int)Math.Ceiling(Math.Log(txCount, 2)); var expectedFinalElement = new BlockTxNode(index: 0, depth: expectedFinalDepth, hash: block.Header.MerkleRoot, pruned: true, encodedTx: null); // pick a random pruning order var random = new Random(); var pruneOrderSource = Enumerable.Range(0, txCount).ToList(); var pruneOrder = new List <int>(txCount); while (pruneOrderSource.Count > 0) { var randomIndex = random.Next(pruneOrderSource.Count); pruneOrder.Add(pruneOrderSource[randomIndex]); pruneOrderSource.RemoveAt(randomIndex); } // add preceding block if (iteration == 1 || iteration == 3) { blockTxesStorage.TryAddBlockTransactions(new UInt256(block.Hash.ToBigInteger() - 1), block.BlockTxes); } // add the block to be pruned blockTxesStorage.TryAddBlockTransactions(block.Hash, block.BlockTxes); // add proceeding block if (iteration == 2 || iteration == 3) { blockTxesStorage.TryAddBlockTransactions(new UInt256(block.Hash.ToBigInteger() + 1), block.BlockTxes); } // prune the block var count = 0; foreach (var pruneIndex in pruneOrder) { Debug.WriteLine(count++); // prune a transaction blockTxesStorage.PruneElements(new[] { new KeyValuePair <UInt256, IEnumerable <int> >(block.Hash, new[] { pruneIndex }) }); // read block transactions IEnumerator <BlockTxNode> blockTxNodes; Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(block.Hash, out blockTxNodes)); // verify block transactions, exception will be fired if invalid MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, blockTxNodes.UsingAsEnumerable()).Count(); } // read fully pruned block and verify IEnumerator <BlockTxNode> finalBlockTxNodes; Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(block.Hash, out finalBlockTxNodes)); var finalNodes = MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, finalBlockTxNodes.UsingAsEnumerable()).ToList(); Assert.AreEqual(1, finalNodes.Count); Assert.AreEqual(expectedFinalElement, finalNodes[0]); // verify preceding block was not affected if (iteration == 1 || iteration == 3) { Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(new UInt256(block.Hash.ToBigInteger() - 1), out finalBlockTxNodes)); // verify block transactions, exception will be fired if invalid Assert.AreEqual(block.Transactions.Length, MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, finalBlockTxNodes.UsingAsEnumerable()).Count()); } // verify proceeding block was not affected if (iteration == 2 || iteration == 3) { Assert.IsTrue(blockTxesStorage.TryReadBlockTxNodes(new UInt256(block.Hash.ToBigInteger() + 1), out finalBlockTxNodes)); // verify block transactions, exception will be fired if invalid Assert.AreEqual(block.Transactions.Length, MerkleTree.ReadMerkleTreeNodes(block.Header.MerkleRoot, finalBlockTxNodes.UsingAsEnumerable()).Count()); } } } }
private IEnumerator<BlockTxNode> ReadBlockTransactions(UInt256 blockHash, bool requireTx) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item; using (var jetTx = cursor.jetSession.BeginTransaction()) { int blockIndex; if (!TryGetBlockIndex(cursor, blockHash, out blockIndex)) throw new MissingDataException(blockHash); Api.JetSetCurrentIndex(cursor.jetSession, cursor.blocksTableId, "IX_BlockIndexTxIndex"); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockIndex, MakeKeyGrbit.NewKey); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, 0, MakeKeyGrbit.None); if (!Api.TrySeek(cursor.jetSession, cursor.blocksTableId, SeekGrbit.SeekGE)) throw new MissingDataException(blockHash); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockIndex, MakeKeyGrbit.NewKey); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, int.MaxValue, MakeKeyGrbit.None); if (!Api.TrySetIndexRange(cursor.jetSession, cursor.blocksTableId, SetIndexRangeGrbit.RangeUpperLimit)) throw new MissingDataException(blockHash); do { var txIndexColumn = new Int32ColumnValue { Columnid = cursor.txIndexColumnId }; var blockDepthColumn = new Int32ColumnValue { Columnid = cursor.blockDepthColumnId }; var blockTxHashColumn = new BytesColumnValue { Columnid = cursor.blockTxHashColumnId }; var blockTxBytesColumn = new BytesColumnValue { Columnid = cursor.blockTxBytesColumnId }; Api.RetrieveColumns(cursor.jetSession, cursor.blocksTableId, txIndexColumn, blockDepthColumn, blockTxHashColumn, blockTxBytesColumn); var txIndex = txIndexColumn.Value.Value; var depth = blockDepthColumn.Value.Value; var txHash = DbEncoder.DecodeUInt256(blockTxHashColumn.Value); var txBytes = blockTxBytesColumn.Value; // determine if transaction is pruned by its depth var pruned = depth >= 0; depth = Math.Max(0, depth); if (pruned && requireTx) throw new MissingDataException(blockHash); var blockTxNode = new BlockTxNode(txIndex, depth, txHash, pruned, txBytes?.ToImmutableArray()); yield return blockTxNode; } while (Api.TryMoveNext(cursor.jetSession, cursor.blocksTableId)); } } }
public static byte[] EncodeBlockTxNode(BlockTxNode blockTx) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodeBlockTxNode(writer, blockTx); return stream.ToArray(); } }
public static void EncodeBlockTxNode(BinaryWriter writer, BlockTxNode blockTx) { writer.WriteInt32(blockTx.Index); writer.WriteInt32(blockTx.Depth); writer.WriteUInt256(blockTx.Hash); writer.WriteBool(blockTx.Pruned); if (!blockTx.Pruned) writer.WriteBytes(blockTx.TxBytes.ToArray()); }
private IEnumerator <BlockTxNode> ReadBlockTransactions(UInt256 blockHash, bool requireTx) { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item; using (var jetTx = cursor.jetSession.BeginTransaction()) { int blockIndex; if (!TryGetBlockIndex(cursor, blockHash, out blockIndex)) { throw new MissingDataException(blockHash); } Api.JetSetCurrentIndex(cursor.jetSession, cursor.blocksTableId, "IX_BlockIndexTxIndex"); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockIndex, MakeKeyGrbit.NewKey); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, 0, MakeKeyGrbit.None); if (!Api.TrySeek(cursor.jetSession, cursor.blocksTableId, SeekGrbit.SeekGE)) { throw new MissingDataException(blockHash); } Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockIndex, MakeKeyGrbit.NewKey); Api.MakeKey(cursor.jetSession, cursor.blocksTableId, int.MaxValue, MakeKeyGrbit.None); if (!Api.TrySetIndexRange(cursor.jetSession, cursor.blocksTableId, SetIndexRangeGrbit.RangeUpperLimit)) { throw new MissingDataException(blockHash); } do { var txIndexColumn = new Int32ColumnValue { Columnid = cursor.txIndexColumnId }; var blockDepthColumn = new Int32ColumnValue { Columnid = cursor.blockDepthColumnId }; var blockTxHashColumn = new BytesColumnValue { Columnid = cursor.blockTxHashColumnId }; var blockTxBytesColumn = new BytesColumnValue { Columnid = cursor.blockTxBytesColumnId }; Api.RetrieveColumns(cursor.jetSession, cursor.blocksTableId, txIndexColumn, blockDepthColumn, blockTxHashColumn, blockTxBytesColumn); var txIndex = txIndexColumn.Value.Value; var depth = blockDepthColumn.Value.Value; var txHash = DbEncoder.DecodeUInt256(blockTxHashColumn.Value); var txBytes = blockTxBytesColumn.Value; // determine if transaction is pruned by its depth var pruned = depth >= 0; depth = Math.Max(0, depth); if (pruned && requireTx) { throw new MissingDataException(blockHash); } var blockTxNode = new BlockTxNode(txIndex, depth, txHash, pruned, txBytes?.ToImmutableArray()); yield return(blockTxNode); }while (Api.TryMoveNext(cursor.jetSession, cursor.blocksTableId)); } } }