public void ContractTransaction_Invalid_MethodParamSerialization() { // Create poorly serialized method params var serializer = new CallDataSerializer(new ContractPrimitiveSerializer(this.node1.CoreNode.FullNode.Network)); var txData = serializer.Serialize(new ContractTxData(1, 1, (Gas)(GasPriceList.BaseCost + 1), new uint160(1), "Test")); var random = new Random(); byte[] bytes = new byte[101]; random.NextBytes(bytes); // Last 4 bytes are 0000. Remove and replace with garbage var garbageTxData = new byte[txData.Length - 4 + bytes.Length]; txData.CopyTo(garbageTxData, 0); bytes.CopyTo(garbageTxData, txData.Length - 4); // Send fails - doesn't even make it to mempool Result <WalletSendTransactionModel> result = this.node1.SendTransaction(new Script(garbageTxData), 25); Assert.True(result.IsFailure); Assert.Equal("Invalid ContractTxData format", result.Error); // TODO: const error message }
public async Task Create_NoSignature_Mempool_Rejects() { using (SignedPoAMockChain chain = new SignedPoAMockChain(2).Build()) { MockChainNode node1 = chain.Nodes[0]; MockChainNode node2 = chain.Nodes[1]; this.SetupNodes(chain, node1, node2); // Create a valid transaction. byte[] toSend = new CSharpContractSigner(new ContractSigner()).PackageSignedCSharpFile(this.network.SigningContractPrivKey, "SmartContracts/StorageDemo.cs"); var buildResult = node1.BuildCreateContractTransaction(toSend, 0); // Replace the SC output ScriptPubKey with an invalid one. Transaction tx = node1.CoreNode.FullNode.Network.CreateTransaction(buildResult.Hex); TxOut txOut = tx.TryGetSmartContractTxOut(); byte[] contractBytes = ContractCompiler.CompileFile("SmartContracts/Auction.cs").Compilation; var serializer = new CallDataSerializer(new ContractPrimitiveSerializer(this.network)); byte[] newScript = serializer.Serialize(new ContractTxData(1, SmartContractFormatLogic.GasLimitMaximum, (RuntimeObserver.Gas)SmartContractMempoolValidator.MinGasPrice, contractBytes)); txOut.ScriptPubKey = new Script(newScript); var broadcasterManager = node1.CoreNode.FullNode.NodeService <IBroadcasterManager>(); // Try and broadcast invalid tx. await broadcasterManager.BroadcastTransactionAsync(tx); // Give it enough time to reach if it was valid. Thread.Sleep(3000); // Nothing arrives. Assert.Empty(node1.CoreNode.CreateRPCClient().GetRawMempool()); // If we were to send a valid one the mempool increases. buildResult = node1.BuildCreateContractTransaction(toSend, 0); tx = node1.CoreNode.FullNode.Network.CreateTransaction(buildResult.Hex); await broadcasterManager.BroadcastTransactionAsync(tx); node1.WaitMempoolCount(1); } }
public void SendAndReceiveSmartContractTransactions() { NetworkRegistration.Register(new SmartContractsRegTest()); using (SmartContractNodeBuilder builder = SmartContractNodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPowNode().WithWallet().Start(); CoreNode scReceiver = builder.CreateSmartContractPowNode().WithWallet().Start(); var callDataSerializer = new CallDataSerializer(new ContractPrimitiveSerializer(scSender.FullNode.Network)); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; HdAddress senderAddress = TestHelper.MineBlocks(scSender, maturity + 5).AddressUsed; // The mining should add coins to the wallet. int spendableBlocks = GetSpendableBlocks(maturity + 5, maturity); var total = scSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * spendableBlocks * 50, total); // Create a token contract. ulong gasPrice = SmartContractMempoolValidator.MinGasPrice; int vmVersion = 1; Gas gasLimit = (Gas)(SmartContractFormatRule.GasLimitMaximum / 2); ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/TransferTest.cs"); Assert.True(compilationResult.Success); var contractTxData = new ContractTxData(vmVersion, gasPrice, gasLimit, compilationResult.Compilation); var contractCreateScript = new Script(callDataSerializer.Serialize(contractTxData)); var txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), MinConfirmations = maturity, TransactionFee = new Money(1, MoneyUnit.BTC), FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; 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 to sync. TestHelper.MineBlocks(scSender, 1); // Sync to the receiver node. TestHelper.ConnectAndSync(scSender, scReceiver); // Ensure that both nodes have 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/TransferTest.cs"); Assert.True(compilationResult.Success); contractTxData = new ContractTxData(vmVersion, gasPrice, gasLimit, compilationResult.Compilation); contractCreateScript = new Script(callDataSerializer.Serialize(contractTxData)); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), MinConfirmations = maturity, TransactionFee = new Money(1, MoneyUnit.BTC), FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 0, ScriptPubKey = contractCreateScript } }.ToList() }; // 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); TestHelper.MineBlocks(scSender, 1); // Ensure both nodes are synced with each other. TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(scReceiver, scSender)); // Ensure that both nodes have the contract. senderState = scSender.FullNode.NodeService <IStateRepositoryRoot>(); receiverState = scReceiver.FullNode.NodeService <IStateRepositoryRoot>(); tokenContractAddress = addressGenerator.GenerateAddress(transferContractTransaction.GetHash(), 0); 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(callDataSerializer.Serialize(contractTxData)); txBuildContext = new TransactionBuildContext(scSender.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, AccountName), MinConfirmations = maturity, TransactionFee = new Money(1, MoneyUnit.BTC), FeeType = FeeType.High, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = 1000, ScriptPubKey = contractCallScript } }.ToList() }; // 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. 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 void SmartContracts_AddToMempool_OnlyValid() { using (SmartContractNodeBuilder builder = SmartContractNodeBuilder.Create(this)) { var stratisNodeSync = builder.CreateSmartContractPowNode().WithWallet().Start(); var callDataSerializer = new CallDataSerializer(new ContractPrimitiveSerializer(stratisNodeSync.FullNode.Network)); TestHelper.MineBlocks(stratisNodeSync, 105); // coinbase maturity = 100 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 var tx = stratisNodeSync.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); var contractTxData = new ContractTxData(1, 100, new Gas(10_000_000), new uint160(0), "Test"); tx.AddOutput(new TxOut(1, new Script(callDataSerializer.Serialize(contractTxData)))); 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 = stratisNodeSync.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), new Script(new[] { (byte)ScOpcodeType.OP_SPEND }))); tx.AddOutput(new TxOut(1, new Script(callDataSerializer.Serialize(contractTxData)))); stratisNodeSync.Broadcast(tx); // 2 smart contract outputs tx = stratisNodeSync.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); tx.AddOutput(new TxOut(1, new Script(callDataSerializer.Serialize(contractTxData)))); tx.AddOutput(new TxOut(1, new Script(callDataSerializer.Serialize(contractTxData)))); tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); stratisNodeSync.Broadcast(tx); // Send to contract uint160 contractAddress = new uint160(123); var state = stratisNodeSync.FullNode.NodeService <IStateRepositoryRoot>(); state.CreateAccount(contractAddress); state.Commit(); tx = stratisNodeSync.FullNode.Network.CreateTransaction(); 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); // Gas price lower than minimum tx = stratisNodeSync.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); var lowGasPriceContractTxData = new ContractTxData(1, SmartContractMempoolValidator.MinGasPrice - 1, new Gas(SmartContractFormatRule.GasLimitMaximum), new uint160(0), "Test"); tx.AddOutput(new TxOut(1, new Script(callDataSerializer.Serialize(lowGasPriceContractTxData)))); 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 = stratisNodeSync.FullNode.Network.CreateTransaction(); 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 GetHistoryWithValidModelWithoutTransactionSpendingDetailsReturnsWalletHistoryModel() { ulong gasPrice = SmartContractMempoolValidator.MinGasPrice; int vmVersion = 1; Gas gasLimit = (Gas)(SmartContractFormatRule.GasLimitMaximum / 2); var contractTxData = new ContractTxData(vmVersion, gasPrice, gasLimit, new byte[] { 0, 1, 2, 3 }); var callDataSerializer = new CallDataSerializer(new ContractPrimitiveSerializer(new SmartContractsRegTest())); var contractCreateScript = new Script(callDataSerializer.Serialize(contractTxData)); string walletName = "myWallet"; HdAddress address = WalletTestsHelpers.CreateAddress(); TransactionData normalTransaction = WalletTestsHelpers.CreateTransaction(new uint256(1), new Money(500000), 1); TransactionData createTransaction = WalletTestsHelpers.CreateTransaction(new uint256(1), new Money(500000), 1); createTransaction.SpendingDetails = new SpendingDetails { BlockHeight = 100, CreationTime = DateTimeOffset.Now, TransactionId = uint256.One, Payments = new List <PaymentDetails> { new PaymentDetails { Amount = new Money(100000), DestinationScriptPubKey = contractCreateScript } } }; address.Transactions.Add(normalTransaction); address.Transactions.Add(createTransaction); var addresses = new List <HdAddress> { address }; Features.Wallet.Wallet wallet = WalletTestsHelpers.CreateWallet(walletName); var account = new HdAccount { ExternalAddresses = addresses }; wallet.AccountsRoot.Add(new AccountRoot() { Accounts = new List <HdAccount> { account } }); List <FlatHistory> flat = addresses.SelectMany(s => s.Transactions.Select(t => new FlatHistory { Address = s, Transaction = t })).ToList(); var accountsHistory = new List <AccountHistory> { new AccountHistory { History = flat, Account = account } }; this.walletManager.Setup(w => w.GetHistory(walletName, It.IsAny <string>())).Returns(accountsHistory); this.walletManager.Setup(w => w.GetWalletByName(walletName)).Returns(wallet); this.walletManager.Setup(w => w.GetAccounts(walletName)).Returns(new List <HdAccount> { account }); this.receiptRepository.Setup(x => x.Retrieve(It.IsAny <uint256>())) .Returns(new Receipt(null, 0, new Log[0], null, null, null, uint160.Zero, true, null)); this.callDataSerializer.Setup(x => x.Deserialize(It.IsAny <byte[]>())) .Returns(Result.Ok(new ContractTxData(0, 0, (Gas)0, new uint160(0), null, null))); var controller = new SmartContractWalletController( this.broadcasterManager.Object, this.callDataSerializer.Object, this.connectionManager.Object, this.loggerFactory.Object, this.network, this.receiptRepository.Object, this.walletManager.Object); IActionResult result = controller.GetHistory(walletName, address.Address); var viewResult = Assert.IsType <JsonResult>(result); var model = viewResult.Value as IEnumerable <ContractTransactionItem>; Assert.NotNull(model); Assert.Equal(3, model.Count()); ContractTransactionItem resultingTransaction = model.ElementAt(2); ContractTransactionItem resultingCreate = model.ElementAt(0); Assert.Equal(ContractTransactionItemType.ContractCreate, resultingCreate.Type); Assert.Equal(createTransaction.SpendingDetails.TransactionId, resultingCreate.Hash); Assert.Equal(createTransaction.SpendingDetails.Payments.First().Amount.ToUnit(MoneyUnit.BTC), resultingCreate.Amount); Assert.Equal(uint160.Zero.ToBase58Address(this.network), resultingCreate.To); Assert.Equal((uint)createTransaction.SpendingDetails.BlockHeight, resultingCreate.BlockHeight); Assert.Equal(ContractTransactionItemType.Received, resultingTransaction.Type); Assert.Equal(address.Address, resultingTransaction.To); Assert.Equal(normalTransaction.Id, resultingTransaction.Hash); Assert.Equal(normalTransaction.Amount.ToUnit(MoneyUnit.BTC), resultingTransaction.Amount); Assert.Equal((uint)1, resultingTransaction.BlockHeight); }
public void GetHistoryWithValidModelWithSkipAndTakeReturnsWalletHistoryModel() { ulong gasPrice = SmartContractMempoolValidator.MinGasPrice; int vmVersion = 1; var gasLimit = (Stratis.SmartContracts.RuntimeObserver.Gas)(SmartContractFormatLogic.GasLimitMaximum / 2); var contractTxData = new ContractTxData(vmVersion, gasPrice, gasLimit, new byte[] { 0, 1, 2, 3 }); var callDataSerializer = new CallDataSerializer(new ContractPrimitiveSerializer(new SmartContractsRegTest())); var contractCreateScript = new Script(callDataSerializer.Serialize(contractTxData)); string walletName = "myWallet"; HdAddress address = WalletTestsHelpers.CreateAddress(); const int totalHistoryLength = 100; const int toSkip = 10; const int toTake = 10; for (int i = 0; i < totalHistoryLength; i++) { TransactionData createTransaction = WalletTestsHelpers.CreateTransaction(new uint256((ulong)i), new Money(500000), 100 + i); createTransaction.SpendingDetails = new SpendingDetails { BlockHeight = 100 + i, CreationTime = DateTimeOffset.Now, TransactionId = new uint256((ulong)i), Payments = new List <PaymentDetails> { new PaymentDetails { Amount = new Money(100000), DestinationScriptPubKey = contractCreateScript } } }; address.Transactions.Add(createTransaction); } var addresses = new List <HdAddress> { address }; Features.Wallet.Wallet wallet = WalletTestsHelpers.CreateWallet(walletName); var account = new HdAccount { ExternalAddresses = addresses }; wallet.AccountsRoot.Add(new AccountRoot() { Accounts = new List <HdAccount> { account } }); List <FlatHistory> flat = addresses.SelectMany(s => s.Transactions.Select(t => new FlatHistory { Address = s, Transaction = t })).ToList(); var accountsHistory = new List <AccountHistory> { new AccountHistory { History = flat, Account = account } }; this.walletManager.Setup(w => w.GetHistory(walletName, It.IsAny <string>())).Returns(accountsHistory); this.walletManager.Setup(w => w.GetWalletByName(walletName)).Returns(wallet); this.walletManager.Setup(w => w.GetAccounts(walletName)).Returns(new List <HdAccount> { account }); var receipt = new Receipt(null, 12345, new Log[0], null, null, null, uint160.Zero, true, null, null, 2, 100000); var receiptList = new List <Receipt>(); for (int i = 0; i < totalHistoryLength; i++) { receiptList.Add(receipt); } this.receiptRepository.Setup(x => x.RetrieveMany(It.IsAny <IList <uint256> >())) .Returns(receiptList); this.callDataSerializer.Setup(x => x.Deserialize(It.IsAny <byte[]>())) .Returns(Result.Ok(new ContractTxData(0, 0, (Stratis.SmartContracts.RuntimeObserver.Gas) 0, new uint160(0), null, null))); var controller = new SmartContractWalletController( this.broadcasterManager.Object, this.callDataSerializer.Object, this.connectionManager.Object, this.loggerFactory.Object, this.network, this.receiptRepository.Object, this.walletManager.Object, this.smartContractTransactionService.Object); var request = new GetHistoryRequest { Address = address.Address, WalletName = walletName, Skip = toSkip, Take = toTake }; IActionResult result = controller.GetHistory(request); JsonResult viewResult = Assert.IsType <JsonResult>(result); var model = viewResult.Value as IEnumerable <ContractTransactionItem>; Assert.NotNull(model); Assert.Equal(toTake, model.Count()); Assert.Equal(new uint256(toSkip), model.ElementAt(toTake - 1).Hash); Assert.Equal(new uint256(toSkip + toTake - 1), model.ElementAt(0).Hash); }
public void SendAndReceiveSmartContractTransactionsOnPosNetwork() { using (SmartContractNodeBuilder builder = SmartContractNodeBuilder.Create(this)) { CoreNode scSender = builder.CreateSmartContractPosNode().WithWallet().Start(); CoreNode scReceiver = builder.CreateSmartContractPosNode().WithWallet().Start(); var callDataSerializer = new CallDataSerializer(new ContractPrimitiveSerializer(scSender.FullNode.Network)); var maturity = (int)scSender.FullNode.Network.Consensus.CoinbaseMaturity; HdAddress senderAddress = TestHelper.MineBlocks(scSender, maturity + 5).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(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 TestHelper.ConnectAndSync(scSender, scReceiver); // 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(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(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)); } }