public void SendAndReceiveSmartContractTransactionsOnPosNetwork() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPosNode(); CoreNode scReceiver = builder.CreateSmartContractPosNode(); builder.StartAll(); scSender.NotInIBD(); scReceiver.NotInIBD(); scSender.FullNode.WalletManager().CreateWallet(Password, WalletName, Passphrase); scReceiver.FullNode.WalletManager().CreateWallet(Password, WalletName, Passphrase); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; HdAddress senderAddress = TestHelper.MineBlocks(scSender, WalletName, Password, AccountName, maturity + 5).AddressUsed; // 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 * 6 * 50, total); // Create a token contract ulong gasPrice = 1; int vmVersion = 1; Gas gasLimit = (Gas)5000; SmartContractCompilationResult compilationResult = SmartContractCompiler.CompileFile("SmartContracts/TransferTestPos.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) { 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 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 IContractStateRoot senderState = scSender.FullNode.NodeService <IContractStateRoot>(); IContractStateRoot receiverState = scReceiver.FullNode.NodeService <IContractStateRoot>(); 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 = SmartContractCompiler.CompileFile("SmartContracts/TransferTestPos.cs"); Assert.True(compilationResult.Success); contractCarrier = SmartContractCarrier.CreateContract(vmVersion, compilationResult.Compilation, gasPrice, gasLimit); contractCreateScript = new Script(contractCarrier.Serialize()); 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); 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)); 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 contractCarrier = SmartContractCarrier.CallContract(1, tokenContractAddress, "Test", gasPrice, gasLimit); Script contractCallScript = new Script(contractCarrier.Serialize()); 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); 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 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>(() => { Task.Delay(2).Wait(); 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>(() => { Task.Delay(2).Wait(); 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>(() => { Task.Delay(2).Wait(); 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); } }