public async Task DoubleSpendCheckCleanUp()
        {
            //arrange
            cleanUpTxService.Pause();
            var cleanUpTxTriggeredSubscription = EventBus.Subscribe <CleanUpTxTriggeredEvent>();

            List <Tx> txList = await CreateAndInsertTxAsync(false, true);

            (_, _, var firstBlockHash) = await InsertDoubleSpend();

            await CheckTxListPresentInDbAsync(txList, true);
            await CheckBlockPresentInDbAsync(firstBlockHash);

            var doubleSpends = (await TxRepositoryPostgres.GetTxsToSendBlockDSNotificationsAsync()).ToList();

            Assert.AreEqual(1, doubleSpends.Count);
            Assert.IsTrue(doubleSpends.Any(x => new uint256(x.TxExternalId) == new uint256(Tx2Hash)));

            foreach (var txDoubleSpend in doubleSpends)
            {
                await TxRepositoryPostgres.SetBlockDoubleSpendSendDateAsync(txDoubleSpend.TxInternalId, txDoubleSpend.BlockInternalId, txDoubleSpend.DoubleSpendTxId, MockedClock.UtcNow);
            }

            doubleSpends = (await TxRepositoryPostgres.GetTxsToSendBlockDSNotificationsAsync()).ToList();
            Assert.AreEqual(0, doubleSpends.Count);

            using (MockedClock.NowIs(DateTime.UtcNow.AddDays(cleanUpTxAfterDays)))
            {
                await ResumeAndWaitForCleanup(cleanUpTxTriggeredSubscription);

                // check if everything in db was cleared
                await CheckBlockNotPresentInDb(firstBlockHash);
                await CheckTxListNotPresentInDbAsync(txList);
            }
        }
        public async Task StoreUnconfirmedParentsOnSubmitTxAsync()
        {
            using CancellationTokenSource cts = new(cancellationTimeout);

            await RegisterNodesWithServiceAndWaitAsync(cts.Token);

            Assert.AreEqual(1, zmqService.GetActiveSubscriptions().Count());

            // Subscribe invalidtx events
            var invalidTxDetectedSubscription = EventBus.Subscribe <InvalidTxDetectedEvent>();

            // Create and submit first transaction
            var coin = availableCoins.Dequeue();

            var(txHex1, txId1) = CreateNewTransaction(coin, new Money(1000L));
            var response = await node0.RpcClient.SendRawTransactionAsync(HelperTools.HexStringToByteArray(txHex1), true, false, cts.Token);

            // Create chain based on first transaction with last transaction being submited to mAPI
            var(lastTxHex, lastTxId, mapiCount) = await CreateUnconfirmedAncestorChainAsync(txHex1, txId1, 100, 0, true, cts.Token);

            // Check that first tx is in database
            long?txInternalId1 = await TxRepositoryPostgres.GetTransactionInternalId((new uint256(txId1)).ToBytes());

            Assert.IsTrue(txInternalId1.HasValue);
            Assert.AreNotEqual(0, txInternalId1.Value);
        }
Esempio n. 3
0
        public async Task SubmitTxWithDsCheckAndOP_RETURN()
        {
            StartupLiveMAPI();

            var coin = availableCoins.Dequeue();

            var tx1    = CreateDS_OP_RETURN_Tx(new Coin[] { coin }, 00);
            var tx1Hex = tx1.ToHex();
            var tx1Id  = tx1.GetHash().ToString();

            var payload = await SubmitTransactions(new string[] { tx1Hex });

            // Create double spend tx and submit it to node 1
            var(txHex2, txId2) = CreateNewTransaction(coin, new Money(1000L));

            loggerTest.LogInformation($"Submiting {txId2} with doublespend");
            await Assert.ThrowsExceptionAsync <RpcException>(async() => await node0.RpcClient.SendRawTransactionAsync(HelperTools.HexStringToByteArray(txHex2), true, false));

            // Wait for a bit for node and Live mAPI to process all events
            await Task.Delay(2000);

            loggerTest.LogInformation("Retrieving notification data");
            var notifications = await TxRepositoryPostgres.GetNotificationsForTestsAsync();

            Assert.AreEqual(1, notifications.Length);
            Assert.AreEqual(txId2, new uint256(notifications.Single().DoubleSpendTxId).ToString());

            await StopMAPI();
        }
Esempio n. 4
0
 private async Task InsertTXAsync()
 {
     var txList = new List <Tx>()
     {
         CreateNewTx(txC0Hash, txC0Hex, false, null, true)
     };
     await TxRepositoryPostgres.InsertTxsAsync(txList, false);
 }
        private static async Task CheckCreateDbAndClearDbAsync(IStartupChecker startup, string dbConnectionString)
        {
            await startup.CheckAsync(true);

            // delete database before each test
            NodeRepositoryPostgres.EmptyRepository(dbConnectionString);
            TxRepositoryPostgres.EmptyRepository(dbConnectionString);
            FeeQuoteRepositoryPostgres.EmptyRepository(dbConnectionString);
        }
Esempio n. 6
0
        private async Task <long> CheckTxPresentInDbAsync(string txHash, bool blockPresent = true)
        {
            var txInternalId = await TxRepositoryPostgres.GetTransactionInternalId(new uint256(txHash).ToBytes());

            Assert.IsNotNull(txInternalId);
            var blocks = (await TxRepositoryPostgres.GetBlocksByTxIdAsync(txInternalId.Value));

            Assert.IsTrue(blockPresent ? blocks.Any() : !blocks.Any());
            return(txInternalId.Value);
        }
Esempio n. 7
0
        private async Task CheckTxNotPresentInDbAsync(string txHash, long txInternalId)
        {
            // tx and block are main tables - the other tables reference these two and have 'delete cascade constraint' declared
            var txDeletedInternalId = await TxRepositoryPostgres.GetTransactionInternalId(new uint256(txHash).ToBytes());

            Assert.IsNull(txDeletedInternalId);
            var block = (await TxRepositoryPostgres.GetBlocksByTxIdAsync(txInternalId)).FirstOrDefault();

            Assert.IsNull(block);
        }
        public async Task With2NodesOnlyOneDoubleSpendShouldBeSent()
        {
            using CancellationTokenSource cts = new(cancellationTimeout);

            // Create two transactions from same input
            var coin = availableCoins.Dequeue();

            var(txHex1, txId1) = CreateNewTransaction(coin, new Money(1000L));
            var(txHex2, txId2) = CreateNewTransaction(coin, new Money(500L));

            // Transactions should not be the same
            Assert.AreNotEqual(txHex1, txHex2);

            // Send first transaction using MAPI
            var payload = await SubmitTransactionAsync(txHex1, false, true);

            // start another node and connect the nodes
            // then wait for the new node to sync up before sending a DS tx
            var node1 = StartBitcoind(1, new BitcoindProcess[] { node0 });

            await SyncNodesBlocksAsync(cts.Token, node0, node1);

            Assert.AreEqual(1, await node1.RpcClient.GetConnectionCountAsync());

            await node1.RpcClient.DisconnectNodeAsync(node0.Host, node0.P2Port);

            do
            {
                await Task.Delay(100);
            } while ((await node1.RpcClient.GetConnectionCountAsync()) > 0);

            // Send second transaction
            _ = await node1.RpcClient.SendRawTransactionAsync(HelperTools.HexStringToByteArray(txHex2), true, false, cts.Token);

            await node1.RpcClient.GenerateAsync(1);

            await node1.RpcClient.AddNodeAsync(node0.Host, node0.P2Port);

            do
            {
                await Task.Delay(100);
            } while ((await node1.RpcClient.GetConnectionCountAsync()) == 0);

            // We are sleeping here for a second to make sure that after the nodes were reconnected
            // there wasn't any additional notification sent because of node1
            await Task.Delay(1000);

            var notifications = await TxRepositoryPostgres.GetNotificationsForTestsAsync();

            foreach (var notification in notifications)
            {
                loggerTest.LogInformation($"NotificationType: {notification.NotificationType}; TxId: {notification.TxInternalId}");
            }
            Assert.AreEqual(1, notifications.Length);
        }
Esempio n. 9
0
        public async Task MultipleInputsWithDS()
        {
            using CancellationTokenSource cts = new(30000);

            // startup another node and link it to the first node
            node1 = StartBitcoind(1, new BitcoindProcess[] { node0 });
            var syncTask = SyncNodesBlocksAsync(cts.Token, node0, node1);

            StartupLiveMAPI();
            var coin0 = availableCoins.Dequeue();
            var coin2 = availableCoins.Dequeue();

            var tx1    = CreateDS_OP_RETURN_Tx(new Coin[] { coin0, availableCoins.Dequeue(), coin2, availableCoins.Dequeue() }, 0, 2);
            var tx1Hex = tx1.ToHex();
            var tx1Id  = tx1.GetHash().ToString();

            var payload = await SubmitTransactions(new string[] { tx1Hex });

            loggerTest.LogInformation($"Submiting {tx1Id} with dsCheck enabled");
            var httpResponse = await PerformRequestAsync(Client, HttpMethod.Get, MapiServer.ApiDSQuery + "/" + tx1Id);

            // Wait for tx to be propagated to node 1 before submiting a doublespend tx to node 1
            await WaitForTxToBeAcceptedToMempool(node1, tx1Id, cts.Token);

            // Create double spend tx and submit it to node 1
            var(txHex2, txId2) = CreateNewTransaction(coin0, new Money(1000L));

            await syncTask;

            loggerTest.LogInformation($"Submiting {txId2} with doublespend");
            await Assert.ThrowsExceptionAsync <RpcException>(async() => await node1.RpcClient.SendRawTransactionAsync(HelperTools.HexStringToByteArray(txHex2), true, false));

            // Wait for a bit for node and Live mAPI to process all events
            await Task.Delay(3000);

            loggerTest.LogInformation("Retrieving notification data");
            var notifications = await TxRepositoryPostgres.GetNotificationsForTestsAsync();

            Assert.AreEqual(1, notifications.Length);
            Assert.AreEqual(txId2, new uint256(notifications.Single().DoubleSpendTxId).ToString());

            //Create another DS tx which should not trigger another notification
            var(txHex3, txId3) = CreateNewTransaction(coin2, new Money(5000L));

            loggerTest.LogInformation($"Submiting {txId3} with doublespend");
            await Assert.ThrowsExceptionAsync <RpcException>(async() => await node1.RpcClient.SendRawTransactionAsync(HelperTools.HexStringToByteArray(txHex3), true, false));

            await Task.Delay(3000);

            notifications = await TxRepositoryPostgres.GetNotificationsForTestsAsync();

            Assert.AreEqual(1, notifications.Length);
            await StopMAPI();
        }
        public virtual async Task TooLongForkCheck()
        {
            Assert.AreEqual(20, AppSettings.MaxBlockChainLengthForFork);
            _ = await CreateAndInsertTxAsync(false, true, 2);

            var node      = NodeRepository.GetNodes().First();
            var rpcClient = rpcClientFactoryMock.Create(node.Host, node.Port, node.Username, node.Password);

            long   blockCount;
            string blockHash;

            do
            {
                var tx = Transaction.Parse(Tx1Hex, Network.Main);
                (blockCount, blockHash) = await CreateAndPublishNewBlock(rpcClient, null, tx, true);
            }while (blockCount < 20);
            PublishBlockHashToEventBus(blockHash);

            uint256 forkBlockHeight8Hash = uint256.Zero;
            uint256 forkBlockHeight9Hash = uint256.Zero;
            var     nextBlock            = NBitcoin.Block.Load(await rpcClient.GetBlockByHeightAsBytesAsync(1), Network.Main);
            var     pubKey = new Key().PubKey;

            blockCount = 1;
            // Setup 2nd chain 30 blocks long that will not be downloaded completely (blockHeight=9 will be saved, blockheight=8 must not be saved)
            do
            {
                var tx            = Transaction.Parse(Tx2Hex, Network.Main);
                var prevBlockHash = nextBlock.GetHash();
                nextBlock = nextBlock.CreateNextBlockWithCoinbase(pubKey, new Money(50, MoneyUnit.MilliBTC), new ConsensusFactory());
                nextBlock.Header.HashPrevBlock = prevBlockHash;
                nextBlock.AddTransaction(tx);
                nextBlock.Check();
                rpcClientFactoryMock.AddKnownBlock(blockCount, nextBlock.ToBytes());

                if (blockCount == 9)
                {
                    forkBlockHeight9Hash = nextBlock.GetHash();
                }
                if (blockCount == 8)
                {
                    forkBlockHeight8Hash = nextBlock.GetHash();
                }
                blockCount++;
            }while (blockCount < 30);
            PublishBlockHashToEventBus(await rpcClient.GetBestBlockHashAsync());

            Assert.IsNotNull(await TxRepositoryPostgres.GetBlockAsync(forkBlockHeight9Hash.ToBytes()));
            Assert.IsNull(await TxRepositoryPostgres.GetBlockAsync(forkBlockHeight8Hash.ToBytes()));
        }
        public async Task DoubleSpendCheck()
        {
            _ = await CreateAndInsertTxAsync(false, true);

            (var doubleSpendTx, var tx2, _) = await InsertDoubleSpend();


            var dbRecords = (await TxRepositoryPostgres.GetTxsToSendBlockDSNotificationsAsync()).ToList();

            Assert.AreEqual(1, dbRecords.Count);
            Assert.AreEqual(doubleSpendTx.GetHash(), new uint256(dbRecords[0].DoubleSpendTxId));
            Assert.AreEqual(doubleSpendTx.Inputs.First().PrevOut.Hash, tx2.Inputs.First().PrevOut.Hash);
            Assert.AreEqual(doubleSpendTx.Inputs.First().PrevOut.N, tx2.Inputs.First().PrevOut.N);
            Assert.AreNotEqual(doubleSpendTx.GetHash(), tx2.GetHash());
        }
Esempio n. 12
0
        public async Task DoubleSpendMempoolCheckCleanUp()
        {
            //arrange
            cleanUpTxService.Pause();
            var cleanUpTxTriggeredSubscription = eventBus.Subscribe <CleanUpTxTriggeredEvent>();

            (List <Tx> txList, uint256 firstBlockHash) = await CreateAndInsertTxWithMempoolAsync(dsCheckMempool : true);

            WaitUntilEventBusIsIdle();

            var           doubleSpendTx = Transaction.Parse(Tx2Hex, Network.Main);
            List <byte[]> dsTxId        = new List <byte[]>
            {
                doubleSpendTx.GetHash().ToBytes()
            };
            var txsWithDSCheck = (await TxRepositoryPostgres.GetTxsForDSCheckAsync(dsTxId, true)).ToArray();

            var txPayload = HelperTools.HexStringToByteArray(tx2Hex);

            foreach (var dsTx in txsWithDSCheck)
            {
                await TxRepositoryPostgres.InsertMempoolDoubleSpendAsync(
                    dsTx.TxInternalId,
                    dsTx.TxExternalIdBytes,
                    txPayload);
            }
            WaitUntilEventBusIsIdle();
            var doubleSpends = (await TxRepositoryPostgres.GetTxsToSendMempoolDSNotificationsAsync()).ToList();

            Assert.AreEqual(1, doubleSpends.Count());

            foreach (var txDoubleSpend in doubleSpends)
            {
                await TxRepositoryPostgres.SetNotificationSendDateAsync(CallbackReason.DoubleSpendAttempt, txDoubleSpend.TxInternalId, -1, txDoubleSpend.DoubleSpendTxId, MockedClock.UtcNow);
            }

            doubleSpends = (await TxRepositoryPostgres.GetTxsToSendMempoolDSNotificationsAsync()).ToList();
            Assert.AreEqual(0, doubleSpends.Count());

            using (MockedClock.NowIs(DateTime.UtcNow.AddDays(cleanUpTxAfterDays)))
            {
                await ResumeAndWaitForCleanup(cleanUpTxTriggeredSubscription);

                // check if everything in db was cleared
                await CheckBlockNotPresentInDb(firstBlockHash);
                await CheckTxListNotPresentInDbAsync(txList);
            }
        }
        public async Task TestBigBlocks(double txsCount)
        {
            var node      = NodeRepository.GetNodes().First();
            var rpcClient = rpcClientFactoryMock.Create(node.Host, node.Port, node.Username, node.Password);

            var stream  = new MemoryStream(Encoders.Hex.DecodeData(File.ReadAllText(@"Data/16mb_tx.txt")));
            var bStream = new BitcoinStream(stream, false)
            {
                MaxArraySize = unchecked ((int)uint.MaxValue)
            };
            var tx = Transaction.Create(Network.Main);

            tx.ReadWrite(bStream);

            var txId = tx.GetHash(int.MaxValue).ToString();

            _ = await CreateAndInsertTxAsync(false, true, 2, new string[] { txId.ToString() });

            List <Transaction> txs = new();

            for (int i = 0; i < txsCount; i++)
            {
                txs.Add(tx);
            }

            (_, string blockHash) = await CreateAndPublishNewBlockWithTxs(rpcClient, null, txs.ToArray(), true, true);

            var block = await TxRepositoryPostgres.GetBestBlockAsync();

            Assert.IsFalse(HelperTools.AreByteArraysEqual(block.BlockHash, new uint256(blockHash).ToBytes()));

            PublishBlockHashToEventBus(blockHash);

            WaitUntilEventBusIsIdle();

            block = await TxRepositoryPostgres.GetBestBlockAsync();

            Assert.IsTrue(HelperTools.AreByteArraysEqual(block.BlockHash, new uint256(blockHash).ToBytes()));
            Assert.AreEqual(0, (await TxRepositoryPostgres.GetUnparsedBlocksAsync()).Length);

            // check if block was correctly parsed
            var blockStream = await RpcClient.GetBlockAsStreamAsync(await RpcClient.GetBestBlockHashAsync());

            var parsedBlock = HelperTools.ParseByteStreamToBlock(blockStream);

            Assert.AreEqual(txsCount + 1, parsedBlock.Transactions.Count);
        }
        public async Task SubmitAndCheckMultipleDSRead()
        {
            var address = BitcoinAddress.Create(testAddress, Network.RegTest);

            var tx1 = BCash.Instance.Regtest.CreateTransaction();

            tx1.Inputs.Add(new TxIn(new OutPoint(new uint256(txC1Hash), 0)));
            tx1.Inputs.Add(new TxIn(new OutPoint(new uint256(txC1Hash), 1)));
            tx1.Outputs.Add(new TxOut(new Money(1000L), address));

            var tx2 = BCash.Instance.Regtest.CreateTransaction();

            tx2.Inputs.Add(new TxIn(new OutPoint(new uint256(txC2Hash), 0)));
            tx2.Inputs.Add(new TxIn(new OutPoint(new uint256(txC2Hash), 1)));
            tx2.Outputs.Add(new TxOut(new Money(100L), address));


            var txList = new List <Tx>()
            {
                CreateNewTx(tx1.GetHash().ToString(), tx1.ToHex(), false, null, true),
                CreateNewTx(tx2.GetHash().ToString(), tx2.ToHex(), false, null, true)
            };
            await TxRepositoryPostgres.InsertTxsAsync(txList, false);

            var txs = await TxRepositoryPostgres.GetTxsForDSCheckAsync(new List <byte[]> {
                tx1.GetHash().ToBytes(), tx2.GetHash().ToBytes()
            }, true);

            Assert.AreEqual(2, txs.Count());
            Assert.AreEqual(2, txs.First().TxIn.Count);
            Assert.AreEqual(2, txs.Last().TxIn.Count);

            var readTx1 = txs.SingleOrDefault(x => x.TxIn.Any(y => new uint256(y.PrevTxId) == new uint256(txC1Hash)));

            Assert.IsNotNull(readTx1);
            Assert.AreEqual(0, readTx1.OrderderInputs.First().N);
            Assert.AreEqual(1, readTx1.OrderderInputs.Last().N);

            var readTx2 = txs.SingleOrDefault(x => x.TxIn.Any(y => new uint256(y.PrevTxId) == new uint256(txC2Hash)));

            Assert.IsNotNull(readTx2);
            Assert.AreEqual(0, readTx2.OrderderInputs.First().N);
            Assert.AreEqual(1, readTx2.OrderderInputs.Last().N);
        }
        protected async Task <List <Tx> > CreateAndInsertTxAsync(bool merkleProof, bool dsCheck, int?limit = null)
        {
            string[] hashes = new string[] { Tx1Hash, Tx2Hash, Tx3Hash, Tx4Hash, Tx5Hash };
            string[] hexes  = new string[] { Tx1Hex, Tx2Hex, Tx3Hex, Tx4Hex, Tx5Hex };
            if (limit == null)
            {
                limit = hashes.Length;
            }
            List <Tx> txList = new List <Tx>();

            for (int i = 0; i < limit; i++)
            {
                txList.Add(CreateNewTx(hashes[i], hexes[i], merkleProof, dsCheck));
            }

            await TxRepositoryPostgres.InsertTxsAsync(txList);

            return(txList);
        }
        public virtual async Task TestSkipParsing()
        {
            var node      = NodeRepository.GetNodes().First();
            var rpcClient = (Mock.RpcClientMock)rpcClientFactoryMock.Create(node.Host, node.Port, node.Username, node.Password);

            long blockCount = await RpcClient.GetBlockCountAsync();

            var blockStream = await RpcClient.GetBlockAsStreamAsync(await RpcClient.GetBestBlockHashAsync());

            var firstBlock = HelperTools.ParseByteStreamToBlock(blockStream);

            rpcClientFactoryMock.AddKnownBlock(blockCount++, firstBlock.ToBytes());

            var tx = Transaction.Parse(Tx1Hex, Network.Main);

            await CreateAndPublishNewBlock(rpcClient, null, tx, true);

            Assert.AreEqual(0, (await TxRepositoryPostgres.GetUnparsedBlocksAsync()).Length);

            var block = await TxRepositoryPostgres.GetBestBlockAsync();

            // we publish same NewBlockAvailableInDB as before
            var block2Parse = block;

            EventBus.Publish(new NewBlockAvailableInDB
            {
                BlockDBInternalId = block2Parse.BlockInternalId,
                BlockHash         = new uint256(block2Parse.BlockHash).ToString()
            });

            WaitUntilEventBusIsIdle();

            // best block must stay the same, since parsing was skipped
            var blockAfterRepublish = await TxRepositoryPostgres.GetBestBlockAsync();

            Assert.AreEqual(block.BlockInternalId, blockAfterRepublish.BlockInternalId);
            Assert.AreEqual(block.ParsedForMerkleAt, blockAfterRepublish.ParsedForMerkleAt);
            Assert.AreEqual(block.ParsedForDSAt, blockAfterRepublish.ParsedForDSAt);
        }
Esempio n. 17
0
        public async Task MerkleProofCheckCleanUp()
        {
            //arrange
            cleanUpTxService.Pause();
            var cleanUpTxTriggeredSubscription = eventBus.Subscribe <CleanUpTxTriggeredEvent>();

            List <Tx> txList = await CreateAndInsertTxAsync(true, false);

            uint256 firstBlockHash = await InsertMerkleProof();

            WaitUntilEventBusIsIdle();

            await CheckTxListPresentInDbAsync(txList, true);
            await CheckBlockPresentInDbAsync(firstBlockHash);

            var merkleProofTxs = (await TxRepositoryPostgres.GetTxsToSendMerkleProofNotificationsAsync(0, 10000)).ToList();

            Assert.AreEqual(5, merkleProofTxs.Count());
            Assert.IsTrue(merkleProofTxs.Any(x => new uint256(x.TxExternalId) == new uint256(Tx2Hash)));

            foreach (var txWithMerkle in merkleProofTxs)
            {
                await TxRepositoryPostgres.SetNotificationSendDateAsync(CallbackReason.MerkleProof, txWithMerkle.TxInternalId, txWithMerkle.BlockInternalId, null, MockedClock.UtcNow);
            }

            merkleProofTxs = (await TxRepositoryPostgres.GetTxsToSendMerkleProofNotificationsAsync(0, 10000)).ToList();
            Assert.AreEqual(0, merkleProofTxs.Count());

            using (MockedClock.NowIs(DateTime.UtcNow.AddDays(cleanUpTxAfterDays)))
            {
                await ResumeAndWaitForCleanup(cleanUpTxTriggeredSubscription);

                // check if everything in db was cleared
                await CheckBlockNotPresentInDb(firstBlockHash);
                await CheckTxListNotPresentInDbAsync(txList);
            }
        }
Esempio n. 18
0
        private async Task CheckBlockNotPresentInDb(uint256 blockHash)
        {
            var firstBlockTest = await TxRepositoryPostgres.GetBlockAsync(blockHash.ToBytes());

            Assert.IsNull(firstBlockTest);
        }
Esempio n. 19
0
        public async Task MultipleDSQueriesReturn1Notification()
        {
            using CancellationTokenSource cts = new(30000);

            StartupLiveMAPI();

            int noOfNodes = 4;
            List <BitcoindProcess> nodeList = new()
            {
                node0
            };

            for (int i = 1; i <= noOfNodes; i++)
            {
                nodeList.Add(StartBitcoind(i, nodeList.ToArray()));
            }

            await SyncNodesBlocksAsync(cts.Token, nodeList.ToArray());

            var coin = availableCoins.Dequeue();

            var tx1    = CreateDS_OP_RETURN_Tx(new Coin[] { coin }, 00);
            var tx1Hex = tx1.ToHex();
            var tx1Id  = tx1.GetHash().ToString();

            loggerTest.LogInformation($"Submiting {tx1Id} with doublespend notification enabled");
            var payload = await SubmitTransactions(new string[] { tx1Hex });

            // Wait for tx to be propagated to all nodes before submiting a doublespend tx to nodes
            List <Task> mempoolTasks = new();

            for (int i = 1; i <= noOfNodes; i++)
            {
                mempoolTasks.Add(WaitForTxToBeAcceptedToMempool(nodeList[i], tx1Id, cts.Token));
            }
            await Task.WhenAll(mempoolTasks);


            // Create double spend tx and submit it to all nodes except the one connected to mAPI
            var(txHex2, txId2) = CreateNewTransaction(coin, new Money(1000L));

            loggerTest.LogInformation($"Submiting {txId2} with doublespend to all running nodes at once");
            List <Task <RpcException> > taskList = new();

            for (int i = 1; i <= noOfNodes; i++)
            {
                taskList.Add(Assert.ThrowsExceptionAsync <RpcException>(async() => await nodeList[i].RpcClient.SendRawTransactionAsync(HelperTools.HexStringToByteArray(txHex2), true, false)));
            }

            await Task.WhenAll(taskList);

            // Need to wait for all nodes to do their calls to mAPI
            await Task.Delay(2000);

            loggerTest.LogInformation("Retrieving notification data");
            var notifications = await TxRepositoryPostgres.GetNotificationsForTestsAsync();

            Assert.AreEqual(1, notifications.Length);
            Assert.AreEqual(txId2, new uint256(notifications.Single().DoubleSpendTxId).ToString());

            await StopMAPI();
        }
Esempio n. 20
0
        private async Task CheckBlockPresentInDbAsync(uint256 blockHash)
        {
            var firstBlockTest = await TxRepositoryPostgres.GetBlockAsync(blockHash.ToBytes()); // this block is present in db without linked tx

            Assert.IsNotNull(firstBlockTest);
        }
        public virtual async Task DoubleMerkleProofCheck()
        {
            _ = await CreateAndInsertTxAsync(true, true, 2);

            var node      = NodeRepository.GetNodes().First();
            var rpcClient = rpcClientFactoryMock.Create(node.Host, node.Port, node.Username, node.Password);

            var(blockCount, _) = await CreateAndPublishNewBlock(rpcClient, null, null);

            NBitcoin.Block forkBlock = null;
            var            nextBlock = NBitcoin.Block.Load(await rpcClient.GetBlockByHeightAsBytesAsync(0), Network.Main);
            var            pubKey    = nextBlock.Transactions.First().Outputs.First().ScriptPubKey.GetDestinationPublicKeys().First();
            // tx1 will be mined in block 1 and the notification has to be sent only once (1 insert into txBlock)
            var tx1 = Transaction.Parse(Tx1Hex, Network.Main);
            // tx2 will be mined in block 15 in chain 1 and in the block 20 in longer chain 2 and the notification
            // has to be sent twice (2 inserts into txBlock)
            var tx2 = Transaction.Parse(Tx2Hex, Network.Main);

            // Setup first chain, 20 blocks long
            do
            {
                var prevBlockHash = nextBlock.GetHash();
                nextBlock = nextBlock.CreateNextBlockWithCoinbase(pubKey, new Money(50, MoneyUnit.MilliBTC), new ConsensusFactory());
                nextBlock.Header.HashPrevBlock = prevBlockHash;
                if (blockCount == 1)
                {
                    nextBlock.AddTransaction(tx1);
                }
                if (blockCount == 9)
                {
                    forkBlock = nextBlock;
                }
                if (blockCount == 15)
                {
                    nextBlock.AddTransaction(tx2);
                }
                nextBlock.Check();
                rpcClientFactoryMock.AddKnownBlock(blockCount++, nextBlock.ToBytes());
            }while (blockCount < 20);
            PublishBlockHashToEventBus(await rpcClient.GetBestBlockHashAsync());

            nextBlock  = forkBlock;
            blockCount = 10;
            // Setup second chain
            do
            {
                var prevBlockHash = nextBlock.GetHash();
                nextBlock = nextBlock.CreateNextBlockWithCoinbase(pubKey, new Money(50, MoneyUnit.MilliBTC), new ConsensusFactory());
                nextBlock.Header.HashPrevBlock = prevBlockHash;
                if (blockCount == 20)
                {
                    nextBlock.AddTransaction(tx2);
                }
                nextBlock.Check();
                rpcClientFactoryMock.AddKnownBlock(blockCount++, nextBlock.ToBytes());
            }while (blockCount < 21);
            PublishBlockHashToEventBus(await rpcClient.GetBestBlockHashAsync());

            var merkleProofs = (await TxRepositoryPostgres.GetTxsToSendMerkleProofNotificationsAsync(0, 100)).ToArray();

            Assert.AreEqual(3, merkleProofs.Length);
            Assert.IsTrue(merkleProofs.Count(x => new uint256(x.TxExternalId).ToString() == Tx1Hash) == 1);
            // Tx2 must have 2 requests for merkle proof notification (blocks 15 and 20)
            Assert.IsTrue(merkleProofs.Count(x => new uint256(x.TxExternalId).ToString() == Tx2Hash) == 2);
            Assert.IsTrue(merkleProofs.Any(x => new uint256(x.TxExternalId).ToString() == Tx2Hash && x.BlockHeight == 15));
            Assert.IsTrue(merkleProofs.Any(x => new uint256(x.TxExternalId).ToString() == Tx2Hash && x.BlockHeight == 20));
        }
Esempio n. 22
0
 protected override void CleanRepositories(string dbConnectionString)
 {
     NodeRepositoryPostgres.EmptyRepository(DBConnectionStringDDL ?? dbConnectionString);
     TxRepositoryPostgres.EmptyRepository(DBConnectionStringDDL ?? dbConnectionString);
     FeeQuoteRepositoryPostgres.EmptyRepository(DBConnectionStringDDL ?? dbConnectionString);
 }