public async Task ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_DisconnectsAsync() { using (var builder = NodeBuilder.Create(this)) { var minerA = builder.CreateStratisPowNode(new BitcoinRegTest(), "cmi-1-minerA").WithDummyWallet(); var minerB = builder.CreateStratisPowNode(new BitcoinRegTestNoValidationRules(), "cmi-1-minerB").NoValidation().WithDummyWallet(); var syncer = builder.CreateStratisPowNode(new BitcoinRegTest(), "cmi-1-syncer").WithDummyWallet(); bool minerADisconnectedFromSyncer = false; // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound). void interceptor(ChainedHeaderBlock chainedHeaderBlock) { if (minerADisconnectedFromSyncer) { return; } if (chainedHeaderBlock.ChainedHeader.Previous.Height == 10) { // Ensure that minerA's tip has rewound to 10. TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 10)); TestHelper.Disconnect(minerA, syncer); minerADisconnectedFromSyncer = true; return; } } // Start minerA and mine 10 blocks. We cannot use a premade chain as it adversely affects the max tip age calculation, causing sporadic sync errors. minerA.Start(); TestHelper.MineBlocks(minerA, 10); TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 10); // Start the nodes. minerB.Start(); syncer.Start(); minerA.SetDisconnectInterceptor(interceptor); // minerB and syncer syncs with minerA. TestHelper.ConnectAndSync(minerA, minerB, syncer); // Disconnect minerB from miner so that it can mine on its own and create a fork. TestHelper.Disconnect(minerA, minerB); // MinerA continues to mine to height 14. TestHelper.MineBlocks(minerA, 4); TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14); TestBase.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 10); TestBase.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 14); // minerB mines 5 more blocks: // Block 6,7,9,10 = valid // Block 8 = invalid Assert.False(TestHelper.IsNodeConnected(minerB)); await TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(13, (node, block) => BlockBuilder.InvalidCoinbaseReward(node, block)).BuildAsync(); // Reconnect minerA to minerB. TestHelper.ConnectNoCheck(minerA, minerB); // minerB should be disconnected from minerA. TestBase.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, minerB)); // syncer should be disconnected from minerA (via interceptor). TestBase.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, syncer)); // The reorg will fail at block 8 and roll back any changes. TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 14)); TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 15)); TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(syncer, 14)); } }
public void SendAndReceiveSmartContractTransactionsUsingController() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractNode(); CoreNode scReceiver = builder.CreateSmartContractNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); scSender.FullNode.WalletManager().CreateWallet(Password, WalletName); scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName); HdAddress addr = scSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, AccountName)); Features.Wallet.Wallet wallet = scSender.FullNode.WalletManager().GetWalletByName(WalletName); Key key = wallet.GetExtendedPrivateKeyForAddress(Password, addr).PrivateKey; scSender.SetDummyMinerSecret(new BitcoinSecret(key, scSender.FullNode.Network)); scReceiver.SetDummyMinerSecret(new BitcoinSecret(key, scReceiver.FullNode.Network)); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; scSender.GenerateStratisWithMiner(maturity + 5); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * (maturity + 5) * 50, total); SmartContractsController senderSmartContractsController = scSender.FullNode.NodeService <SmartContractsController>(); SmartContractWalletController senderWalletController = scSender.FullNode.NodeService <SmartContractWalletController>(); SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/StorageDemo.cs"); Assert.True(compilationResult.Success); var buildRequest = new BuildCreateContractTransactionRequest { AccountName = AccountName, GasLimit = "10000", GasPrice = "1", ContractCode = compilationResult.Compilation.ToHexString(), FeeAmount = "0.001", Password = Password, WalletName = WalletName, Sender = addr.Address }; JsonResult result = (JsonResult)senderSmartContractsController.BuildCreateSmartContractTransaction(buildRequest); var response = (BuildCreateContractTransactionResponse)result.Value; scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); SmartContractSharedSteps.SendTransactionAndMine(scSender, scReceiver, senderWalletController, response.Hex); var receiptStorage = scReceiver.FullNode.NodeService <ISmartContractReceiptStorage>(); Assert.NotNull(receiptStorage.GetReceipt(response.TransactionId)); // Check wallet history is updating correctly result = (JsonResult)senderWalletController.GetHistory(new WalletHistoryRequest { AccountName = AccountName, WalletName = WalletName }); var walletHistoryModel = (WalletHistoryModel)result.Value; Assert.Single(walletHistoryModel.AccountsHistoryModel.First().TransactionsHistory.Where(x => x.Type == TransactionItemType.Send)); string storageRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "TestSave", DataType = SmartContractDataType.String })).Value; Assert.Equal("Hello, smart contract world!", storageRequestResult); string ownerRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Owner", DataType = SmartContractDataType.Address })).Value; Assert.NotEmpty(ownerRequestResult); string counterRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Counter", DataType = SmartContractDataType.Int })).Value; Assert.Equal("12345", counterRequestResult); var callRequest = new BuildCallContractTransactionRequest { AccountName = AccountName, GasLimit = "10000", GasPrice = "1", Amount = "0", MethodName = "Increment", ContractAddress = response.NewContractAddress, FeeAmount = "0.001", Password = Password, WalletName = WalletName, Sender = addr.Address }; result = (JsonResult)senderSmartContractsController.BuildCallSmartContractTransaction(callRequest); var callResponse = (BuildCallContractTransactionResponse)result.Value; SmartContractSharedSteps.SendTransactionAndMine(scSender, scReceiver, senderWalletController, callResponse.Hex); counterRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Counter", DataType = SmartContractDataType.Int })).Value; Assert.Equal("12346", counterRequestResult); // Check wallet history again result = (JsonResult)senderWalletController.GetHistory(new WalletHistoryRequest { AccountName = AccountName, WalletName = WalletName }); walletHistoryModel = (WalletHistoryModel)result.Value; Assert.Equal(2, walletHistoryModel.AccountsHistoryModel.First().TransactionsHistory.Where(x => x.Type == TransactionItemType.Send).Count()); // Check receipts var receiptResponse = (JsonResult)senderSmartContractsController.GetReceipt(callResponse.TransactionId.ToString()); var receiptModel = (ReceiptModel)receiptResponse.Value; Assert.True(receiptModel.Successful); } }
public void Staking_Wallet_Can_Mint_New_Coins() { using (var builder = NodeBuilder.Create(this)) { var configParameters = new NodeConfigParameters { { "savetrxhex", "true" } }; var network = new StratisRegTest(); var minerA = builder.CreateStratisPosNode(network, "stake-1-minerA", configParameters: configParameters).OverrideDateTimeProvider().WithWallet().Start(); var addressUsed = TestHelper.MineBlocks(minerA, (int)network.Consensus.PremineHeight).AddressUsed; var wallet = minerA.FullNode.WalletManager().Wallets.Single(w => w.Name == "mywallet"); var allTrx = wallet.walletStore.GetForAddress(addressUsed.Address); // Since the pre-mine will not be immediately spendable, the transactions have to be counted directly from the address. allTrx.Count().Should().Be((int)network.Consensus.PremineHeight); allTrx.Sum(s => s.Amount).Should().Be(network.Consensus.PremineReward + network.Consensus.ProofOfWorkReward); var balance = minerA.FullNode.WalletManager().GetAddressBalance(addressUsed.Address); balance.AmountConfirmed.Should().Be(network.Consensus.PremineReward + network.Consensus.ProofOfWorkReward); // Mine blocks to maturity TestHelper.MineBlocks(minerA, (int)network.Consensus.CoinbaseMaturity + 10); // Get set of transaction IDs present in wallet before staking is started. this.transactionsBeforeStaking.Clear(); foreach (TransactionOutputData transactionData in this.GetTransactionsSnapshot(minerA)) { this.transactionsBeforeStaking.Add(transactionData.Id); } // Start staking on the node. var minter = minerA.FullNode.NodeService <IPosMinting>(); minter.Stake(new WalletSecret() { WalletName = "mywallet", WalletPassword = "******" }); // If new transactions are appearing in the wallet, staking has been successful. Due to coin maturity settings the // spendable balance of the wallet actually drops after staking, so the wallet balance should not be used to // determine whether staking occurred. TestBase.WaitLoop(() => { List <TransactionOutputData> transactions = this.GetTransactionsSnapshot(minerA); foreach (TransactionOutputData transactionData in transactions) { if (!this.transactionsBeforeStaking.Contains(transactionData.Id) && (transactionData.IsCoinStake ?? false)) { return(true); } } return(false); }); // build a dictionary of coinstake tx's indexed by tx id. foreach (var tx in this.GetTransactionsSnapshot(minerA)) { this.transactionLookup[tx.Id] = tx; } TestBase.WaitLoop(() => { List <TransactionOutputData> transactions = this.GetTransactionsSnapshot(minerA); foreach (TransactionOutputData transactionData in transactions) { if (!this.transactionsBeforeStaking.Contains(transactionData.Id) && (transactionData.IsCoinStake ?? false)) { Transaction coinstakeTransaction = minerA.FullNode.Network.CreateTransaction(transactionData.Hex); var balance = new Money(0); // Add coinstake outputs to balance. foreach (TxOut output in coinstakeTransaction.Outputs) { balance += output.Value; } // Subtract coinstake inputs from balance. foreach (TxIn input in coinstakeTransaction.Inputs) { this.transactionLookup.TryGetValue(input.PrevOut.Hash, out TransactionOutputData prevTransactionData); if (prevTransactionData == null) { continue; } Transaction prevTransaction = minerA.FullNode.Network.CreateTransaction(prevTransactionData.Hex); balance -= prevTransaction.Outputs[input.PrevOut.N].Value; } Assert.Equal(minerA.FullNode.Network.Consensus.ProofOfStakeReward, balance); return(true); } } return(false); }); } }
public void XMinesTransaction_SBFNSyncs() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisOverrideRegTest(); CoreNode stratisXNode = builder.CreateStratisXNode(version: "2.0.0.5").Start(); var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC()); var config = new NodeConfigParameters(); config.Add("whitelist", stratisXNode.Endpoint.ToString()); config.Add("gateway", "1"); CoreNode stratisNode = builder .CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, minProtocolVersion: ProtocolVersion.POS_PROTOCOL_VERSION, configParameters: config) .WithWallet().Start(); RPCClient stratisXRpc = stratisXNode.CreateRPCClient(); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); stratisXRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(stratisXNode.Endpoint, false); stratisXRpc.SendCommand(RPCOperations.generate, 11); var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token; // Without this there seems to be a race condition between the blocks all getting generated and SBFN syncing high enough to fall through the getbestblockhash check. TestBase.WaitLoop(() => stratisXRpc.GetBlockCount() >= 11, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Send transaction to arbitrary address from X side. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); stratisXRpc.SendCommand(RPCOperations.sendtoaddress, aliceAddress.ToString(), 1); TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Transaction should percolate through to SBFN's mempool. TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Now X must mine the block. stratisXRpc.SendCommand(RPCOperations.generate, 1); TestBase.WaitLoop(() => stratisXRpc.GetBlockCount() >= 12, cancellationToken: shortCancellationToken); // We expect that SBFN will sync correctly. TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Sanity check - mempools should both become empty. TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); } }
public void SendAndReceiveSmartContractTransactionsOnPosNetwork() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPosNode().NotInIBD(); CoreNode scReceiver = builder.CreateSmartContractPosNode().NotInIBD(); builder.StartAll(); scSender.WithWallet(Password, WalletName, Passphrase); scReceiver.WithWallet(Password, WalletName, Passphrase); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; HdAddress senderAddress = TestHelper.MineBlocks(scSender, maturity + 5, WalletName, Password, AccountName).AddressUsed; // The mining should add coins to the wallet. var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 6 * 50, total); // Create a token contract ulong gasPrice = 1; int vmVersion = 1; Gas gasLimit = (Gas)5000; ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/TransferTestPos.cs"); Assert.True(compilationResult.Success); var contractTxData = new ContractTxData(vmVersion, gasPrice, gasLimit, compilationResult.Compilation); var contractCreateScript = new Script(this.callDataSerializer.Serialize(contractTxData)); var txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), ChangeAddress = senderAddress, MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; // Build the transfer contract transaction var transferContractTransaction = BuildTransferContractTransaction(scSender, txBuildContext); // Add the smart contract transaction to the mempool to be mined. scSender.AddToStratisMempool(transferContractTransaction); // Ensure the smart contract transaction is in the mempool. TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); // Mine the token transaction and wait for it sync TestHelper.MineBlocks(scSender, 1); // Sync to the receiver node scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that boths nodes has the contract IStateRepositoryRoot senderState = scSender.FullNode.NodeService <IStateRepositoryRoot>(); IStateRepositoryRoot receiverState = scReceiver.FullNode.NodeService <IStateRepositoryRoot>(); IAddressGenerator addressGenerator = scSender.FullNode.NodeService <IAddressGenerator>(); uint160 tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a transfer token contract compilationResult = ContractCompiler.CompileFile("SmartContracts/TransferTestPos.cs"); Assert.True(compilationResult.Success); contractTxData = new ContractTxData(vmVersion, gasPrice, gasLimit, compilationResult.Compilation); contractCreateScript = new Script(this.callDataSerializer.Serialize(contractTxData)); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), ChangeAddress = senderAddress, MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; // Build the transfer contract transaction transferContractTransaction = BuildTransferContractTransaction(scSender, txBuildContext); // Add the smart contract transaction to the mempool to be mined. scSender.AddToStratisMempool(transferContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); TestHelper.MineBlocks(scSender, 1); // Ensure both nodes are synced with each other TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); // nonce is 0 for user contract creation. Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a call contract transaction which will transfer funds contractTxData = new ContractTxData(1, gasPrice, gasLimit, tokenContractAddress, "Test"); Script contractCallScript = new Script(this.callDataSerializer.Serialize(contractTxData)); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), ChangeAddress = senderAddress, MinConfirmations = maturity, FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 1000, ScriptPubKey = contractCallScript } }.ToList() }; // Build the transfer contract transaction var callContractTransaction = BuildTransferContractTransaction(scSender, txBuildContext); // Add the smart contract transaction to the mempool to be mined. scSender.AddToStratisMempool(callContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); TestHelper.MineBlocks(scSender, 1); // Ensure the nodes are synced TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // The balance should now reflect the transfer Assert.Equal((ulong)900, senderState.GetCurrentBalance(tokenContractAddress)); } }
public async Task GetTransactionOnUnconfirmedTransactionAsync() { using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. // Create a sending and a receiving node. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start(); TestHelper.ConnectAndSync(sendingNode, receivingNode); // Get an address to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with an Op_Return. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeType = "low", Password = "******", ShuffleOutputs = false, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = "1" }).ToList(), }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); uint256 txId = buildTransactionModel.TransactionId; TestBase.WaitLoop(() => { WalletHistoryModel history = $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/history") .SetQueryParams(new { walletName = "mywallet", AccountName = "account 0" }) .GetAsync() .ReceiveJson <WalletHistoryModel>().GetAwaiter().GetResult(); return(history.AccountsHistoryModel.First().TransactionsHistory.Any(h => h.Id == txId)); }); TestBase.WaitLoop(() => { WalletHistoryModel history = $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/history") .SetQueryParams(new { walletName = "mywallet", AccountName = "account 0" }) .GetAsync() .ReceiveJson <WalletHistoryModel>().GetAwaiter().GetResult(); return(history.AccountsHistoryModel.First().TransactionsHistory.Any(h => h.Id == txId)); }); Transaction trx = this.network.Consensus.ConsensusFactory.CreateTransaction(buildTransactionModel.Hex); RPCClient rpcReceivingNode = receivingNode.CreateRPCClient(); RPCResponse txReceivingWallet = rpcReceivingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); RPCClient rpcSendingNode = sendingNode.CreateRPCClient(); RPCResponse txSendingWallet = rpcSendingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); // Assert. GetTransactionModel resultSendingWallet = txSendingWallet.Result.ToObject <GetTransactionModel>(); resultSendingWallet.Amount.Should().Be((decimal) - 1.00000000); resultSendingWallet.Fee.Should().Be((decimal) - 0.0001); resultSendingWallet.Confirmations.Should().Be(0); resultSendingWallet.TransactionId.Should().Be(txId); resultSendingWallet.BlockHash.Should().BeNull(); resultSendingWallet.BlockIndex.Should().BeNull(); resultSendingWallet.BlockTime.Should().BeNull(); resultSendingWallet.TimeReceived.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultSendingWallet.Details.Count.Should().Be(1); GetTransactionDetailsModel detailsSendingWallet = resultSendingWallet.Details.Single(); detailsSendingWallet.Address.Should().Be(unusedaddresses.Single()); detailsSendingWallet.Amount.Should().Be((decimal) - 1.00000000); detailsSendingWallet.Fee.Should().Be((decimal) - 0.0001); detailsSendingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Send); detailsSendingWallet.OutputIndex.Should().Be(1); // The output at index 0 is the change. GetTransactionModel resultReceivingWallet = txReceivingWallet.Result.ToObject <GetTransactionModel>(); resultReceivingWallet.Amount.Should().Be((decimal)1.00000000); resultReceivingWallet.Fee.Should().BeNull(); resultReceivingWallet.Confirmations.Should().Be(0); resultReceivingWallet.TransactionId.Should().Be(txId); resultReceivingWallet.BlockHash.Should().BeNull(); resultReceivingWallet.BlockIndex.Should().BeNull(); resultReceivingWallet.BlockTime.Should().BeNull(); resultReceivingWallet.TimeReceived.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultReceivingWallet.TransactionTime.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultReceivingWallet.Details.Should().ContainSingle(); GetTransactionDetailsModel detailsReceivingWallet = resultReceivingWallet.Details.Single(); detailsReceivingWallet.Address.Should().Be(unusedaddresses.Single()); detailsReceivingWallet.Amount.Should().Be((decimal)1.00000000); detailsReceivingWallet.Fee.Should().BeNull(); detailsReceivingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Receive); detailsReceivingWallet.OutputIndex.Should().Be(1); } }
public void SBFNMinesTransaction_XSyncs() { // TODO: Currently fails due to issue #2468 (coinbase // reward on stratisX cannot be >4. No fees are allowed) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisOverrideRegTest(); CoreNode stratisXNode = builder.CreateStratisXNode(version: "2.0.0.5").Start(); // We do not want the datetime provider to be substituted, // so a custom builder callback has to be used. var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC() .UseTestChainedHeaderTree() .MockIBD()); CoreNode stratisNode = builder.CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.POS_PROTOCOL_VERSION).WithWallet().Start(); RPCClient stratisXRpc = stratisXNode.CreateRPCClient(); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); stratisXRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(stratisXNode.Endpoint, false); TestHelper.MineBlocks(stratisNode, 11); // It takes a reasonable amount of time for blocks to be generated without // the datetime provider substitution. var longCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(15)).Token; var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: longCancellationToken); // Send transaction to arbitrary address from SBFN side. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); stratisNodeRpc.WalletPassphrase("password", 60); stratisNodeRpc.SendToAddress(aliceAddress, Money.Coins(1.0m)); TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Transaction should percolate through to X's mempool. TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Now SBFN must mine the block. TestHelper.MineBlocks(stratisNode, 1); // We expect that X will sync correctly. TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Sanity check - mempools should both become empty. TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); } }
public void SendAndReceiveSmartContractTransactionsUsingController() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPowNode(); CoreNode scReceiver = builder.CreateSmartContractPowNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); int maturity = (int)scReceiver.FullNode.Network.Consensus.CoinbaseMaturity; scSender.FullNode.WalletManager().CreateWallet(Password, WalletName, Passphrase); scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName, Passphrase); HdAddress addr = TestHelper.MineBlocks(scSender, maturity + 5, WalletName, Password, AccountName).AddressUsed; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); int spendable = GetSpendableBlocks(maturity + 5, maturity); var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * spendable * 50, total); SmartContractsController senderSmartContractsController = scSender.FullNode.NodeService <SmartContractsController>(); SmartContractWalletController senderWalletController = scSender.FullNode.NodeService <SmartContractWalletController>(); ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/StorageDemo.cs"); Assert.True(compilationResult.Success); var buildRequest = new BuildCreateContractTransactionRequest { AccountName = AccountName, GasLimit = "10000", GasPrice = "1", ContractCode = compilationResult.Compilation.ToHexString(), FeeAmount = "0.001", Password = Password, WalletName = WalletName, Sender = addr.Address }; JsonResult result = (JsonResult)senderSmartContractsController.BuildCreateSmartContractTransaction(buildRequest); var response = (BuildCreateContractTransactionResponse)result.Value; scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); SmartContractSharedSteps.SendTransaction(scSender, scReceiver, senderWalletController, response.Hex); TestHelper.MineBlocks(scReceiver, 2, WalletName, Password, AccountName); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Check wallet history is updating correctly. result = (JsonResult)senderWalletController.GetHistory(new WalletHistoryRequest { AccountName = AccountName, WalletName = WalletName }); var walletHistoryModel = (WalletHistoryModel)result.Value; Assert.Single(walletHistoryModel.AccountsHistoryModel.First().TransactionsHistory.Where(x => x.Type == TransactionItemType.Send)); // Check receipt was stored and can be retrieved. var receiptResponse = (ReceiptResponse)((JsonResult)senderSmartContractsController.GetReceipt(response.TransactionId.ToString())).Value; Assert.True(receiptResponse.Success); Assert.Equal(response.NewContractAddress, receiptResponse.NewContractAddress); Assert.Null(receiptResponse.To); Assert.Equal(addr.Address, receiptResponse.From); string storageRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "TestSave", DataType = SmartContractDataType.String })).Value; Assert.Equal("Hello, smart contract world!", storageRequestResult); string ownerRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Owner", DataType = SmartContractDataType.Address })).Value; Assert.NotEmpty(ownerRequestResult); string counterRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Counter", DataType = SmartContractDataType.Int })).Value; Assert.Equal("12345", counterRequestResult); var callRequest = new BuildCallContractTransactionRequest { AccountName = AccountName, GasLimit = "10000", GasPrice = "1", Amount = "0", MethodName = "Increment", ContractAddress = response.NewContractAddress, FeeAmount = "0.001", Password = Password, WalletName = WalletName, Sender = addr.Address }; result = (JsonResult)senderSmartContractsController.BuildCallSmartContractTransaction(callRequest); var callResponse = (BuildCallContractTransactionResponse)result.Value; SmartContractSharedSteps.SendTransaction(scSender, scReceiver, senderWalletController, callResponse.Hex); TestHelper.MineBlocks(scReceiver, 2, WalletName, Password, AccountName); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); counterRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Counter", DataType = SmartContractDataType.Int })).Value; Assert.Equal("12346", counterRequestResult); // Check receipt was stored and can be retrieved. receiptResponse = (ReceiptResponse)((JsonResult)senderSmartContractsController.GetReceipt(callResponse.TransactionId.ToString())).Value; Assert.True(receiptResponse.Success); Assert.Null(receiptResponse.NewContractAddress); Assert.Equal(response.NewContractAddress, receiptResponse.To); Assert.Equal(addr.Address, receiptResponse.From); // Check wallet history again result = (JsonResult)senderWalletController.GetHistory(new WalletHistoryRequest { AccountName = AccountName, WalletName = WalletName }); walletHistoryModel = (WalletHistoryModel)result.Value; Assert.Equal(2, walletHistoryModel.AccountsHistoryModel.First().TransactionsHistory.Where(x => x.Type == TransactionItemType.Send).Count()); // Test serialization // TODO: When refactoring integration tests, move this to the one place and test all types, from method param to storage to serialization. var serializationRequest = new BuildCallContractTransactionRequest { AccountName = AccountName, GasLimit = "10000", GasPrice = "1", Amount = "0", MethodName = "TestSerializer", ContractAddress = response.NewContractAddress, FeeAmount = "0.001", Password = Password, WalletName = WalletName, Sender = addr.Address }; result = (JsonResult)senderSmartContractsController.BuildCallSmartContractTransaction(serializationRequest); var serializationResponse = (BuildCallContractTransactionResponse)result.Value; SmartContractSharedSteps.SendTransaction(scSender, scReceiver, senderWalletController, serializationResponse.Hex); TestHelper.MineBlocks(scReceiver, 2, WalletName, Password, AccountName); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Would have only saved if execution completed successfully counterRequestResult = (string)((JsonResult)senderSmartContractsController.GetStorage(new GetStorageRequest { ContractAddress = response.NewContractAddress.ToString(), StorageKey = "Int32", DataType = SmartContractDataType.Int })).Value; Assert.Equal("12345", counterRequestResult); } }
protected override void BeforeTest() { this.nodeBuilder = NodeBuilder.Create(); }
public void MempoolSyncTransactions() { using (NodeBuilder builder = NodeBuilder.Create()) { var xelsNodeSync = builder.CreateXelsPowNode(); var xelsNode1 = builder.CreateXelsPowNode(); var xelsNode2 = builder.CreateXelsPowNode(); builder.StartAll(); xelsNodeSync.NotInIBD(); xelsNode1.NotInIBD(); xelsNode2.NotInIBD(); // generate blocks and wait for the downloader to pickup xelsNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), xelsNodeSync.FullNode.Network)); xelsNodeSync.GenerateXelsWithMiner(105); // coinbase maturity = 100 // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(xelsNodeSync)); // sync both nodes xelsNode1.CreateRPCClient().AddNode(xelsNodeSync.Endpoint, true); xelsNode2.CreateRPCClient().AddNode(xelsNodeSync.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(xelsNode1, xelsNodeSync)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(xelsNode2, xelsNodeSync)); // create some transactions and push them to the pool var trxs = new List <Transaction>(); foreach (var index in Enumerable.Range(1, 5)) { var block = xelsNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(xelsNodeSync.FullNode.Chain.GetBlock(index).HashBlock).Result; var prevTrx = block.Transactions.First(); var dest = new BitcoinSecret(new Key(), xelsNodeSync.FullNode.Network); Transaction tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(xelsNodeSync.MinerSecret.PubKey))); tx.AddOutput(new TxOut("25", dest.PubKey.Hash)); tx.AddOutput(new TxOut("24", new Key().PubKey.Hash)); // 1 btc fee tx.Sign(xelsNodeSync.MinerSecret, false); trxs.Add(tx); } var options = new ParallelOptions { MaxDegreeOfParallelism = 5 }; Parallel.ForEach(trxs, options, transaction => { xelsNodeSync.Broadcast(transaction); }); // wait for all nodes to have all trx TestHelper.WaitLoop(() => xelsNodeSync.CreateRPCClient().GetRawMempool().Length == 5); // the full node should be connected to both nodes Assert.True(xelsNodeSync.FullNode.ConnectionManager.ConnectedPeers.Count() >= 2); // reset the trickle timer on the full node that has the transactions in the pool foreach (var node in xelsNodeSync.FullNode.ConnectionManager.ConnectedPeers) { node.Behavior <MempoolBehavior>().NextInvSend = 0; } TestHelper.WaitLoop(() => xelsNode1.CreateRPCClient().GetRawMempool().Length == 5); TestHelper.WaitLoop(() => xelsNode2.CreateRPCClient().GetRawMempool().Length == 5); // mine the transactions in the mempool xelsNodeSync.GenerateXelsWithMiner(1); TestHelper.WaitLoop(() => xelsNodeSync.CreateRPCClient().GetRawMempool().Length == 0); // wait for block and mempool to change TestHelper.WaitLoop(() => xelsNode1.CreateRPCClient().GetBestBlockHash() == xelsNodeSync.CreateRPCClient().GetBestBlockHash()); TestHelper.WaitLoop(() => xelsNode2.CreateRPCClient().GetBestBlockHash() == xelsNodeSync.CreateRPCClient().GetBestBlockHash()); TestHelper.WaitLoop(() => xelsNode1.CreateRPCClient().GetRawMempool().Length == 0); TestHelper.WaitLoop(() => xelsNode2.CreateRPCClient().GetRawMempool().Length == 0); } }
public void TxMempoolBlockDoublespend() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithDummyWallet().Start(); stratisNodeSync.FullNode.NodeService <MempoolSettings>().RequireStandard = true; // make sure to test standard tx TestHelper.MineBlocks(stratisNodeSync, 100); // coinbase maturity = 100 // Make sure skipping validation of transctions that were // validated going into the memory pool does not allow // double-spends in blocks to pass validation when they should not. Script scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey); Block genBlock = stratisNodeSync.FullNode.BlockStore().GetBlockAsync(stratisNodeSync.FullNode.Chain.GetBlock(1).HashBlock).Result; // Create a double-spend of mature coinbase txn: var spends = new List <Transaction>(2); foreach (int index in Enumerable.Range(1, 2)) { Transaction trx = stratisNodeSync.FullNode.Network.CreateTransaction(); trx.AddInput(new TxIn(new OutPoint(genBlock.Transactions[0].GetHash(), 0), scriptPubKey)); trx.AddOutput(Money.Cents(11), new Key().PubKey.Hash); // Sign: trx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); spends.Add(trx); } // Test 1: block with both of those transactions should be rejected. var tipBeforeBlockCreation = stratisNodeSync.FullNode.Chain.Tip; Assert.Throws <ConsensusException>(() => { Block block = TestHelper.GenerateBlockManually(stratisNodeSync, spends); }); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == tipBeforeBlockCreation.HashBlock); // Test 2: ... and should be rejected if spend1 is in the memory pool tipBeforeBlockCreation = stratisNodeSync.FullNode.Chain.Tip; Assert.True(stratisNodeSync.AddToStratisMempool(spends[0])); Assert.Throws <ConsensusException>(() => { Block block = TestHelper.GenerateBlockManually(stratisNodeSync, spends, 100_000); }); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == tipBeforeBlockCreation.HashBlock); stratisNodeSync.FullNode.MempoolManager().Clear().Wait(); // Test 3: ... and should be rejected if spend2 is in the memory pool tipBeforeBlockCreation = stratisNodeSync.FullNode.Chain.Tip; Assert.True(stratisNodeSync.AddToStratisMempool(spends[1])); Assert.Throws <ConsensusException>(() => { Block block = TestHelper.GenerateBlockManually(stratisNodeSync, spends, 100_000_000); }); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == tipBeforeBlockCreation.HashBlock); stratisNodeSync.FullNode.MempoolManager().Clear().Wait(); // Final sanity test: first spend in mempool, second in block, that's OK: var oneSpend = new List <Transaction>(); oneSpend.Add(spends[0]); Assert.True(stratisNodeSync.AddToStratisMempool(spends[1])); var validBlock = TestHelper.GenerateBlockManually(stratisNodeSync, oneSpend); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusManager().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == validBlock.GetHash()); // spends[1] should have been removed from the mempool when the // block with spends[0] is accepted: TestHelper.WaitLoop(() => stratisNodeSync.FullNode.MempoolManager().MempoolSize().Result == 0); } }
public void TxMempoolMapOrphans() { var rand = new Random(); var randByte = new byte[32]; Func <uint256> randHash = () => { rand.NextBytes(randByte); return(new uint256(randByte)); }; using (NodeBuilder builder = NodeBuilder.Create()) { var xelsNode = builder.CreateXelsPowNode(); builder.StartAll(); xelsNode.SetDummyMinerSecret(new BitcoinSecret(new Key(), xelsNode.FullNode.Network)); // 50 orphan transactions: for (ulong i = 0; i < 50; i++) { Transaction tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(randHash(), 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), xelsNode.MinerSecret.ScriptPubKey)); xelsNode.FullNode.MempoolManager().Orphans.AddOrphanTx(i, tx).Wait(); } Assert.Equal(50, xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count); // ... and 50 that depend on other orphans: for (ulong i = 0; i < 50; i++) { var txPrev = xelsNode.FullNode.MempoolManager().Orphans.OrphansList().ElementAt(rand.Next(xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count)); Transaction tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money((1 + i + 100) * Money.CENT), xelsNode.MinerSecret.ScriptPubKey)); xelsNode.FullNode.MempoolManager().Orphans.AddOrphanTx(i, tx).Wait(); } Assert.Equal(100, xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count); // This really-big orphan should be ignored: for (ulong i = 0; i < 10; i++) { var txPrev = xelsNode.FullNode.MempoolManager().Orphans.OrphansList().ElementAt(rand.Next(xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count)); Transaction tx = new Transaction(); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), xelsNode.MinerSecret.ScriptPubKey)); foreach (var index in Enumerable.Range(0, 2777)) { tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), index), new Script(OpcodeType.OP_1))); } Assert.False(xelsNode.FullNode.MempoolManager().Orphans.AddOrphanTx(i, tx).Result); } Assert.Equal(100, xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count); // Test EraseOrphansFor: for (ulong i = 0; i < 3; i++) { var sizeBefore = xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count; xelsNode.FullNode.MempoolManager().Orphans.EraseOrphansFor(i).Wait(); Assert.True(xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count < sizeBefore); } // Test LimitOrphanTxSize() function: xelsNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(40).Wait(); Assert.True(xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count <= 40); xelsNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(10).Wait(); Assert.True(xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Count <= 10); xelsNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(0).Wait(); Assert.True(!xelsNode.FullNode.MempoolManager().Orphans.OrphansList().Any()); } }
public void TxMempoolBlockDoublespend() { using (NodeBuilder builder = NodeBuilder.Create()) { var xelsNodeSync = builder.CreateXelsPowNode(); builder.StartAll(); xelsNodeSync.NotInIBD(); xelsNodeSync.FullNode.Settings.RequireStandard = true; // make sure to test standard tx xelsNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), xelsNodeSync.FullNode.Network)); xelsNodeSync.GenerateXels(100); // coinbase maturity = 100 TestHelper.WaitLoop(() => xelsNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == xelsNodeSync.FullNode.Chain.Tip.HashBlock); TestHelper.WaitLoop(() => xelsNodeSync.FullNode.HighestPersistedBlock().HashBlock == xelsNodeSync.FullNode.Chain.Tip.HashBlock); // Make sure skipping validation of transctions that were // validated going into the memory pool does not allow // double-spends in blocks to pass validation when they should not. var scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(xelsNodeSync.MinerSecret.PubKey); var genBlock = xelsNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(xelsNodeSync.FullNode.Chain.GetBlock(1).HashBlock).Result; // Create a double-spend of mature coinbase txn: List <Transaction> spends = new List <Transaction>(2); foreach (var index in Enumerable.Range(1, 2)) { var trx = new Transaction(); trx.AddInput(new TxIn(new OutPoint(genBlock.Transactions[0].GetHash(), 0), scriptPubKey)); trx.AddOutput(Money.Cents(11), new Key().PubKey.Hash); // Sign: trx.Sign(xelsNodeSync.MinerSecret, false); spends.Add(trx); } // Test 1: block with both of those transactions should be rejected. var block = xelsNodeSync.GenerateXels(1, spends).Single(); TestHelper.WaitLoop(() => xelsNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == xelsNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(xelsNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash()); // Test 2: ... and should be rejected if spend1 is in the memory pool Assert.True(xelsNodeSync.AddToXelsMempool(spends[0])); block = xelsNodeSync.GenerateXels(1, spends).Single(); TestHelper.WaitLoop(() => xelsNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == xelsNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(xelsNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash()); xelsNodeSync.FullNode.MempoolManager().Clear().Wait(); // Test 3: ... and should be rejected if spend2 is in the memory pool Assert.True(xelsNodeSync.AddToXelsMempool(spends[1])); block = xelsNodeSync.GenerateXels(1, spends).Single(); TestHelper.WaitLoop(() => xelsNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == xelsNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(xelsNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash()); xelsNodeSync.FullNode.MempoolManager().Clear().Wait(); // Final sanity test: first spend in mempool, second in block, that's OK: List <Transaction> oneSpend = new List <Transaction>(); oneSpend.Add(spends[0]); Assert.True(xelsNodeSync.AddToXelsMempool(spends[1])); block = xelsNodeSync.GenerateXels(1, oneSpend).Single(); TestHelper.WaitLoop(() => xelsNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == xelsNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(xelsNodeSync.FullNode.Chain.Tip.HashBlock == block.GetHash()); // spends[1] should have been removed from the mempool when the // block with spends[0] is accepted: TestHelper.WaitLoop(() => xelsNodeSync.FullNode.MempoolManager().MempoolSize().Result == 0); } }
public ProofOfStakeSteps(string displayName) { this.nodeBuilder = NodeBuilder.Create(Path.Combine(this.GetType().Name, displayName)); }
public void TxMempoolMapOrphans() { var rand = new Random(); var randByte = new byte[32]; uint256 randHash() { rand.NextBytes(randByte); return(new uint256(randByte)); } using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNode = builder.CreateStratisPowNode(this.network).WithDummyWallet().Start(); // 50 orphan transactions: for (ulong i = 0; i < 50; i++) { Transaction tx = stratisNode.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(randHash(), 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); stratisNode.FullNode.NodeService <MempoolOrphans>().AddOrphanTx(i, tx); } Assert.Equal(50, stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count); // ... and 50 that depend on other orphans: for (ulong i = 0; i < 50; i++) { MempoolOrphans.OrphanTx txPrev = stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().ElementAt(rand.Next(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count)); Transaction tx = stratisNode.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money((1 + i + 100) * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); stratisNode.FullNode.NodeService <MempoolOrphans>().AddOrphanTx(i, tx); } Assert.Equal(100, stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count); // This really-big orphan should be ignored: for (ulong i = 0; i < 10; i++) { MempoolOrphans.OrphanTx txPrev = stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().ElementAt(rand.Next(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count)); Transaction tx = stratisNode.FullNode.Network.CreateTransaction(); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); foreach (int index in Enumerable.Range(0, 2777)) { tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), index), new Script(OpcodeType.OP_1))); } Assert.False(stratisNode.FullNode.NodeService <MempoolOrphans>().AddOrphanTx(i, tx)); } Assert.Equal(100, stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count); // Test EraseOrphansFor: for (ulong i = 0; i < 3; i++) { int sizeBefore = stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count; stratisNode.FullNode.NodeService <MempoolOrphans>().EraseOrphansFor(i); Assert.True(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count < sizeBefore); } // Test LimitOrphanTxSize() function: stratisNode.FullNode.NodeService <MempoolOrphans>().LimitOrphanTxSize(40); Assert.True(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count <= 40); stratisNode.FullNode.NodeService <MempoolOrphans>().LimitOrphanTxSize(10); Assert.True(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count <= 10); stratisNode.FullNode.NodeService <MempoolOrphans>().LimitOrphanTxSize(0); Assert.True(!stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Any()); } }
public void Staking_Wont_Include_Time_Ahead_Of_Coinstake_Timestamp() { using (var builder = NodeBuilder.Create(this)) { var configParameters = new NodeConfigParameters { { "savetrxhex", "true" } }; var network = new StratisRegTest(); var minerA = builder.CreateStratisPosNode(network, "stake-1-minerA", configParameters: configParameters).OverrideDateTimeProvider().WithWallet().Start(); var addressUsed = TestHelper.MineBlocks(minerA, (int)network.Consensus.PremineHeight).AddressUsed; // Since the pre-mine will not be immediately spendable, the transactions have to be counted directly from the address. addressUsed.Transactions.Count().Should().Be((int)network.Consensus.PremineHeight); addressUsed.Transactions.Sum(s => s.Amount).Should().Be(network.Consensus.PremineReward + network.Consensus.ProofOfWorkReward); // Mine blocks to maturity. TestHelper.MineBlocks(minerA, (int)network.Consensus.CoinbaseMaturity + 1); // Create a transaction and set its timestamp to one that will be rejected from the block. Transaction tx = minerA.FullNode.WalletTransactionHandler().BuildTransaction(new TransactionBuildContext(network) { Recipients = new List <Recipient>() { new Recipient { ScriptPubKey = addressUsed.ScriptPubKey, Amount = Money.Coins(1m) } }, AccountReference = new WalletAccountReference(minerA.WalletName, "account 0"), WalletPassword = minerA.WalletPassword, Time = (uint)minerA.FullNode.DateTimeProvider.GetAdjustedTimeAsUnixTimestamp() }); minerA.AddToStratisMempool(tx); TestBase.WaitLoop(() => minerA.FullNode.MempoolManager().InfoAll().Count == 1); // Get our height right now. int currentHeight = minerA.FullNode.ChainIndexer.Height; // Start staking on the node. var minter = minerA.FullNode.NodeService <IPosMinting>(); minter.Stake(new WalletSecret() { WalletName = minerA.WalletName, WalletPassword = minerA.WalletPassword }); // Ensure we've staked a block. TestBase.WaitLoop(() => minerA.FullNode.ChainIndexer.Height > currentHeight); // Get the staked block. ChainedHeader header = minerA.FullNode.ChainIndexer.GetHeader(currentHeight + 1); Block block = minerA.FullNode.BlockStore().GetBlock(header.HashBlock); // The transaction should not be in it. Assert.DoesNotContain(block.Transactions, x => x.GetHash() == tx.GetHash()); } }
protected override void BeforeTest() { this.builder = NodeBuilder.Create(Path.Combine(this.GetType().Name, this.CurrentTest.DisplayName)); }
public void TestDualClientWithoutTor() { using (NodeBuilder builder = NodeBuilder.Create(version: "0.15.1")) { HttpClient client = null; var coreNode = builder.CreateNode(false); coreNode.ConfigParameters.AddOrReplace("debug", "1"); coreNode.ConfigParameters.AddOrReplace("printtoconsole", "0"); coreNode.ConfigParameters.AddOrReplace("prematurewitness", "1"); coreNode.ConfigParameters.AddOrReplace("walletprematurewitness", "1"); coreNode.ConfigParameters.AddOrReplace("rpcworkqueue", "100"); coreNode.Start(); // Replicate portions of BreezeServer's Program.cs. Maybe refactor it into a class/function in future var serviceProvider = new ServiceCollection() .AddLogging() .AddSingleton <Breeze.BreezeServer.Services.ITumblerService, Breeze.BreezeServer.Services.TumblerService>() .BuildServiceProvider(); serviceProvider .GetService <ILoggerFactory>() .AddConsole(LogLevel.Debug); // Skip the registration code - that can be tested separately string configPath = Path.Combine(coreNode.DataFolder, "breeze.conf"); string[] breezeServerConfig = { "network=regtest", // Only the network setting is currently used from this file "rpc.user=dummy", "rpc.password=dummy", "rpc.url=http://127.0.0.1:26174/", "breeze.ipv4=127.0.0.1", "breeze.ipv6=2001:0db8:85a3:0000:0000:8a2e:0370:7334", "breeze.onion=0123456789ABCDEF", "breeze.port=37123", "breeze.regtxfeevalue=10000", "breeze.regtxoutputvalue=1000", "tumbler.url=http://127.0.0.1:37123/api/v1/", "tumbler.rsakeyfile=/Users/username/.ntumblebitserver/RegTest/Tumbler.pem", "tumbler.ecdsakeyaddress=TVwRFmEKRCnQAgShf3QshBjp1Tmucm1e87" }; File.WriteAllLines(configPath, breezeServerConfig); BreezeConfiguration config = new BreezeConfiguration(configPath); var rpc3 = coreNode.CreateRPCClient(); string ntbServerConfigPath = Path.Combine(coreNode.DataFolder, "server.config"); string[] ntbServerConfig = { "regtest=1", "rpc.url=http://127.0.0.1:" + rpc3.Address.Port + "/", "rpc.user="******"rpc.password="******"cycle=kotori", "tor.enabled=false" }; File.WriteAllLines(ntbServerConfigPath, ntbServerConfig); // We need to start up the masternode prior to creating the SBFN instance so that // we have the URI available for starting the TumbleBit feature // TODO: Also need to see if NTB interactive console interferes with later parts of the test new Thread(delegate() { Thread.CurrentThread.IsBackground = true; // By instantiating the TumblerService directly the registration logic is skipped var tumbler = serviceProvider.GetService <Breeze.BreezeServer.Services.ITumblerService>(); tumbler.StartTumbler(config, false, "server.config", Path.GetFullPath(coreNode.DataFolder), false); }).Start(); // Wait for URI file to be written out by the TumblerService while (!File.Exists(Path.Combine(coreNode.DataFolder, "uri.txt"))) { Thread.Sleep(1000); } Console.WriteLine("* URI file detected *"); Thread.Sleep(5000); var serverAddress = File.ReadAllText(Path.Combine(coreNode.DataFolder, "uri.txt")); // Not used for this test ConfigurationOptionWrapper <string> registrationStoreDirectory = new ConfigurationOptionWrapper <string>("RegistrationStoreDirectory", ""); // Force SBFN to use the temporary hidden service to connect to the server ConfigurationOptionWrapper <string> masternodeUri = new ConfigurationOptionWrapper <string>("MasterNodeUri", serverAddress); ConfigurationOptionWrapper <string>[] configurationOptions = { registrationStoreDirectory, masternodeUri }; // Logging for NTB client code ConsoleLoggerProcessor loggerProcessor = new ConsoleLoggerProcessor(); Logs.Configure(new FuncLoggerFactory(i => new CustomerConsoleLogger(i, Logs.SupportDebug(true), false, loggerProcessor))); CoreNode node1 = builder.CreateStratisPowNode(false, fullNodeBuilder => { fullNodeBuilder .UseConsensus() .UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .AddMining() .UseWallet() .UseWatchOnlyWallet() .UseApi() .AddRPC() .UseTumbleBit(configurationOptions); }); node1.ConfigParameters.AddOrReplace("apiuri", "http://localhost:37229"); CoreNode node2 = builder.CreateStratisPowNode(false, fullNodeBuilder => { fullNodeBuilder .UseConsensus() .UseBlockStore() .UseMempool() .UseBlockNotification() .UseTransactionNotification() .AddMining() .UseWallet() .UseWatchOnlyWallet() .UseApi() .AddRPC() .UseTumbleBit(configurationOptions); }); node2.ConfigParameters.AddOrReplace("apiuri", "http://localhost:37228"); var apiSettings1 = node1.FullNode.NodeService <ApiSettings>(); var apiSettings2 = node2.FullNode.NodeService <ApiSettings>(); node1.Start(); node2.Start(); // TODO: See if it is possible to split node1 and node2's logs into separate folders NLog.Config.LoggingConfiguration config1 = LogManager.Configuration; var folder = Path.Combine(node1.DataFolder, "Logs"); var tbTarget = new FileTarget(); tbTarget.Name = "tumblebit"; tbTarget.FileName = Path.Combine(folder, "tumblebit.txt"); tbTarget.ArchiveFileName = Path.Combine(folder, "tb-${date:universalTime=true:format=yyyy-MM-dd}.txt"); tbTarget.ArchiveNumbering = ArchiveNumberingMode.Sequence; tbTarget.ArchiveEvery = FileArchivePeriod.Day; tbTarget.MaxArchiveFiles = 7; tbTarget.Layout = "[${longdate:universalTime=true} ${threadid}${mdlc:item=id}] ${level:uppercase=true}: ${callsite} ${message}"; tbTarget.Encoding = Encoding.UTF8; var ruleTb = new LoggingRule("*", NLog.LogLevel.Debug, tbTarget); config1.LoggingRules.Add(ruleTb); config1.AddTarget(tbTarget); // Apply new rules. LogManager.ReconfigExistingLoggers(); node1.NotInIBD(); node2.NotInIBD(); // Create the source and destination wallets for node 1 var wm1 = node1.FullNode.NodeService <IWalletManager>() as WalletManager; wm1.CreateWallet("TumbleBit1", "alice1"); wm1.CreateWallet("TumbleBit1", "bob1"); // Create the source and destination wallets for node 2 var wm2 = node2.FullNode.NodeService <IWalletManager>() as WalletManager; wm2.CreateWallet("TumbleBit1", "alice2"); wm2.CreateWallet("TumbleBit1", "bob2"); // Mined coins only mature after 100 blocks on regtest // Additionally, we need to force Segwit to activate in order for NTB to work correctly coreNode.FindBlock(450); var rpc1 = node1.CreateRPCClient(); var rpc2 = node2.CreateRPCClient(); rpc3.AddNode(node1.Endpoint, false); rpc3.AddNode(node2.Endpoint, false); rpc1.AddNode(coreNode.Endpoint, false); rpc1.AddNode(node2.Endpoint, false); var amount = new Money(5.0m, MoneyUnit.BTC); var destination1 = wm1.GetUnusedAddress(new WalletAccountReference("alice1", "account 0")); var destination2 = wm2.GetUnusedAddress(new WalletAccountReference("alice2", "account 0")); rpc3.SendToAddress(BitcoinAddress.Create(destination1.Address, Network.RegTest), amount); rpc3.SendToAddress(BitcoinAddress.Create(destination2.Address, Network.RegTest), amount); Console.WriteLine("Waiting for transactions to propagate and finalise"); Thread.Sleep(5000); coreNode.FindBlock(1); // Wait for SBFN to sync with the core node TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc3.GetBestBlockHash()); TestHelper.WaitLoop(() => rpc2.GetBestBlockHash() == rpc3.GetBestBlockHash()); // Test implementation note: the coins do not seem to immediately appear in the wallet. // This is possibly some sort of race condition between the wallet manager and block generation/sync. // This extra delay seems to ensure that the coins are definitely in the wallet by the time the // transaction count gets logged to the console below. // Wait instead of generating a block Thread.Sleep(5000); var loggerFactory1 = node1.FullNode.NodeService <ILoggerFactory>(); var loggerFactory2 = node2.FullNode.NodeService <ILoggerFactory>(); var logger1 = loggerFactory1.CreateLogger(this.GetType().FullName); var logger2 = loggerFactory2.CreateLogger(this.GetType().FullName); logger1.LogError("(1) Number of wallet transactions: " + wm1.GetSpendableTransactionsInWallet("alice1").Count()); logger2.LogError("(2) Number of wallet transactions: " + wm2.GetSpendableTransactionsInWallet("alice2").Count()); // Connect to server and start tumbling using (client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // Sample returned output // {"tumbler":"ctb://<onionaddress>.onion?h=<confighash>","denomination":"0.01000000","fee":"0.00010000","network":"RegTest","estimate":"22200"} var connectResponse = client.GetStringAsync(apiSettings1.ApiUri + "api/TumbleBit/connect").GetAwaiter().GetResult(); //Assert.StartsWith("[{\"", connectResponse); var tumbleModel = new TumbleRequest { OriginWalletName = "alice1", OriginWalletPassword = "******", DestinationWalletName = "bob1" }; var tumbleContent = new StringContent(tumbleModel.ToString(), Encoding.UTF8, "application/json"); var tumbleResponse = client.PostAsync(apiSettings1.ApiUri + "api/TumbleBit/tumble", tumbleContent).GetAwaiter().GetResult(); // Note that the TB client takes about 30 seconds to completely start up, as it has to check the server parameters and // RSA key proofs //Assert.StartsWith("[{\"", tumbleResponse); } using (client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var connectResponse = client.GetStringAsync(apiSettings2.ApiUri + "api/TumbleBit/connect").GetAwaiter().GetResult(); var tumbleModel = new TumbleRequest { OriginWalletName = "alice2", OriginWalletPassword = "******", DestinationWalletName = "bob2" }; var tumbleContent = new StringContent(tumbleModel.ToString(), Encoding.UTF8, "application/json"); var tumbleResponse = client.PostAsync(apiSettings2.ApiUri + "api/TumbleBit/tumble", tumbleContent).GetAwaiter().GetResult(); // Note that the TB client takes about 30 seconds to completely start up, as it has to check the server parameters and // RSA key proofs } logger1.LogError("(1) About to start tumbling loop"); logger2.LogError("(2) About to start tumbling loop"); // TODO: Move forward specific numbers of blocks and check interim states? TB tests already do that for (int i = 0; i < 80; i++) { rpc3.Generate(1); builder.SyncNodes(); // Try to ensure the invalid phase error does not occur // (seems to occur when the server has not yet processed a new block and the client has) TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc3.GetBestBlockHash()); TestHelper.WaitLoop(() => rpc2.GetBestBlockHash() == rpc3.GetBestBlockHash()); /*var mempool = node1.FullNode.NodeService<MempoolManager>(); * var mempoolTx = mempool.GetMempoolAsync().Result; * if (mempoolTx.Count > 0) * { * Console.WriteLine("--- Mempool contents ---"); * foreach (var tx in mempoolTx) * { * var hex = mempool.GetTransaction(tx).Result; * Console.WriteLine(tx + " ->"); * Console.WriteLine(hex); * Console.WriteLine("---"); * } * }*/ Thread.Sleep(20000); } // Check destination wallet for tumbled coins // TODO: Need to amend TumblerService so that it can be shut down within the test if (client != null) { client.Dispose(); client = null; } } }
public async Task GetTransactionOnTransactionSentFromMultipleOutputsAsync() { using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start(); TestHelper.ConnectAndSync(sendingNode, receivingNode); // Get an address to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with an Op_Return. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeType = "low", Password = "******", ShuffleOutputs = false, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = "98000002" }).ToList(), }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); uint256 txId = buildTransactionModel.TransactionId; // Mine so that we make sure the node is up to date. TestHelper.MineBlocks(sendingNode, 1); // Get the block that was mined. string lastBlockHash = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("consensus/getbestblockhash") .GetJsonAsync <string>(); BlockModel blockModelAtTip = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("blockstore/block") .SetQueryParams(new { hash = lastBlockHash, outputJson = true }) .GetJsonAsync <BlockModel>(); Transaction trx = this.network.Consensus.ConsensusFactory.CreateTransaction(buildTransactionModel.Hex); RPCClient rpcSendingNode = sendingNode.CreateRPCClient(); RPCResponse txSendingWallet = rpcSendingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); // Assert. GetTransactionModel resultSendingWallet = txSendingWallet.Result.ToObject <GetTransactionModel>(); resultSendingWallet.Amount.Should().Be((decimal) - 98000002.00000000); resultSendingWallet.Fee.Should().Be((decimal) - 0.0001); resultSendingWallet.Confirmations.Should().Be(1); resultSendingWallet.Isgenerated.Should().BeNull(); resultSendingWallet.TransactionId.Should().Be(txId); resultSendingWallet.BlockHash.Should().Be(uint256.Parse(blockModelAtTip.Hash)); resultSendingWallet.BlockIndex.Should().Be(1); resultSendingWallet.BlockTime.Should().Be(blockModelAtTip.Time); resultSendingWallet.TimeReceived.Should().BeLessOrEqualTo(blockModelAtTip.Time); resultSendingWallet.TransactionTime.Should().Be(((PosTransaction)trx).Time); resultSendingWallet.Details.Count.Should().Be(1); GetTransactionDetailsModel detailsSendingWallet = resultSendingWallet.Details.Single(); detailsSendingWallet.Address.Should().Be(unusedaddresses.Single()); detailsSendingWallet.Amount.Should().Be((decimal) - 98000002.00000000); detailsSendingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Send); detailsSendingWallet.Fee.Should().Be((decimal) - 0.0001); detailsSendingWallet.OutputIndex.Should().Be(1); } }
public void SmartContracts_AddToMempool_OnlyValid() { using (NodeBuilder builder = NodeBuilder.Create(this)) { var stratisNodeSync = builder.CreateSmartContractPowNode(); builder.StartAll(); stratisNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network)); stratisNodeSync.GenerateStratisWithMiner(105); // coinbase maturity = 100 TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusManager().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.GetBlockStoreTip().HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); var block = stratisNodeSync.FullNode.BlockStore().GetBlockAsync(stratisNodeSync.FullNode.Chain.GetBlock(4).HashBlock).Result; var prevTrx = block.Transactions.First(); var dest = new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network); // Gas higher than allowed limit Transaction tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); SmartContractCarrier smartContractCarrier = SmartContractCarrier.CallContract(1, new uint160(0), "Test", 1, new Gas(10_000_000)); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); // OP_SPEND in user's tx - we can't sign this because the TransactionBuilder recognises the ScriptPubKey is invalid. tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), new Script(new[] { (byte)ScOpcodeType.OP_SPEND }))); smartContractCarrier = SmartContractCarrier.CallContract(1, new uint160(0), "Test", 1, new Gas(100_000)); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); stratisNodeSync.Broadcast(tx); // 2 smart contract outputs tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); smartContractCarrier = SmartContractCarrier.CallContract(1, new uint160(0), "Test", 1, new Gas(100_000)); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); tx.AddOutput(new TxOut(1, new Script(smartContractCarrier.Serialize()))); tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); // Send to contract uint160 contractAddress = new uint160(123); var state = stratisNodeSync.FullNode.NodeService <IContractStateRoot>(); state.CreateAccount(contractAddress); state.Commit(); tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); tx.AddOutput(new TxOut(100, PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(new KeyId(contractAddress)))); tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); // After 5 seconds (plenty of time but ideally we would have a more accurate measure) no txs in mempool. All failed validation. Thread.Sleep(5000); Assert.Empty(stratisNodeSync.CreateRPCClient().GetRawMempool()); // Valid tx still works tx = new Transaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); tx.AddOutput(new TxOut("25", dest.PubKey.Hash)); tx.AddOutput(new TxOut("24", new Key().PubKey.Hash)); // 1 btc fee tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); TestHelper.WaitLoop(() => stratisNodeSync.CreateRPCClient().GetRawMempool().Length == 1); } }
public void SBFNCreatesOpReturnTransaction_XSyncs() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisOverrideRegTest(); CoreNode stratisXNode = builder.CreateStratisXNode(version: "2.0.0.5").Start(); // We do not want the datetime provider to be substituted, // so a custom builder callback has to be used. var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC() .UseTestChainedHeaderTree() .MockIBD()); CoreNode stratisNode = builder.CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.POS_PROTOCOL_VERSION, minProtocolVersion: ProtocolVersion.POS_PROTOCOL_VERSION).WithWallet().Start(); RPCClient stratisXRpc = stratisXNode.CreateRPCClient(); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); stratisXRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(stratisXNode.Endpoint, false); TestHelper.MineBlocks(stratisNode, 11); // It takes a reasonable amount of time for blocks to be generated without // the datetime provider substitution. var longCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(15)).Token; var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: longCancellationToken); // Send transaction to arbitrary address from SBFN side. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); //stratisNodeRpc.WalletPassphrase("password", 60); var transactionBuildContext = new TransactionBuildContext(stratisNode.FullNode.Network) { AccountReference = new WalletAccountReference("mywallet", "account 0"), MinConfirmations = 1, OpReturnData = "test", OpReturnAmount = Money.Coins(0.01m), WalletPassword = "******", Recipients = new List <Recipient>() { new Recipient() { Amount = Money.Coins(1), ScriptPubKey = aliceAddress.ScriptPubKey } } }; var transaction = stratisNode.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); stratisNode.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Transaction should percolate through to X's mempool. TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); } }
public void WalletCanReorg() { // this test has 4 parts: // send first transaction from one wallet to another and wait for it to be confirmed // send a second transaction and wait for it to be confirmed // connected to a longer chain that couse a reorg back so the second trasnaction is undone // mine the second transaction back in to the main chain using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisSender = builder.CreateStratisPowNode(); CoreNode stratisReceiver = builder.CreateStratisPowNode(); CoreNode stratisReorg = builder.CreateStratisPowNode(); builder.StartAll(); stratisSender.NotInIBD(); stratisReceiver.NotInIBD(); stratisReorg.NotInIBD(); // get a key from the wallet Mnemonic mnemonic1 = stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet"); Mnemonic mnemonic2 = stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic1.Words.Length); Assert.Equal(12, mnemonic2.Words.Length); HdAddress addr = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); Features.Wallet.Wallet wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet"); Key key = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); stratisReorg.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); int maturity = (int)stratisSender.FullNode.Network.Consensus.CoinbaseMaturity; stratisSender.GenerateStratisWithMiner(maturity + 15); int currentBestHeight = maturity + 15; // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); // the mining should add coins to the wallet long total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * currentBestHeight * 50, total); // sync all nodes stratisReceiver.CreateRPCClient().AddNode(stratisSender.Endpoint, true); stratisReceiver.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSender.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); // Build Transaction 1 // ==================== // send coins to the receiver HdAddress sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // broadcast to the other node stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), false)); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); long receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // generate two new blocks so the trx is confirmed stratisSender.GenerateStratisWithMiner(1); int transaction1MinedHeight = currentBestHeight + 1; stratisSender.GenerateStratisWithMiner(1); currentBestHeight = currentBestHeight + 2; // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); TestHelper.WaitLoop(() => transaction1MinedHeight == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // Build Transaction 2 // ==================== // remove the reorg node stratisReceiver.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); stratisSender.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(stratisReorg)); ChainedHeader forkblock = stratisReceiver.FullNode.Chain.Tip; // send more coins to the wallet sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); Transaction transaction2 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 101)); stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction2.GetHash(), false)); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); long newamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 110, newamount); Assert.Contains(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet"), b => b.Transaction.BlockHeight == null); // mine more blocks so its included in the chain stratisSender.GenerateStratisWithMiner(1); int transaction2MinedHeight = currentBestHeight + 1; stratisSender.GenerateStratisWithMiner(1); currentBestHeight = currentBestHeight + 2; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); // create a reorg by mining on two different chains // ================================================ // advance both chains, one chin is longer stratisSender.GenerateStratisWithMiner(2); stratisReorg.GenerateStratisWithMiner(10); currentBestHeight = forkblock.Height + 10; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg)); // connect the reorg chain stratisReceiver.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSender.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); // wait for the chains to catch up TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); // ensure wallet reorg complete TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().WalletTipHash == stratisReorg.CreateRPCClient().GetBestBlockHash()); // check the wallet amount was rolled back long newtotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(receivetotal, newtotal); TestHelper.WaitLoop(() => maturity + 16 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // ReBuild Transaction 2 // ==================== // After the reorg transaction2 was returned back to mempool stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); // mine the transaction again stratisSender.GenerateStratisWithMiner(1); transaction2MinedHeight = currentBestHeight + 1; stratisSender.GenerateStratisWithMiner(1); currentBestHeight = currentBestHeight + 2; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); long newsecondamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(newamount, newsecondamount); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); } }
public void Transaction_TraversesNodes_AndIsMined_AndNodesSync() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisOverrideRegTest(); CoreNode xNode1 = builder.CreateStratisXNode(version: "2.0.0.5").Start(); var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC()); var config = new NodeConfigParameters(); config.Add("whitelist", xNode1.Endpoint.ToString()); config.Add("gateway", "1"); CoreNode sbfnNode2 = builder .CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, minProtocolVersion: ProtocolVersion.POS_PROTOCOL_VERSION, configParameters: config) .WithWallet().Start(); CoreNode xNode3 = builder.CreateStratisXNode(version: "2.0.0.5").Start(); RPCClient xRpc1 = xNode1.CreateRPCClient(); RPCClient sbfnRpc2 = sbfnNode2.CreateRPCClient(); RPCClient xRpc3 = xNode3.CreateRPCClient(); sbfnRpc2.AddNode(xNode1.Endpoint, false); sbfnRpc2.AddNode(xNode3.Endpoint, false); xRpc1.SendCommand(RPCOperations.generate, 11); var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => xRpc1.GetBlockCount() >= 11, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc1.GetBestBlockHash() == sbfnRpc2.GetBestBlockHash(), cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc1.GetBestBlockHash() == xRpc3.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Send transaction to arbitrary address. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); xRpc1.SendCommand(RPCOperations.sendtoaddress, aliceAddress.ToString(), 1); TestBase.WaitLoop(() => xRpc1.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => sbfnRpc2.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc3.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // TODO: Until #2468 is fixed we need an X node to mine the block so it doesn't get rejected. xRpc1.SendCommand(RPCOperations.generate, 1); TestBase.WaitLoop(() => xRpc1.GetBlockCount() >= 12, cancellationToken: shortCancellationToken); // We expect that SBFN and the other X node will sync correctly. TestBase.WaitLoop(() => sbfnRpc2.GetBestBlockHash() == xRpc1.GetBestBlockHash(), cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc3.GetBestBlockHash() == xRpc1.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Sanity check - mempools should all become empty. TestBase.WaitLoop(() => xRpc1.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => sbfnRpc2.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc3.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); } }
public void WalletCanReceiveAndSendCorrectly() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisSender = builder.CreateStratisPowNode(); CoreNode stratisReceiver = builder.CreateStratisPowNode(); builder.StartAll(); stratisSender.NotInIBD(); stratisReceiver.NotInIBD(); // get a key from the wallet Mnemonic mnemonic1 = stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet"); Mnemonic mnemonic2 = stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic1.Words.Length); Assert.Equal(12, mnemonic2.Words.Length); HdAddress addr = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); Features.Wallet.Wallet wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet"); Key key = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); int maturity = (int)stratisSender.FullNode.Network.Consensus.CoinbaseMaturity; stratisSender.GenerateStratis(maturity + 5); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); // the mining should add coins to the wallet long total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 105 * 50, total); // sync both nodes stratisSender.CreateRPCClient().AddNode(stratisReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); // send coins to the receiver HdAddress sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); Transaction trx = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext( new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // broadcast to the other node stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(trx.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); long receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // generate two new blocks do the trx is confirmed stratisSender.GenerateStratis(1, new List <Transaction>(new[] { trx.Clone() })); stratisSender.GenerateStratis(1); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => maturity + 6 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); } }
public void SendAndReceiveSmartContractTransactions() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractNode(); CoreNode scReceiver = builder.CreateSmartContractNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); scSender.FullNode.WalletManager().CreateWallet(Password, WalletName); scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName); HdAddress addr = scSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, AccountName)); Features.Wallet.Wallet wallet = scSender.FullNode.WalletManager().GetWalletByName(WalletName); Key key = wallet.GetExtendedPrivateKeyForAddress(Password, addr).PrivateKey; scSender.SetDummyMinerSecret(new BitcoinSecret(key, scSender.FullNode.Network)); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; scSender.GenerateStratisWithMiner(maturity + 5); // Wait for block repo for block sync to work. TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // The mining should add coins to the wallet. var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * (maturity + 5) * 50, total); // Create a token contract ulong gasPrice = 1; int vmVersion = 1; Gas gasLimit = (Gas)2000; SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTest.cs"); Assert.True(compilationResult.Success); var contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); var contractCreateScript = new Script(contractCarrier.Serialize()); var txBuildContext = new TransactionBuildContext(scSender.FullNode.Network, new WalletAccountReference(WalletName, AccountName), new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList(), Password) { MinConfirmations = maturity, FeeType = FeeType.High }; Transaction transferContractTransaction = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); // Broadcast the token transaction to the network scSender.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(transferContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); // Mine the token transaction and wait for it sync scSender.GenerateStratisWithMiner(1); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // Sync to the receiver node scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that boths nodes has the contract ContractStateRepositoryRoot senderState = scSender.FullNode.NodeService <ContractStateRepositoryRoot>(); ContractStateRepositoryRoot receiverState = scReceiver.FullNode.NodeService <ContractStateRepositoryRoot>(); uint160 tokenContractAddress = transferContractTransaction.GetNewContractAddress(); Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a transfer token contract compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTest.cs"); Assert.True(compilationResult.Success); contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); contractCreateScript = new Script(contractCarrier.Serialize()); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network, new WalletAccountReference(WalletName, AccountName), new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList(), Password) { MinConfirmations = maturity, FeeType = FeeType.High }; // Broadcast the token transaction to the network transferContractTransaction = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); scSender.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(transferContractTransaction); // Wait for the token transaction to be picked up by the mempool TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); scSender.GenerateStratisWithMiner(1); // Ensure the node is synced TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // Ensure both nodes are synced with each other TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that boths nodes has the contract senderState = scSender.FullNode.NodeService <ContractStateRepositoryRoot>(); receiverState = scReceiver.FullNode.NodeService <ContractStateRepositoryRoot>(); tokenContractAddress = transferContractTransaction.GetNewContractAddress(); Assert.NotNull(senderState.GetCode(tokenContractAddress)); Assert.NotNull(receiverState.GetCode(tokenContractAddress)); scSender.FullNode.MempoolManager().Clear(); // Create a call contract transaction which will transfer funds contractCarrier = SmartContractCarrier.CallContract(1, tokenContractAddress, "Test", gasPrice, gasLimit); Script contractCallScript = new Script(contractCarrier.Serialize()); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network, new WalletAccountReference(WalletName, AccountName), new[] { new Recipient { Amount = 1000, ScriptPubKey = contractCallScript } }.ToList(), Password) { MinConfirmations = maturity, FeeType = FeeType.High }; // Broadcast the token transaction to the network transferContractTransaction = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); scSender.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(transferContractTransaction); TestHelper.WaitLoop(() => scSender.CreateRPCClient().GetRawMempool().Length > 0); // Mine the transaction scSender.GenerateStratisWithMiner(1); // Ensure the nodes are synced TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // The balance should now reflect the transfer Assert.Equal((ulong)900, senderState.GetCurrentBalance(tokenContractAddress)); } }
public async Task WalletCanMineWithColdWalletCoinsAsync() { using (var builder = NodeBuilder.Create(this)) { var network = new StraxRegTest(); CoreNode stratisSender = CreatePowPosMiningNode(builder, network, TestBase.CreateTestDir(this), coldStakeNode: false); CoreNode stratisHotStake = CreatePowPosMiningNode(builder, network, TestBase.CreateTestDir(this), coldStakeNode: true); CoreNode stratisColdStake = CreatePowPosMiningNode(builder, network, TestBase.CreateTestDir(this), coldStakeNode: true); stratisSender.WithReadyBlockchainData(ReadyBlockchain.StraxRegTest150Miner).Start(); stratisHotStake.WithWallet().Start(); stratisColdStake.WithWallet().Start(); var senderWalletManager = stratisSender.FullNode.WalletManager() as ColdStakingManager; var coldWalletManager = stratisColdStake.FullNode.WalletManager() as ColdStakingManager; var hotWalletManager = stratisHotStake.FullNode.WalletManager() as ColdStakingManager; // Set up cold staking account on cold wallet. coldWalletManager.GetOrCreateColdStakingAccount(WalletName, true, Password, null); HdAddress coldWalletAddress = coldWalletManager.GetFirstUnusedColdStakingAddress(WalletName, true); // Set up cold staking account on hot wallet. hotWalletManager.GetOrCreateColdStakingAccount(WalletName, false, Password, null); HdAddress hotWalletAddress = hotWalletManager.GetFirstUnusedColdStakingAddress(WalletName, false); var walletAccountReference = new WalletAccountReference(WalletName, Account); long total2 = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInAccount(walletAccountReference, 1).Sum(s => s.Transaction.Amount); // Sync all nodes TestHelper.ConnectAndSync(stratisHotStake, stratisSender); TestHelper.ConnectAndSync(stratisHotStake, stratisColdStake); TestHelper.Connect(stratisSender, stratisColdStake); // Send coins to hot wallet. Money amountToSend = total2 - network.Consensus.ProofOfWorkReward; HdAddress sendto = hotWalletManager.GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, amountToSend, FeeType.Medium, 1)); // Broadcast to the other node await stratisSender.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisHotStake.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisHotStake.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), null, false)); TestBase.WaitLoop(() => stratisHotStake.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); long receiveTotal = stratisHotStake.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(amountToSend, (Money)receiveTotal); Assert.Null(stratisHotStake.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // Setup cold staking from the hot wallet. Money amountToSend2 = receiveTotal - network.Consensus.ProofOfWorkReward; (Transaction transaction2, _) = hotWalletManager.GetColdStakingSetupTransaction(stratisHotStake.FullNode.WalletTransactionHandler(), coldWalletAddress.Address, hotWalletAddress.Address, WalletName, Account, Password, amountToSend2, new Money(0.02m, MoneyUnit.BTC), false, false, false); // Broadcast to the other node await stratisHotStake.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Any()); long receivetotal2 = coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Sum(s => s.Transaction.Amount); Assert.Equal(amountToSend2, (Money)receivetotal2); Assert.Null(coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).First().Transaction.BlockHeight); // Allow coins to reach maturity int stakingMaturity = ((PosConsensusOptions)network.Consensus.Options).GetStakeMinConfirmations(0, network); TestHelper.MineBlocks(stratisSender, stakingMaturity, true); // Start staking. var hotMiningFeature = stratisHotStake.FullNode.NodeFeature <MiningFeature>(); hotMiningFeature.StartStaking(WalletName, Password); TestBase.WaitLoop(() => { var stakingInfo = stratisHotStake.FullNode.NodeService <IPosMinting>().GetGetStakingInfoModel(); return(stakingInfo.Staking); }); // Wait for money from staking. var cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(3)).Token; TestBase.WaitLoop(() => { // Keep mining to ensure that staking outputs reach maturity. TestHelper.MineBlocks(stratisSender, 1, true); return(coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Sum(s => s.Transaction.Amount) > receivetotal2); }, cancellationToken: cancellationToken); } }
public void SendAndReceiveCorrectly() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractNode(); CoreNode scReceiver = builder.CreateSmartContractNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); Mnemonic mnemonic1 = scSender.FullNode.WalletManager().CreateWallet(Password, WalletName); Mnemonic mnemonic2 = scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName); HdAddress addr = scSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, AccountName)); Features.Wallet.Wallet wallet = scSender.FullNode.WalletManager().GetWalletByName(WalletName); Key key = wallet.GetExtendedPrivateKeyForAddress(Password, addr).PrivateKey; scSender.SetDummyMinerSecret(new BitcoinSecret(key, scSender.FullNode.Network)); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; scSender.GenerateStratisWithMiner(maturity + 5); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); // the mining should add coins to the wallet var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * (maturity + 5) * 50, total); // sync both nodes scSender.CreateRPCClient().AddNode(scReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // send coins to the receiver HdAddress sendto = scReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, AccountName)); var txBuildContext = new TransactionBuildContext(scSender.FullNode.Network, new WalletAccountReference(WalletName, AccountName), new[] { new Recipient { Amount = Money.COIN * 100, ScriptPubKey = sendto.ScriptPubKey } }.ToList(), Password) { MinConfirmations = maturity, FeeType = FeeType.Medium }; Transaction trx = (scSender.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); // broadcast to the other node scSender.FullNode.NodeService <SmartContractWalletController>().SendTransaction(new SendTransactionRequest(trx.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => scReceiver.CreateRPCClient().GetRawMempool().Length > 0); TestHelper.WaitLoop(() => scReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); var receivetotal = scReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(scReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // generate two new blocks do the trx is confirmed scSender.AddToStratisMempool(scSender.FullNode.Network.CreateTransaction(trx.ToBytes())); scSender.GenerateStratisWithMiner(1); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(scSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); TestHelper.WaitLoop(() => maturity + 6 == scReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); } }
public void WalletCanReorg() { // This test has 4 parts: // Send first transaction from one wallet to another and wait for it to be confirmed // Send a second transaction and wait for it to be confirmed // Connect to a longer chain that causes a reorg so that the second trasnaction is undone // Mine the second transaction back in to the main chain using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisSender = builder.CreateStratisPowNode(this.network).WithWallet().Start(); CoreNode stratisReceiver = builder.CreateStratisPowNode(this.network).WithWallet().Start(); CoreNode stratisReorg = builder.CreateStratisPowNode(this.network).WithWallet().Start(); int maturity = (int)stratisSender.FullNode.Network.Consensus.CoinbaseMaturity; TestHelper.MineBlocks(stratisSender, maturity + 1 + 15); int currentBestHeight = maturity + 1 + 15; // The mining should add coins to the wallet. long total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 16 * 50, total); // Sync all nodes. TestHelper.ConnectAndSync(stratisReceiver, stratisSender); TestHelper.ConnectAndSync(stratisReceiver, stratisReorg); TestHelper.ConnectAndSync(stratisSender, stratisReorg); // Build Transaction 1. // Send coins to the receiver. HdAddress sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // Broadcast to the other node. stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); // Wait for the transaction to arrive. TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), null, false)); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); long receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // Generate two new blocks so the transaction is confirmed. TestHelper.MineBlocks(stratisSender, 1); int transaction1MinedHeight = currentBestHeight + 1; TestHelper.MineBlocks(stratisSender, 1); currentBestHeight = currentBestHeight + 2; // Wait for block repo for block sync to work. TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); TestBase.WaitLoop(() => transaction1MinedHeight == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // Build Transaction 2. // Remove the reorg node. TestHelper.Disconnect(stratisReceiver, stratisReorg); TestHelper.Disconnect(stratisSender, stratisReorg); ChainedHeader forkblock = stratisReceiver.FullNode.ChainIndexer.Tip; // Send more coins to the wallet sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction transaction2 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 101)); stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction2.GetHash(), null, false)); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); long newamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 110, newamount); Assert.Contains(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName), b => b.Transaction.BlockHeight == null); // Mine more blocks so it gets included in the chain. TestHelper.MineBlocks(stratisSender, 1); int transaction2MinedHeight = currentBestHeight + 1; TestHelper.MineBlocks(stratisSender, 1); currentBestHeight = currentBestHeight + 2; TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); // Create a reorg by mining on two different chains. // Advance both chains, one chain is longer. TestHelper.MineBlocks(stratisSender, 2); TestHelper.MineBlocks(stratisReorg, 10); currentBestHeight = forkblock.Height + 10; // Connect the reorg chain. TestHelper.Connect(stratisReceiver, stratisReorg); TestHelper.Connect(stratisSender, stratisReorg); // Wait for the chains to catch up. TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg, true)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); // Ensure wallet reorg completes. TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().WalletTipHash == stratisReorg.CreateRPCClient().GetBestBlockHash()); // Check the wallet amount was rolled back. long newtotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(receivetotal, newtotal); TestBase.WaitLoop(() => maturity + 1 + 16 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // ReBuild Transaction 2. // After the reorg transaction2 was returned back to mempool. stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); // Mine the transaction again. TestHelper.MineBlocks(stratisSender, 1); transaction2MinedHeight = currentBestHeight + 1; TestHelper.MineBlocks(stratisSender, 1); currentBestHeight = currentBestHeight + 2; TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); long newsecondamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(newamount, newsecondamount); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); } }
protected override void BeforeTest() { this.nodeBuilder = NodeBuilder.Create(Path.Combine(this.GetType().Name, this.CurrentTest.DisplayName)); this.network = KnownNetworks.RegTest; }
public async Task ReorgChainFailsFullValidationReconnectOldChainConnectedAsync() { using (var builder = NodeBuilder.Create(this)) { var bitcoinNoValidationRulesNetwork = new BitcoinRegTestNoValidationRules(); var minerA = builder.CreateStratisPowNode(this.powNetwork, "cmfr-1-minerA").WithDummyWallet().WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner); var minerB = builder.CreateStratisPowNode(bitcoinNoValidationRulesNetwork, "cmfr-1-minerB").NoValidation().WithDummyWallet().Start(); ChainedHeader minerBChainTip = null; bool interceptorsEnabled = false; bool minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain = false; bool minerA_IsConnecting_To_MinerBChain = false; bool minerA_Disconnected_MinerBsChain = false; bool minerA_Reconnected_Its_OwnChain = false; // Configure the interceptor to intercept when Miner A connects Miner B's chain. void interceptorConnect(ChainedHeaderBlock chainedHeaderBlock) { if (!interceptorsEnabled) { return; } if (!minerA_IsConnecting_To_MinerBChain) { if (chainedHeaderBlock.ChainedHeader.Height == 12) { minerA_IsConnecting_To_MinerBChain = minerA.FullNode.ConsensusManager().Tip.HashBlock == minerBChainTip.GetAncestor(12).HashBlock; } return; } if (!minerA_Reconnected_Its_OwnChain) { if (chainedHeaderBlock.ChainedHeader.Height == 14) { minerA_Reconnected_Its_OwnChain = true; } return; } } // Configure the interceptor to intercept when Miner A disconnects Miner B's chain after the reorg. void interceptorDisconnect(ChainedHeaderBlock chainedHeaderBlock) { if (!interceptorsEnabled) { return; } if (!minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain) { if (minerA.FullNode.ConsensusManager().Tip.Height == 10) { minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain = true; } return; } if (!minerA_Disconnected_MinerBsChain) { if (minerA.FullNode.ConsensusManager().Tip.Height == 10) { minerA_Disconnected_MinerBsChain = true; } return; } } minerA.Start(); minerA.SetConnectInterceptor(interceptorConnect); minerA.SetDisconnectInterceptor(interceptorDisconnect); // Miner B syncs with Miner A TestHelper.ConnectAndSync(minerB, minerA); // Disable Miner A from sending blocks to Miner B TestHelper.DisableBlockPropagation(minerA, minerB); // Miner A continues to mine to height 14 TestHelper.MineBlocks(minerA, 4); TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14); Assert.Equal(10, minerB.FullNode.ConsensusManager().Tip.Height); // Enable the interceptors so that they are active during the reorg. interceptorsEnabled = true; // Miner B mines 5 more blocks: // Block 6,7,9,10 = valid // Block 8 = invalid minerBChainTip = await TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(13, (coreNode, block) => BlockBuilder.InvalidCoinbaseReward(coreNode, block)).BuildAsync(); Assert.Equal(15, minerBChainTip.Height); Assert.Equal(15, minerB.FullNode.ConsensusManager().Tip.Height); // Wait until Miner A disconnected its own chain so that it can connect to // Miner B's longer chain. TestBase.WaitLoop(() => minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain); // Wait until Miner A has connected Miner B's chain (but failed) TestBase.WaitLoop(() => minerA_IsConnecting_To_MinerBChain); // Wait until Miner A has disconnected Miner B's invalid chain. TestBase.WaitLoop(() => minerA_Disconnected_MinerBsChain); // Wait until Miner A has reconnected its own chain. TestBase.WaitLoop(() => minerA_Reconnected_Its_OwnChain); } }