Ejemplo n.º 1
0
        public void AddTwoTxesToNewBlock()
        {
            var key = new Key();

            var tx1 = new BlockTx()
            {
                Amount     = 1,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test").Public
            };

            var tx2 = new BlockTx()
            {
                Amount     = 5,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test2").Public
            };

            tx1.Sign(key);
            tx2.Sign(key);

            var block = new Block(0);

            block.AddTx(tx1);
            block.AddTx(tx2);
            Assert.NotNull(block);
            Assert.NotNull(block.Txes);
            Assert.AreEqual(2, block.Txes.Length);
        }
Ejemplo n.º 2
0
        public void TestExceptionInLoadTxInput()
        {
            var expectedException = new Exception();

            var coreStorage = new Mock <ICoreStorage>();

            var chainedHeader = RandomData.RandomChainedHeader();
            var tx            = RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 1
            });
            var txLookupKey = new TxLookupKey(UInt256.Zero, 0);
            var inputTx     = RandomData.RandomTransaction();
            var loadingTx   = new LoadingTx(1, tx, chainedHeader, ImmutableArray.Create(txLookupKey));

            var loadingTxes = new BufferBlock <LoadingTx>();

            loadingTxes.Post(loadingTx);
            loadingTxes.Complete();

            // throw expected exception when the input transaction is looked up
            BlockTx outputTx = null;

            coreStorage.Setup(x => x.TryGetTransaction(txLookupKey.BlockHash, txLookupKey.TxIndex, out outputTx)).Throws(expectedException);

            var loadedTxes = TxLoader.LoadTxes(coreStorage.Object, loadingTxes);

            Exception actualEx;

            AssertMethods.AssertAggregateThrows <Exception>(() =>
                                                            loadedTxes.ToEnumerable().ToList(), out actualEx);
            Assert.AreSame(expectedException, actualEx);
        }
Ejemplo n.º 3
0
        public void TestUtxoLookAheadWarmupException()
        {
            var deferredCursor = new Mock <IDeferredChainStateCursor>();

            deferredCursor.Setup(x => x.CursorCount).Returns(4);

            var blockTxes = new BufferBlock <DecodedBlockTx>();

            var lookAhead = UtxoLookAhead.LookAhead(blockTxes, deferredCursor.Object);

            var blockTx0 = BlockTx.Create(0, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));
            var blockTx1 = BlockTx.Create(1, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));

            var expectedException = new InvalidOperationException();

            deferredCursor.Setup(x => x.WarmUnspentTx(blockTx1.Transaction.Inputs[0].PrevTxOutputKey.TxHash)).Throws(expectedException);

            blockTxes.Post(blockTx0);
            blockTxes.Post(blockTx1);
            blockTxes.Complete();

            Exception actualEx;

            AssertMethods.AssertAggregateThrows <Exception>(() =>
                                                            lookAhead.Completion.Wait(2000), out actualEx);
            Assert.AreSame(expectedException, actualEx);
        }
Ejemplo n.º 4
0
        public void TestUtxoLookAheadCoinbaseOnly()
        {
            var blockTxes = new BufferBlock <DecodedBlockTx>();

            var deferredCursor = new Mock <IDeferredChainStateCursor>();

            deferredCursor.Setup(x => x.CursorCount).Returns(4);

            var lookAhead = UtxoLookAhead.LookAhead(blockTxes, deferredCursor.Object);

            // post a coinbase transaction, the inputs should not be looked up
            var blockTx = BlockTx.Create(0, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));

            blockTxes.Post(blockTx);
            blockTxes.Complete();

            // verify coinbase tx was forarded, with no inputs used from the chain state (no inputs were mocked)
            var warmedTxes = lookAhead.ReceiveAllAsync().Result;

            Assert.AreEqual(1, warmedTxes.Count);
            Assert.AreEqual(warmedTxes[0].Hash, blockTx.Hash);

            Assert.IsTrue(lookAhead.Completion.Wait(2000));
        }
Ejemplo n.º 5
0
        private IEnumerable <BlockTx> ReadBlockTransactions(UInt256 blockHash, int blockId)
        {
            using (var handle = this.cursorCache.TakeItem())
            {
                var cursor = handle.Item;

                using (var jetTx = cursor.jetSession.BeginTransaction())
                {
                    var sha256 = new SHA256Managed();

                    Api.JetSetCurrentIndex(cursor.jetSession, cursor.blocksTableId, "IX_BlockIdTxIndex");
                    Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockId, MakeKeyGrbit.NewKey);
                    Api.MakeKey(cursor.jetSession, cursor.blocksTableId, -1, MakeKeyGrbit.None);

                    if (!Api.TrySeek(cursor.jetSession, cursor.blocksTableId, SeekGrbit.SeekGE))
                    {
                        yield break;
                    }

                    do
                    {
                        if (blockId != Api.RetrieveColumnAsInt32(cursor.jetSession, cursor.blocksTableId, cursor.blockIdColumnId).Value)
                        {
                            yield break;
                        }

                        var txIndex = Api.RetrieveColumnAsInt32(cursor.jetSession, cursor.blocksTableId, cursor.blockTxIndexColumnId).Value;
                        var depth   = Api.RetrieveColumnAsInt32(cursor.jetSession, cursor.blocksTableId, cursor.blockDepthColumnId).Value;
                        var txHash  = DbEncoder.DecodeUInt256(Api.RetrieveColumn(cursor.jetSession, cursor.blocksTableId, cursor.blockTxHashColumnId));
                        var txBytes = Api.RetrieveColumn(cursor.jetSession, cursor.blocksTableId, cursor.blockTxBytesColumnId);

                        // determine if transaction is pruned by its depth
                        var pruned = depth >= 0;
                        depth = Math.Max(0, depth);

                        Transaction tx;
                        if (!pruned)
                        {
                            // verify transaction is not corrupt
                            if (txHash != new UInt256(sha256.ComputeDoubleHash(txBytes)))
                            {
                                throw new MissingDataException(blockHash);
                            }

                            tx = DataEncoder.DecodeTransaction(txBytes);
                        }
                        else
                        {
                            tx = null;
                        }

                        var blockTx = new BlockTx(txIndex, depth, txHash, pruned, tx);

                        yield return(blockTx);
                    } while (Api.TryMoveNext(cursor.jetSession, cursor.blocksTableId));
                }
            }
        }
Ejemplo n.º 6
0
 public void ValidationTransactionScript(Chain newChain, BlockTx tx, TxInput txInput, int txInputIndex, PrevTxOutput prevTxOutput)
 {
     if (ValidationTransactionScriptAction == null)
     {
         coreRules.ValidationTransactionScript(newChain, tx, txInput, txInputIndex, prevTxOutput);
     }
     else
     {
         ValidationTransactionScriptAction(newChain, tx, txInput, txInputIndex, prevTxOutput);
     }
 }
Ejemplo n.º 7
0
        public void TestDoubleSpend()
        {
            // prepare block
            var fakeHeaders      = new FakeHeaders();
            var chainedHeader0   = fakeHeaders.GenesisChained();
            var chainedHeader1   = fakeHeaders.NextChained();
            var chainedHeader2   = fakeHeaders.NextChained();
            var chain            = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder();
            var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));
            var emptyCoinbaseTx1 = BlockTx.Create(0, Transaction.Create(1, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));

            // initialize memory utxo builder storage
            var memoryStorage          = new MemoryStorageManager();
            var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item;

            memoryChainStateCursor.BeginTransaction();

            // initialize utxo builder
            var utxoBuilder = new UtxoBuilder();

            // prepare an unspent transaction
            var txHash      = new UInt256(100);
            var unspentTx   = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 1, OutputState.Unspent);
            var txOutputKey = new TxOutputKey(txHash, 0);
            var txOutput    = new TxOutput(0, ImmutableArray <byte> .Empty);

            // add the unspent transaction
            memoryChainStateCursor.TryAddUnspentTx(unspentTx);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutputKey, txOutput);

            // create an input to spend the unspent transaction
            var input = new TxInput(txHash, 0, ImmutableArray.Create <byte>(), 0);
            var tx    = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader1);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            UnspentTx actualUnspentTx; TxOutput actualTxOutput;

            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.IsFullySpent);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutputKey, out actualTxOutput));
            Assert.AreEqual(txOutput, actualTxOutput);

            // attempt to spend the input again, validation exception should be thrown
            chain.AddBlock(chainedHeader2);
            AssertMethods.AssertAggregateThrows <ValidationException>(() =>
                                                                      utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx }.ToBufferBlock()).ToEnumerable().ToList());
        }
Ejemplo n.º 8
0
        public bool TryGetTransaction(UInt256 blockHash, int txIndex, out BlockTx transaction)
        {
            using (var handle = this.cursorCache.TakeItem())
            {
                var cursor = handle.Item;

                using (var jetTx = cursor.jetSession.BeginTransaction())
                {
                    int blockIndex;
                    if (!TryGetBlockIndex(cursor, blockHash, out blockIndex))
                    {
                        transaction = null;
                        return(false);
                    }

                    Api.JetSetCurrentIndex(cursor.jetSession, cursor.blocksTableId, "IX_BlockIndexTxIndex");
                    Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockIndex, MakeKeyGrbit.NewKey);
                    Api.MakeKey(cursor.jetSession, cursor.blocksTableId, txIndex, MakeKeyGrbit.None);
                    if (Api.TrySeek(cursor.jetSession, cursor.blocksTableId, SeekGrbit.SeekEQ))
                    {
                        var blockTxHashColumn = new BytesColumnValue {
                            Columnid = cursor.blockTxHashColumnId
                        };
                        var blockTxBytesColumn = new BytesColumnValue {
                            Columnid = cursor.blockTxBytesColumnId
                        };
                        Api.RetrieveColumns(cursor.jetSession, cursor.blocksTableId, blockTxHashColumn, blockTxBytesColumn);

                        if (blockTxBytesColumn.Value != null)
                        {
                            var txHash = DbEncoder.DecodeUInt256(blockTxHashColumn.Value);
                            transaction = new BlockTx(txIndex, txHash, blockTxBytesColumn.Value.ToImmutableArray());
                            return(true);
                        }
                        else
                        {
                            transaction = null;
                            return(false);
                        }
                    }
                    else
                    {
                        transaction = null;
                        return(false);
                    }
                }
            }
        }
Ejemplo n.º 9
0
        public void SingTxWithPublicKey()
        {
            var key = new Key();
            var tx  = new BlockTx()
            {
                Amount     = 1,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test").Public
            };
            var serializedTx = tx.Serialize();

            tx.Sign(key);
            Assert.NotNull(tx.Fingerprint);
            Assert.NotZero(tx.Fingerprint.Length);
            Assert.IsTrue(key.Verify(serializedTx, ByteArray.HexToByte(tx.Fingerprint)));
        }
Ejemplo n.º 10
0
        public void ValidateTxFailFromAlteredTransactionAmount()
        {
            var key = new Key();
            var tx  = new BlockTx()
            {
                Amount     = 1,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test").Public
            };

            tx.Sign(key);
            var signKey = new Key(ByteArray.HexToByte(tx.PubKeyFrom));

            tx.Amount = 2;
            Assert.IsFalse(signKey.Verify(tx.Serialize(), ByteArray.HexToByte(tx.Fingerprint)));
        }
Ejemplo n.º 11
0
        public void ValidateTxFromPublicKey()
        {
            var key = new Key();
            var tx  = new BlockTx()
            {
                Amount     = 1,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test").Public
            };
            var serializedTx = tx.Serialize();

            tx.Sign(key);
            var signKey = new Key(ByteArray.HexToByte(tx.PubKeyFrom));

            Assert.IsTrue(signKey.Verify(serializedTx, ByteArray.HexToByte(tx.Fingerprint)));
        }
Ejemplo n.º 12
0
        public bool TryAddBlockTransactions(UInt256 blockHash, IEnumerable <EncodedTx> blockTxes)
        {
            if (ContainsBlock(blockHash))
            {
                return(false);
            }

            var writeBatch = new WriteBatch();

            try
            {
                int txCount;
                using (var snapshot = db.GetSnapshot())
                {
                    var readOptions = new ReadOptions {
                        Snapshot = snapshot
                    };

                    var txIndex = 0;
                    foreach (var tx in blockTxes)
                    {
                        var key = DbEncoder.EncodeBlockHashTxIndex(blockHash, txIndex);

                        Slice existingValue;
                        if (db.TryGet(readOptions, key, out existingValue))
                        {
                            return(false);
                        }

                        var blockTx = new BlockTx(txIndex, tx);
                        var value   = DataEncoder.EncodeBlockTxNode(blockTx);

                        writeBatch.Put(key, value);

                        txIndex++;
                    }

                    txCount = txIndex;
                }

                return(TryAddBlockInner(blockHash, txCount, writeBatch));
            }
            finally
            {
                writeBatch.Dispose();
            }
        }
Ejemplo n.º 13
0
        public static Block DecodeBlock(UInt256 blockHash, byte[] buffer, ref int offset)
        {
            var header = DecodeBlockHeader(blockHash, buffer, ref offset);

            var blockTxesCount = buffer.ReadVarInt(ref offset).ToIntChecked();
            var blockTxes      = ImmutableArray.CreateBuilder <BlockTx>(blockTxesCount);

            for (var i = 0; i < blockTxesCount; i++)
            {
                var encodedTx = DecodeEncodedTx(null, buffer, ref offset);
                var blockTx   = new BlockTx(i, encodedTx);

                blockTxes.Add(blockTx);
            }

            return(new Block(header, blockTxes.MoveToImmutable()));
        }
Ejemplo n.º 14
0
        public void ValidateTxFailFromAlteredPublicKey()
        {
            var key = new Key();
            var tx  = new BlockTx()
            {
                Amount     = 1,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test").Public
            };
            var serializedTx = tx.Serialize();

            tx.Sign(key);
            var signKey       = new Key(ByteArray.HexToByte(tx.PubKeyFrom));
            var alteredPubKey = ByteArray.HexToByte(tx.Fingerprint.Replace("B", "C"));

            Assert.IsFalse(signKey.Verify(serializedTx, alteredPubKey));
        }
Ejemplo n.º 15
0
        public bool TryGetTransaction(UInt256 blockHash, int txIndex, out BlockTx transaction)
        {
            ImmutableSortedDictionary <int, BlockTxNode> blockTxes;
            BlockTxNode blockTxNode;

            if (this.allBlockTxNodes.TryGetValue(blockHash, out blockTxes) &&
                blockTxes.TryGetValue(txIndex, out blockTxNode) &&
                !blockTxNode.Pruned)
            {
                transaction = blockTxNode.ToBlockTx();
                return(true);
            }
            else
            {
                transaction = null;
                return(false);
            }
        }
Ejemplo n.º 16
0
        public static Block DecodeBlock(BinaryReader reader)
        {
            var header = DecodeBlockHeader(null, reader.ReadExactly(80));

            var blockTxesCount = reader.ReadVarInt().ToIntChecked();
            var blockTxes      = ImmutableArray.CreateBuilder <BlockTx>(blockTxesCount);

            for (var i = 0; i < blockTxesCount; i++)
            {
                var txBytes   = ReadTransaction(reader);
                var encodedTx = DecodeEncodedTx(null, txBytes);
                var blockTx   = new BlockTx(i, encodedTx);

                blockTxes.Add(blockTx);
            }

            return(new Block(header, blockTxes.MoveToImmutable()));
        }
Ejemplo n.º 17
0
        public void AddTxToNewBlock()
        {
            var key = new Key();
            var tx  = new BlockTx()
            {
                Amount     = 1,
                PubKeyFrom = key.Public,
                PubKeyTo   = new Key("test").Public
            };

            tx.Sign(key);
            var block = new Block(0);

            block.AddTx(tx);
            Assert.NotNull(block);
            Assert.NotNull(block.Txes);
            Assert.AreEqual(1, block.Txes.Length);
        }
Ejemplo n.º 18
0
        public void TestUtxoLookAheadWithTransactions()
        {
            var deferredCursor = new Mock <IDeferredChainStateCursor>();

            deferredCursor.Setup(x => x.CursorCount).Returns(4);

            var blockTxes = new BufferBlock <DecodedBlockTx>();

            var lookAhead = UtxoLookAhead.LookAhead(blockTxes, deferredCursor.Object);

            var blockTx0 = BlockTx.Create(0, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));
            var blockTx1 = BlockTx.Create(1, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));
            var blockTx2 = BlockTx.Create(2, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));

            blockTxes.Post(blockTx0);
            blockTxes.Post(blockTx1);
            blockTxes.Post(blockTx2);
            blockTxes.Complete();

            // verify each transaction was forwarded
            var expectedBlockTxes = new[] { blockTx0, blockTx1, blockTx2 };
            var warmedTxes        = lookAhead.ReceiveAllAsync().Result;

            Assert.AreEqual(3, warmedTxes.Count);
            CollectionAssert.AreEqual(expectedBlockTxes.Select(x => x.Hash).ToList(), warmedTxes.Select(x => x.Hash).ToList());

            // verify each non-coinbase input transaction was warmed up
            var expectedLookups = expectedBlockTxes.Skip(1).SelectMany(x => x.Transaction.Inputs.Select(input => input.PrevTxOutputKey.TxHash));

            foreach (var txHash in expectedLookups)
            {
                deferredCursor.Verify(x => x.WarmUnspentTx(txHash));
            }

            Assert.IsTrue(lookAhead.Completion.Wait(2000));
        }
Ejemplo n.º 19
0
        public void ValidationTransactionScript(Chain newChain, BlockTx tx, TxInput txInput, int txInputIndex, PrevTxOutput prevTxOutput)
        {
            var chainedHeader = newChain.LastBlock;

            // BIP16 didn't become active until Apr 1 2012
            var nBIP16SwitchTime      = DateTimeOffset.FromUnixTimeSeconds(1333238400U);
            var strictPayToScriptHash = chainedHeader.Time >= nBIP16SwitchTime;

            var flags = strictPayToScriptHash ? verify_flags_type.verify_flags_p2sh : verify_flags_type.verify_flags_none;

            // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks,
            // when 75% of the network has upgraded:
            if (chainedHeader.Version >= 3 &&
                IsSuperMajority(3, newChain, ChainParams.MajorityEnforceBlockUpgrade))
            {
                flags |= verify_flags_type.verify_flags_dersig;
            }

            // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4
            // blocks, when 75% of the network has upgraded:
            if (chainedHeader.Version >= 4 &&
                IsSuperMajority(4, newChain, ChainParams.MajorityEnforceBlockUpgrade))
            {
                flags |= verify_flags_type.verify_flags_checklocktimeverify;
            }

            var result = LibbitcoinConsensus.VerifyScript(
                tx.TxBytes,
                prevTxOutput.ScriptPublicKey,
                txInputIndex,
                flags);

            if (!result)
            {
                logger.Debug($"Script did not pass in block: {chainedHeader.Hash}, tx: {tx.Index}, {tx.Hash}, input: {txInputIndex}");
                throw new ValidationException(chainedHeader.Hash);
            }
        }
Ejemplo n.º 20
0
        public bool TryGetTransaction(UInt256 blockHash, int txIndex, out BlockTx transaction)
        {
            Slice value;

            if (db.TryGet(new ReadOptions(), DbEncoder.EncodeBlockHashTxIndex(blockHash, txIndex), out value))
            {
                var blockTxNode = DataDecoder.DecodeBlockTxNode(value.ToArray());
                if (!blockTxNode.Pruned)
                {
                    transaction = blockTxNode.ToBlockTx();
                    return(true);
                }
                else
                {
                    transaction = default(BlockTx);
                    return(false);
                }
            }
            else
            {
                transaction = default(BlockTx);
                return(false);
            }
        }
Ejemplo n.º 21
0
 public bool TryGetTransaction(UInt256 blockHash, int txIndex, out BlockTx transaction)
 {
     return(this.blockTxesStorage.Value.TryGetTransaction(blockHash, txIndex, out transaction));
 }
Ejemplo n.º 22
0
        public void TestReplayForward()
        {
            var coreStorage = new Mock <ICoreStorage>();
            var chainState  = new Mock <IChainState>();

            chainState.Setup(x => x.CursorCount).Returns(4);

            var testBlocks = new TestBlocks();

            var block         = testBlocks.MineAndAddBlock(txCount: 10);
            var chainedHeader = testBlocks.Chain.LastBlock;

            chainState.Setup(x => x.Chain).Returns(() => testBlocks.Chain);

            // mock block txes read
            var blockTxes = block.Transactions.Select((tx, txIndex) => (BlockTx)BlockTx.Create(txIndex, tx)).GetEnumerator();

            coreStorage.Setup(x => x.TryReadBlockTransactions(chainedHeader.Hash, out blockTxes)).Returns(true);

            // mock unspent tx lookup
            var expectedValue = 50UL * (ulong)100.MILLION();

            for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++)
            {
                var tx = block.Transactions[txIndex];
                for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
                {
                    var input = tx.Inputs[inputIndex];

                    // create a fake unspent tx, with enough outputs for this input
                    var unspentTx = new UnspentTx(input.PrevTxOutputKey.TxHash, blockIndex: 1, txIndex: txIndex * inputIndex, txVersion: 0, isCoinbase: false,
                                                  outputStates: tx.IsCoinbase ? OutputStates.Empty : new OutputStates(input.PrevTxOutputKey.TxOutputIndex.ToIntChecked() + 1, OutputState.Unspent));
                    var txOutput = new TxOutput(tx.Outputs[0].Value, tx.Outputs[0].ScriptPublicKey);

                    chainState.Setup(x => x.TryGetUnspentTx(unspentTx.TxHash, out unspentTx)).Returns(true);
                    chainState.Setup(x => x.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)).Returns(true);
                }
            }

            var validatableTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage.Object, chainState.Object, chainedHeader).ToEnumerable().ToList();

            // verify correct number of transactions were replayed
            Assert.AreEqual(validatableTxes.Count, block.Transactions.Length);

            expectedValue = 50UL * (ulong)100.MILLION();
            foreach (var validatableTx in validatableTxes)
            {
                // verify validatable tx matches original block tx
                Assert.AreEqual(block.Transactions[validatableTx.Index].Hash, validatableTx.Transaction.Hash);

                // if coinbase, verify no tx outputs for coinbase inputs
                if (validatableTx.IsCoinbase)
                {
                    Assert.AreEqual(0, validatableTx.PrevTxOutputs.Length);
                }
                else
                {
                    // verify there is a tx output for each input
                    Assert.AreEqual(block.Transactions[validatableTx.Index].Inputs.Length, validatableTx.PrevTxOutputs.Length);

                    // verify each tx output matches the mocked data
                    for (var inputIndex = 0; inputIndex < validatableTx.Transaction.Inputs.Length; inputIndex++)
                    {
                        var prevTxOutput = validatableTx.PrevTxOutputs[inputIndex];

                        expectedValue -= 1;
                        Assert.AreEqual(expectedValue, prevTxOutput.Value);
                        CollectionAssert.AreEqual(block.Transactions[0].Outputs[0].ScriptPublicKey, prevTxOutput.ScriptPublicKey);
                    }
                }
            }
        }
Ejemplo n.º 23
0
 public bool TryGetTransaction(UInt256 blockHash, int txIndex, out BlockTx transaction)
 {
     return(GetStorage(blockHash).TryGetTransaction(blockHash, txIndex, out transaction));
 }
Ejemplo n.º 24
0
        private void TestRollback(ITestStorageProvider provider)
        {
            ConsoleLoggingModule.Configure();
            var logger = LogManager.GetCurrentClassLogger();

            var blockCount = 10.THOUSAND();
            var checkUtxoHashFrequencey = 1000;

            var blockProvider = new TestNet3BlockProvider();
            var blocks        = blockProvider.ReadBlocks().Take(blockCount).ToList();

            var genesisBlock  = blocks[0];
            var genesisHeader = new ChainedHeader(genesisBlock.Header, height: 0, totalWork: 0, dateSeen: DateTimeOffset.Now);
            var genesisChain  = Chain.CreateForGenesisBlock(genesisHeader);

            var chainParams = new Testnet3Params();
            var rules       = new CoreRules(chainParams)
            {
                IgnoreScripts      = true,
                IgnoreSignatures   = true,
                IgnoreScriptErrors = true
            };

            using (var storageManager = provider.OpenStorageManager())
                using (var coreStorage = new CoreStorage(storageManager))
                    using (var chainStateBuilder = new ChainStateBuilder(rules, coreStorage, storageManager))
                    {
                        // add blocks to storage
                        coreStorage.AddGenesisBlock(ChainedHeader.CreateForGenesisBlock(blocks[0].Header));
                        foreach (var block in blocks)
                        {
                            coreStorage.TryAddBlock(block);
                        }

                        // store empty utxo hash
                        var expectedUtxoHashes = new List <UInt256>();
                        using (var chainState = chainStateBuilder.ToImmutable())
                            expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState));

                        // calculate utxo forward and store its state at each step along the way
                        for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                        {
                            logger.Info($"Adding: {blockIndex:N0}");

                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);

                            chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select(
                                                                (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait();

                            if (blockIndex % checkUtxoHashFrequencey == 0 || blockIndex == blocks.Count - 1)
                            {
                                using (var chainState = chainStateBuilder.ToImmutable())
                                    expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState));
                            }
                        }

                        // verify the utxo state before rolling back
                        var expectedLastUtxoHash = UInt256.ParseHex("5f155c7d8a5c850d5fb2566aec5110caa40e270184126d17022ae9780fd65fd9");
                        Assert.AreEqual(expectedLastUtxoHash, expectedUtxoHashes.Last());
                        expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1);

                        // roll utxo backwards and validate its state at each step along the way
                        for (var blockIndex = blocks.Count - 1; blockIndex >= 0; blockIndex--)
                        {
                            logger.Info($"Rolling back: {blockIndex:N0}");

                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);
                            var blockTxes     = block.Transactions.Select((tx, txIndex) => BlockTx.Create(txIndex, tx));

                            chainStateBuilder.RollbackBlock(chainedHeader, blockTxes);

                            if ((blockIndex - 1) % checkUtxoHashFrequencey == 0 || blockIndex == 0)
                            {
                                var expectedUtxoHash = expectedUtxoHashes.Last();
                                expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1);

                                using (var chainState = chainStateBuilder.ToImmutable())
                                    Assert.AreEqual(expectedUtxoHash, UtxoCommitment.ComputeHash(chainState));
                            }
                        }

                        // verify chain state was rolled all the way back
                        Assert.AreEqual(-1, chainStateBuilder.Chain.Height);
                        Assert.AreEqual(0, expectedUtxoHashes.Count);

                        // calculate utxo forward again
                        for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++)
                        {
                            logger.Info($"Adding: {blockIndex:N0}");

                            var block         = blocks[blockIndex];
                            var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now);

                            chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select(
                                                                (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait();
                        }

                        // verify final utxo state again
                        using (var chainState = chainStateBuilder.ToImmutable())
                            Assert.AreEqual(expectedLastUtxoHash, UtxoCommitment.ComputeHash(chainState));
                    }
        }
Ejemplo n.º 25
0
        public void TestSimpleSpend()
        {
            // prepare block
            var fakeHeaders      = new FakeHeaders();
            var chainedHeader0   = fakeHeaders.GenesisChained();
            var chainedHeader1   = fakeHeaders.NextChained();
            var chainedHeader2   = fakeHeaders.NextChained();
            var chainedHeader3   = fakeHeaders.NextChained();
            var chain            = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder();
            var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));
            var emptyCoinbaseTx1 = BlockTx.Create(1, Transaction.Create(1, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));
            var emptyCoinbaseTx2 = BlockTx.Create(2, Transaction.Create(2, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));

            // initialize memory utxo builder storage
            var memoryStorage          = new MemoryStorageManager();
            var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item;

            memoryChainStateCursor.BeginTransaction();

            // initialize utxo builder
            var utxoBuilder = new UtxoBuilder();

            // prepare an unspent transaction
            var txHash       = new UInt256(100);
            var unspentTx    = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 3, OutputState.Unspent);
            var txOutput1Key = new TxOutputKey(txHash, 0);
            var txOutput1    = new TxOutput(0, ImmutableArray <byte> .Empty);
            var txOutput2Key = new TxOutputKey(txHash, 1);
            var txOutput2    = new TxOutput(1, ImmutableArray <byte> .Empty);
            var txOutput3Key = new TxOutputKey(txHash, 2);
            var txOutput3    = new TxOutput(2, ImmutableArray <byte> .Empty);

            // prepare unspent output
            var unspentTransactions = ImmutableDictionary.Create <UInt256, UnspentTx>().Add(txHash, unspentTx);

            // add the unspent transaction
            memoryChainStateCursor.TryAddUnspentTx(unspentTx);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput2Key, txOutput2);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput3Key, txOutput3);

            // create an input to spend the unspent transaction's first output
            var input0 = new TxInput(txHash, 0, ImmutableArray.Create <byte>(), 0);
            var tx0    = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input0), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader1);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx0 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            UnspentTx actualUnspentTx; TxOutput actualTxOutput;

            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3);
            Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Unspent);
            Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent);

            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput));
            Assert.AreEqual(txOutput1, actualTxOutput);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput2Key, out actualTxOutput));
            Assert.AreEqual(txOutput2, actualTxOutput);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput3Key, out actualTxOutput));
            Assert.AreEqual(txOutput3, actualTxOutput);

            // create an input to spend the unspent transaction's second output
            var input1 = new TxInput(txHash, 1, ImmutableArray.Create <byte>(), 0);
            var tx1    = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input1), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader2);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx1 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3);
            Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent);

            // create an input to spend the unspent transaction's third output
            var input2 = new TxInput(txHash, 2, ImmutableArray.Create <byte>(), 0);
            var tx2    = BlockTx.Create(2, Transaction.Create(0, ImmutableArray.Create(input2), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader3);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx2, tx2 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.IsFullySpent);
        }
Ejemplo n.º 26
0
        public void TestUtxoLookAheadOrdering()
        {
            var deferredCursor = new Mock <IDeferredChainStateCursor>();

            deferredCursor.Setup(x => x.CursorCount).Returns(4);

            var blockTxes = new BufferBlock <DecodedBlockTx>();

            var lookAhead = UtxoLookAhead.LookAhead(blockTxes, deferredCursor.Object);

            var blockTx0 = BlockTx.Create(0, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));
            var blockTx1 = BlockTx.Create(1, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));
            var blockTx2 = BlockTx.Create(2, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));
            var blockTx3 = BlockTx.Create(3, RandomData.RandomTransaction(new RandomDataOptions {
                TxInputCount = 2
            }));

            // setup events so that transactions finish in 0, 3, 1, 2 order
            using (var blockTx1ReadEvent = new ManualResetEventSlim())
                using (var blockTx2ReadEvent = new ManualResetEventSlim())
                    using (var blockTx3ReadEvent = new ManualResetEventSlim())
                    {
                        deferredCursor.Setup(x => x.WarmUnspentTx(blockTx1.Transaction.Inputs[0].PrevTxOutputKey.TxHash))
                        .Callback(() =>
                        {
                            blockTx3ReadEvent.Wait();
                            blockTx1ReadEvent.Set();
                        });

                        deferredCursor.Setup(x => x.WarmUnspentTx(blockTx2.Transaction.Inputs[0].PrevTxOutputKey.TxHash))
                        .Callback(() =>
                        {
                            blockTx3ReadEvent.Wait();
                            blockTx1ReadEvent.Wait();
                            blockTx2ReadEvent.Set();
                        });

                        deferredCursor.Setup(x => x.WarmUnspentTx(blockTx3.Transaction.Inputs[0].PrevTxOutputKey.TxHash))
                        .Callback(() =>
                        {
                            blockTx3ReadEvent.Set();
                        });

                        blockTxes.Post(blockTx0);
                        blockTxes.Post(blockTx1);
                        blockTxes.Post(blockTx2);
                        blockTxes.Post(blockTx3);
                        blockTxes.Complete();

                        // verify each transaction was forwarded, in the correct order
                        var expectedBlockTxes = new[] { blockTx0, blockTx1, blockTx2, blockTx3 };
                        var warmedTxes        = lookAhead.ReceiveAllAsync().Result;
                        Assert.AreEqual(4, warmedTxes.Count);
                        CollectionAssert.AreEqual(expectedBlockTxes.Select(x => x.Hash).ToList(), warmedTxes.Select(x => x.Hash).ToList());
                    }

            Assert.IsTrue(lookAhead.Completion.Wait(2000));
        }
Ejemplo n.º 27
0
        public void TestReadOneLoadingTx()
        {
            var coreStorageMock = new Mock <ICoreStorage>();

            // create a fake transaction with 4 inputs
            var prevTxCount   = 4;
            var txIndex       = 1;
            var chainedHeader = RandomData.RandomChainedHeader();

            // create previous transactions for 4 inputs
            var prevTxes = new Transaction[prevTxCount];
            var inputs   = new TxInput[prevTxCount];

            for (var i = 0; i < prevTxCount; i++)
            {
                var prevTx      = RandomData.RandomTransaction();
                var prevBlockTx = (BlockTx)BlockTx.Create(i, prevTx);

                prevTxes[i] = prevTx;
                inputs[i]   = new TxInput(prevTx.Hash, 0, ImmutableArray.Create <byte>(), 0);

                // mock retrieval of the previous transaction
                coreStorageMock.Setup(coreStorage => coreStorage.TryGetTransaction(UInt256.Zero, i, out prevBlockTx)).Returns(true);
            }

            // create a loading tx with the 4 inputs referencing block hash 0
            var tx = RandomData.RandomTransaction(new RandomDataOptions {
                TxOutputCount = 1
            })
                     .CreateWith(Inputs: inputs.ToImmutableArray()).Transaction;
            var prevOutputTxKeys = ImmutableArray.CreateRange(
                Enumerable.Range(0, prevTxCount).Select(x => new TxLookupKey(UInt256.Zero, x)));
            var loadingTx = new LoadingTx(txIndex, tx, chainedHeader, prevOutputTxKeys);

            // begin queuing transactions to load
            var loadingTxes = new BufferBlock <LoadingTx>();

            loadingTxes.Post(loadingTx);
            loadingTxes.Complete();

            // begin transaction loading
            var txLoader = TxLoader.LoadTxes(coreStorageMock.Object, loadingTxes);

            // verify the loaded transaction
            var loadedTxesBuffer = new BufferBlock <LoadedTx>();

            txLoader.LinkTo(loadedTxesBuffer, new DataflowLinkOptions {
                PropagateCompletion = true
            });
            txLoader.Completion.Wait();

            IList <LoadedTx> actualLoadedTxes;

            Assert.IsTrue(loadedTxesBuffer.TryReceiveAll(out actualLoadedTxes));

            var actualLoadedTx = actualLoadedTxes.Single();

            Assert.AreEqual(loadingTx.TxIndex, actualLoadedTx.TxIndex);
            Assert.AreEqual(loadingTx.Transaction, actualLoadedTx.Transaction);
            CollectionAssert.AreEqual(prevTxes.Select(x => x.Hash).ToArray(), actualLoadedTx.InputTxes.Select(x => x.Hash).ToArray());
        }