Esempio n. 1
0
        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);
            }
        }
Esempio n. 3
0
        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);
            }
        }
Esempio n. 5
0
        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));
            }
        }