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); }
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(); }
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); }
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); }
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); }
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()); }
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); }
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); } }
private async Task CheckBlockNotPresentInDb(uint256 blockHash) { var firstBlockTest = await TxRepositoryPostgres.GetBlockAsync(blockHash.ToBytes()); Assert.IsNull(firstBlockTest); }
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(); }
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)); }
protected override void CleanRepositories(string dbConnectionString) { NodeRepositoryPostgres.EmptyRepository(DBConnectionStringDDL ?? dbConnectionString); TxRepositoryPostgres.EmptyRepository(DBConnectionStringDDL ?? dbConnectionString); FeeQuoteRepositoryPostgres.EmptyRepository(DBConnectionStringDDL ?? dbConnectionString); }