Example #1
0
        public BuildCallContractTransactionResponse CreateSuccessfulBuildCallContractTransactionResponse()
        {
            var transaction = CreateEmpty();

            return(BuildCallContractTransactionResponse.Succeeded("methodNameDoesNotMatter", transaction, new Money(500_000_000)));
        }
Example #2
0
        private BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request)
        {
            this.logger.LogTrace(request.ToString());

            AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender);

            if (addressBalance.AmountConfirmed == 0)
            {
                return(BuildCallContractTransactionResponse.Failed($"The 'Sender' address you're trying to spend from doesn't have a confirmed balance. Current unconfirmed balance: {addressBalance.AmountUnconfirmed}. Please check the 'Sender' address."));
            }

            var selectedInputs = new List <OutPoint>();

            selectedInputs = this.walletManager.GetSpendableTransactionsInWallet(request.WalletName, MinConfirmationsAllChecks).Where(x => x.Address.Address == request.Sender).Select(x => x.ToOutPoint()).ToList();

            ulong   gasPrice       = ulong.Parse(request.GasPrice);
            ulong   gasLimit       = ulong.Parse(request.GasLimit);
            uint160 addressNumeric = new Address(request.ContractAddress).ToUint160(this.network);

            SmartContractCarrier carrier;

            if (request.Parameters != null && request.Parameters.Any())
            {
                carrier = SmartContractCarrier.CallContract(ReflectionVirtualMachine.VmVersion, addressNumeric, request.MethodName, gasPrice, new Gas(gasLimit), request.Parameters);
            }
            else
            {
                carrier = SmartContractCarrier.CallContract(ReflectionVirtualMachine.VmVersion, addressNumeric, request.MethodName, gasPrice, new Gas(gasLimit));
            }

            HdAddress senderAddress = null;

            if (!string.IsNullOrWhiteSpace(request.Sender))
            {
                Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName);
                HdAccount account             = wallet.GetAccountByCoinType(request.AccountName, this.coinType);
                senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender);
            }

            ulong totalFee = (gasPrice * gasLimit) + Money.Parse(request.FeeAmount);
            var   context  = new TransactionBuildContext(this.network)
            {
                AccountReference = new WalletAccountReference(request.WalletName, request.AccountName),
                TransactionFee   = totalFee,
                ChangeAddress    = senderAddress,
                SelectedInputs   = selectedInputs,
                MinConfirmations = MinConfirmationsAllChecks,
                WalletPassword   = request.Password,
                Recipients       = new[] { new Recipient {
                                               Amount = request.Amount, ScriptPubKey = new Script(carrier.Serialize())
                                           } }.ToList()
            };

            try
            {
                Transaction transaction = this.walletTransactionHandler.BuildTransaction(context);
                return(BuildCallContractTransactionResponse.Succeeded(request.MethodName, transaction, context.TransactionFee));
            }
            catch (Exception exception)
            {
                return(BuildCallContractTransactionResponse.Failed(exception.Message));
            }
        }
        public void InternalTransfer_Create_WithValueTransfer()
        {
            // Ensure fixture is funded.
            this.mockChain.MineBlocks(1);

            // Deploy contract
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/CreationTransfer.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            decimal amount = 25;
            Money   senderBalanceBefore = this.node1.WalletSpendableBalance;
            uint256 currentHash         = this.node1.GetLastBlock().GetHash();

            // Send amount to contract, which will send to new address of contract it creates
            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(
                nameof(CreationTransfer.CreateAnotherContract),
                preResponse.NewContractAddress,
                amount);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // Get created contract address - TODO FIX
            uint160 createdAddress = this.addressGenerator.GenerateAddress(response.TransactionId, 0);

            // Block contains a condensing transaction
            Assert.Equal(3, lastBlock.Transactions.Count);
            Transaction condensingTransaction = lastBlock.Transactions[2];

            Assert.Single(condensingTransaction.Outputs); // Entire balance was forwarded,
            byte[] toBytes = condensingTransaction.Outputs[0].ScriptPubKey.ToBytes();
            Assert.Equal((byte)ScOpcodeType.OP_INTERNALCONTRACTTRANSFER, toBytes[0]);
            uint160 toAddress = new uint160(toBytes.Skip(1).ToArray());

            Assert.Equal(createdAddress, toAddress);
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC), condensingTransaction.Outputs[0].Value);

            // Contract doesn't maintain any balance
            Assert.Equal((ulong)0, this.node1.GetContractBalance(preResponse.NewContractAddress));

            // Created contract received full amount
            Assert.Equal((ulong)new Money((ulong)amount, MoneyUnit.BTC), this.node1.GetContractBalance(createdAddress.ToBase58Address(this.node1.CoreNode.FullNode.Network)));

            // Receipt is correct
            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.Equal(lastBlock.GetHash().ToString(), receipt.BlockHash);
            Assert.Equal(response.TransactionId.ToString(), receipt.TransactionHash);
            Assert.Empty(receipt.Logs); // TODO: Could add logs to this test
            Assert.True(receipt.Success);
            Assert.True(receipt.GasUsed > GasPriceList.BaseCost);
            Assert.Null(receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.Null(receipt.Error);
            Assert.Equal(preResponse.NewContractAddress, receipt.To);
        }
Example #4
0
        public BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request)
        {
            AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender);

            if (addressBalance.AmountConfirmed == 0 && addressBalance.AmountUnconfirmed == 0)
            {
                return(BuildCallContractTransactionResponse.Failed(SenderNoBalanceError));
            }

            var selectedInputs = new List <OutPoint>();

            selectedInputs = this.walletManager.GetSpendableInputsForAddress(request.WalletName, request.Sender);

            uint160 addressNumeric = request.ContractAddress.ToUint160(this.network);

            ContractTxData txData;

            if (request.Parameters != null && request.Parameters.Any())
            {
                try
                {
                    object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters);
                    txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, addressNumeric, request.MethodName, methodParameters);
                }
                catch (MethodParameterStringSerializerException exception)
                {
                    return(BuildCallContractTransactionResponse.Failed(exception.Message));
                }
            }
            else
            {
                txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, addressNumeric, request.MethodName);
            }

            HdAddress senderAddress = null;

            if (!string.IsNullOrWhiteSpace(request.Sender))
            {
                Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName);
                HdAccount account             = wallet.GetAccount(request.AccountName);
                if (account == null)
                {
                    return(BuildCallContractTransactionResponse.Failed($"No account with the name '{request.AccountName}' could be found."));
                }

                senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender);
            }

            ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount);
            var   context  = new TransactionBuildContext(this.network)
            {
                AccountReference = new WalletAccountReference(request.WalletName, request.AccountName),
                TransactionFee   = totalFee,
                ChangeAddress    = senderAddress,
                SelectedInputs   = selectedInputs,
                MinConfirmations = MinConfirmationsAllChecks,
                WalletPassword   = request.Password,
                Recipients       = new[] { new Recipient {
                                               Amount = request.Amount, ScriptPubKey = new Script(this.callDataSerializer.Serialize(txData))
                                           } }.ToList()
            };

            try
            {
                Transaction transaction = this.walletTransactionHandler.BuildTransaction(context);
                return(BuildCallContractTransactionResponse.Succeeded(request.MethodName, transaction, context.TransactionFee));
            }
            catch (Exception exception)
            {
                return(BuildCallContractTransactionResponse.Failed(exception.Message));
            }
        }
        public void InternalTransfer_BetweenContracts()
        {
            // Ensure fixture is funded.
            this.mockChain.MineBlocks(1);

            // Deploy contract to send to
            ContractCompilationResult receiveCompilationResult = ContractCompiler.CompileFile("SmartContracts/NestedCallsReceiver.cs");

            Assert.True(receiveCompilationResult.Success);
            BuildCreateContractTransactionResponse receiveResponse = this.node1.SendCreateContractTransaction(receiveCompilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(receiveResponse.NewContractAddress));

            // Deploy contract to send from
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/NestedCallsStarter.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            decimal amount = 25;
            Money   senderBalanceBefore = this.node1.WalletSpendableBalance;
            uint256 currentHash         = this.node1.GetLastBlock().GetHash();

            string[] parameters = new string[]
            {
                string.Format("{0}#{1}", (int)MethodParameterDataType.Address, receiveResponse.NewContractAddress)
            };

            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(nameof(NestedCallsStarter.Start), preResponse.NewContractAddress, amount, parameters);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // Storage set correctly
            Assert.Equal(BitConverter.GetBytes(NestedCallsStarter.Return), this.node1.GetStorageValue(preResponse.NewContractAddress, NestedCallsStarter.Key));

            // Block contains a condensing transaction
            Assert.Equal(3, lastBlock.Transactions.Count);
            Transaction condensingTransaction = lastBlock.Transactions[2];

            Assert.Equal(2, condensingTransaction.Outputs.Count);

            // 1 output which is starting contract
            byte[] toBytes = condensingTransaction.Outputs[0].ScriptPubKey.ToBytes();
            Assert.Equal((byte)ScOpcodeType.OP_INTERNALCONTRACTTRANSFER, toBytes[0]);
            uint160 toAddress = new uint160(toBytes.Skip(1).ToArray());

            Assert.Equal(preResponse.NewContractAddress, toAddress.ToBase58Address(this.node1.CoreNode.FullNode.Network));

            // Received 1/2 the sent funds + 1/2 of those funds
            Money transferAmount1 = new Money((long)amount, MoneyUnit.BTC) / 2;
            Money transferAmount2 = new Money((long)amount, MoneyUnit.BTC) / 4;

            Assert.Equal(transferAmount1 + transferAmount2, condensingTransaction.Outputs[0].Value);
            Assert.Equal((ulong)(transferAmount1 + transferAmount2), this.node1.GetContractBalance(preResponse.NewContractAddress));

            // 1 output to other deployed contract
            toBytes = condensingTransaction.Outputs[1].ScriptPubKey.ToBytes();
            Assert.Equal((byte)ScOpcodeType.OP_INTERNALCONTRACTTRANSFER, toBytes[0]);
            toAddress = new uint160(toBytes.Skip(1).ToArray());
            Assert.Equal(receiveResponse.NewContractAddress, toAddress.ToBase58Address(this.node1.CoreNode.FullNode.Network));

            // Received 1/2 the sent funds, but sent 1/2 of those funds back
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC) - (transferAmount1 + transferAmount2), condensingTransaction.Outputs[1].Value);
            Assert.Equal((ulong)(new Money((long)amount, MoneyUnit.BTC) - (transferAmount1 + transferAmount2)), this.node1.GetContractBalance(receiveResponse.NewContractAddress));
        }
        public void InternalTransfer_ToWalletAddress()
        {
            // Ensure fixture is funded.
            this.mockChain.MineBlocks(1);

            // Deploy contract
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/BasicTransfer.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            decimal amount = 25;
            Money   senderBalanceBefore = this.node1.WalletSpendableBalance;
            uint256 currentHash         = this.node1.GetLastBlock().GetHash();

            // Send amount to contract, which will send to wallet address (address without code)
            uint160 walletUint160 = new uint160(1);
            string  address       = walletUint160.ToBase58Address(this.node1.CoreNode.FullNode.Network);

            string[] parameters = new string[] { string.Format("{0}#{1}", (int)MethodParameterDataType.Address, address) };
            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(
                nameof(BasicTransfer.SendToAddress),
                preResponse.NewContractAddress,
                amount,
                parameters);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // Block contains a condensing transaction
            Assert.Equal(3, lastBlock.Transactions.Count);
            Transaction condensingTransaction = lastBlock.Transactions[2];

            Assert.Single(condensingTransaction.Outputs); // Entire balance was forwarded,
            uint160 transferReceiver = this.senderRetriever.GetAddressFromScript(condensingTransaction.Outputs[0].ScriptPubKey).Sender;

            Assert.Equal(walletUint160, transferReceiver);
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC), condensingTransaction.Outputs[0].Value);

            // Contract doesn't maintain any balance
            Assert.Equal((ulong)0, this.node1.GetContractBalance(preResponse.NewContractAddress));

            // Receipt is correct
            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.Equal(lastBlock.GetHash().ToString(), receipt.BlockHash);
            Assert.Equal(response.TransactionId.ToString(), receipt.TransactionHash);
            Assert.Empty(receipt.Logs); // TODO: Could add logs to this test
            Assert.True(receipt.Success);
            Assert.True(receipt.GasUsed > GasPriceList.BaseCost);
            Assert.Null(receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.Null(receipt.Error);
            Assert.Equal(preResponse.NewContractAddress, receipt.To);
        }
        public void CallContract_SerializeEachParameterType()
        {
            // Ensure fixture is funded.
            this.mockChain.MineBlocks(1);

            // Deploy contract
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/CallWithAllParameters.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            decimal amount      = 25;
            uint256 currentHash = this.node1.GetLastBlock().GetHash();

            const char   testChar    = 'c';
            string       testAddress = new uint160("0x0000000000000000000000000000000000000001").ToBase58Address(this.node1.CoreNode.FullNode.Network);
            const bool   testBool    = true;
            const int    testInt     = int.MaxValue;
            const long   testLong    = long.MaxValue;
            const uint   testUint    = uint.MaxValue;
            const ulong  testUlong   = ulong.MaxValue;
            UInt128      testUInt128 = UInt128.MaxValue;
            UInt256      testUInt256 = UInt256.MaxValue;
            const string testString  = "The quick brown fox jumps over the lazy dog";

            string[] parameters = new string[]
            {
                string.Format("{0}#{1}", (int)MethodParameterDataType.Char, testChar),
                string.Format("{0}#{1}", (int)MethodParameterDataType.Address, testAddress),
                string.Format("{0}#{1}", (int)MethodParameterDataType.Bool, testBool),
                string.Format("{0}#{1}", (int)MethodParameterDataType.Int, testInt),
                string.Format("{0}#{1}", (int)MethodParameterDataType.Long, testLong),
                string.Format("{0}#{1}", (int)MethodParameterDataType.UInt, testUint),
                string.Format("{0}#{1}", (int)MethodParameterDataType.ULong, testUlong),
                string.Format("{0}#{1}", (int)MethodParameterDataType.UInt128, testUInt128),
                string.Format("{0}#{1}", (int)MethodParameterDataType.UInt256, testUInt256),
                string.Format("{0}#{1}", (int)MethodParameterDataType.String, testString)
            };
            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(nameof(CallWithAllParameters.Call), preResponse.NewContractAddress, amount, parameters);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // Block doesn't contain any extra transactions
            Assert.Equal(2, lastBlock.Transactions.Count);

            // Contract keeps balance
            Assert.Equal((ulong)new Money((ulong)amount, MoneyUnit.BTC), this.node1.GetContractBalance(preResponse.NewContractAddress));

            // Receipt is correct
            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.Equal(lastBlock.GetHash().ToString(), receipt.BlockHash);
            Assert.Equal(response.TransactionId.ToString(), receipt.TransactionHash);
            Assert.True(receipt.Success);
            Assert.Empty(receipt.Logs);
            Assert.True(receipt.GasUsed > GasPriceList.BaseCost);
            Assert.Null(receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.Equal(preResponse.NewContractAddress, receipt.To);
            Assert.Null(receipt.Error);
        }
        public void InternalTransfer_ToContractAddress()
        {
            // Ensure fixture is funded.
            this.mockChain.MineBlocks(1);

            // Deploy contract to send to
            ContractCompilationResult receiveCompilationResult = ContractCompiler.CompileFile("SmartContracts/BasicReceive.cs");

            Assert.True(receiveCompilationResult.Success);
            BuildCreateContractTransactionResponse receiveResponse = this.node1.SendCreateContractTransaction(receiveCompilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(receiveResponse.NewContractAddress));

            // Deploy contract to send from
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/BasicTransfer.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            decimal amount = 25;
            Money   senderBalanceBefore = this.node1.WalletSpendableBalance;
            uint256 currentHash         = this.node1.GetLastBlock().GetHash();

            // Send amount to contract, which will send to contract address
            string[] parameters = new string[] { string.Format("{0}#{1}", (int)MethodParameterDataType.Address, receiveResponse.NewContractAddress) };
            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(
                nameof(BasicTransfer.SendToAddress),
                preResponse.NewContractAddress,
                amount,
                parameters);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // Contract doesn't maintain any balance
            Assert.Equal((ulong)0, this.node1.GetContractBalance(preResponse.NewContractAddress));

            // Receiver contract now has balance
            Assert.Equal((ulong)new Money((int)amount, MoneyUnit.BTC), this.node1.GetContractBalance(receiveResponse.NewContractAddress));

            // Receiver contract stored to state
            Assert.Equal(new byte[] { 1 }, this.node1.GetStorageValue(receiveResponse.NewContractAddress, BasicReceive.ReceiveKey));

            // Log was stored - bloom filter should be non-zero
            Assert.NotEqual(new Bloom(), ((ISmartContractBlockHeader)lastBlock.Header).LogsBloom);

            // Block contains a condensing transaction
            Assert.Equal(3, lastBlock.Transactions.Count);
            Transaction condensingTransaction = lastBlock.Transactions[2];

            Assert.Single(condensingTransaction.Outputs); // Entire balance was forwarded
            byte[] toBytes = condensingTransaction.Outputs[0].ScriptPubKey.ToBytes();
            Assert.Equal((byte)ScOpcodeType.OP_INTERNALCONTRACTTRANSFER, toBytes[0]);
            uint160 toAddress = new uint160(toBytes.Skip(1).ToArray());

            Assert.Equal(receiveResponse.NewContractAddress, toAddress.ToBase58Address(this.node1.CoreNode.FullNode.Network));
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC), condensingTransaction.Outputs[0].Value);

            // Receipt is correct
            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.Equal(lastBlock.GetHash().ToString(), receipt.BlockHash);
            Assert.Equal(response.TransactionId.ToString(), receipt.TransactionHash);
            Assert.Single(receipt.Logs);
            Assert.Equal(receiveResponse.NewContractAddress, receipt.Logs[0].Address);
            Assert.True(receipt.Success);
            Assert.True(receipt.GasUsed > GasPriceList.BaseCost);
            Assert.Null(receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.Null(receipt.Error);
            Assert.Equal(preResponse.NewContractAddress, receipt.To);
        }
        public void InternalTransfer_ToContractAddress()
        {
            // Deploy contract to send to
            ContractCompilationResult receiveCompilationResult = ContractCompiler.CompileFile("SmartContracts/BasicReceive.cs");

            Assert.True(receiveCompilationResult.Success);
            BuildCreateContractTransactionResponse receiveResponse = this.node1.SendCreateContractTransaction(receiveCompilationResult.Compilation, 0);

            this.node1.WaitMempoolCount(1);
            this.node1.WaitForBlocksToBeMined(1);
            Assert.NotNull(this.node1.GetCode(receiveResponse.NewContractAddress));

            // Deploy contract to send from
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/BasicTransfer.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.node1.WaitMempoolCount(1);
            this.node1.WaitForBlocksToBeMined(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            double  amount      = 25;
            uint256 currentHash = this.node1.GetLastBlock().GetHash();

            // Send amount to contract, which will send to contract address
            string[] parameters = new string[] { string.Format("{0}#{1}", (int)MethodParameterDataType.Address, receiveResponse.NewContractAddress) };
            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(
                nameof(BasicTransfer.SendToAddress),
                preResponse.NewContractAddress,
                amount,
                parameters);

            this.node2.WaitMempoolCount(1);
            this.node2.WaitForBlocksToBeMined(1);

            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // Contract doesn't maintain any balance
            Assert.Equal((ulong)0, this.node1.GetContractBalance(preResponse.NewContractAddress));

            // Receiver contract now has balance
            Assert.Equal((ulong)new Money((int)amount, MoneyUnit.BTC), this.node1.GetContractBalance(receiveResponse.NewContractAddress));

            // Receiver contract stored to state
            Assert.Equal(new byte[] { 1 }, this.node1.GetStorageValue(receiveResponse.NewContractAddress, BasicReceive.ReceiveKey));

            // Receipt is correct
            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.Equal(response.TransactionId.ToString(), receipt.TransactionHash);
            Assert.Single(receipt.Logs);
            Assert.Equal(receiveResponse.NewContractAddress, receipt.Logs[0].Address);
            Assert.True(receipt.Success);
            Assert.True(receipt.GasUsed > GasPriceList.BaseCost);
            Assert.Null(receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.Null(receipt.Error);
            Assert.Equal(preResponse.NewContractAddress, receipt.To);
        }
Example #10
0
        public void Persisting_Nothing_Null_And_Empty_Byte_Arrays_Are_The_Same()
        {
            // Demonstrates some potentially unusual behaviour when saving contract state.

            // Ensure fixture is funded.
            this.mockChain.MineBlocks(1);

            // Deploy contract
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/BehaviourTest.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            uint256 currentHash = this.node1.GetLastBlock().GetHash();

            // Call CheckData() and confirm that it succeeds
            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction(
                nameof(BehaviourTest.DataIsByte0),
                preResponse.NewContractAddress,
                0);

            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            NBitcoin.Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.True(receipt.Success);

            // Invoke PersistEmptyString. It should succeed
            response = this.node1.SendCallContractTransaction(
                nameof(BehaviourTest.PersistEmptyString),
                preResponse.NewContractAddress,
                0);
            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            receipt = this.node1.GetReceipt(response.TransactionId.ToString());
            Assert.True(receipt.Success);

            // The storage value should be null, but the contract only sees it as a byte[0]
            Assert.Null(this.node1.GetStorageValue(preResponse.NewContractAddress, nameof(BehaviourTest.Data)));

            // Now call CheckData() to confirm that it's a byte[0]
            response = this.node1.SendCallContractTransaction(
                nameof(BehaviourTest.DataIsByte0),
                preResponse.NewContractAddress,
                0);
            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            receipt = this.node1.GetReceipt(response.TransactionId.ToString());
            Assert.True(receipt.Success);

            // Now call PersistNull, which should fail if the null is not returned as byte[0]
            response = this.node1.SendCallContractTransaction(
                nameof(BehaviourTest.PersistNull),
                preResponse.NewContractAddress,
                0);
            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            lastBlock = this.node1.GetLastBlock();

            receipt = this.node1.GetReceipt(response.TransactionId.ToString());
            Assert.True(receipt.Success);

            // The storage value should be null
            Assert.Null(this.node1.GetStorageValue(preResponse.NewContractAddress, nameof(BehaviourTest.Data)));

            // Now call CheckData() to confirm that it's a byte[0]
            response = this.node1.SendCallContractTransaction(
                nameof(BehaviourTest.DataIsByte0),
                preResponse.NewContractAddress,
                0);
            this.mockChain.WaitAllMempoolCount(1);
            this.mockChain.MineBlocks(1);

            lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            receipt = this.node1.GetReceipt(response.TransactionId.ToString());
            Assert.True(receipt.Success);
        }
        public void ContractTransaction_ExceptionInCall()
        {
            // Ensure fixture is funded.
            this.node1.MineBlocks(1);

            // Deploy contract
            ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/ExceptionInMethod.cs");

            Assert.True(compilationResult.Success);
            BuildCreateContractTransactionResponse preResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0);

            this.node1.WaitMempoolCount(1);
            this.node1.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(preResponse.NewContractAddress));

            double  amount = 25;
            Money   senderBalanceBefore = this.node1.WalletSpendableBalance;
            uint256 currentHash         = this.node1.GetLastBlock().GetHash();

            BuildCallContractTransactionResponse response = this.node1.SendCallContractTransaction("Method", preResponse.NewContractAddress, amount);

            this.node2.WaitMempoolCount(1);
            this.node2.MineBlocks(1);
            Block lastBlock = this.node1.GetLastBlock();

            // Blocks progressed
            Assert.NotEqual(currentHash, lastBlock.GetHash());

            // State wasn't persisted
            Assert.Null(this.node2.GetStorageValue(preResponse.NewContractAddress, "Test"));

            // Logs weren't persisted
            Assert.Equal(new Bloom(), ((SmartContractBlockHeader)lastBlock.Header).LogsBloom);

            // Block contains a refund transaction
            Assert.Equal(3, lastBlock.Transactions.Count);
            Transaction refundTransaction = lastBlock.Transactions[2];

            Assert.Single(refundTransaction.Outputs); // No transfers persisted
            uint160 refundReceiver = this.senderRetriever.GetAddressFromScript(refundTransaction.Outputs[0].ScriptPubKey).Sender;

            Assert.Equal(this.node1.MinerAddress.Address, refundReceiver.ToAddress(this.mockChain.Network).Value);
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC), refundTransaction.Outputs[0].Value);
            Money fee = lastBlock.Transactions[0].Outputs[0].Value - new Money(50, MoneyUnit.BTC);

            // Amount was refunded to wallet, minus fee
            Assert.Equal(senderBalanceBefore - this.node1.WalletSpendableBalance, fee);

            // Receipt is correct
            ReceiptResponse receipt = this.node1.GetReceipt(response.TransactionId.ToString());

            Assert.Equal(lastBlock.GetHash().ToString(), receipt.BlockHash);
            Assert.Equal(response.TransactionId.ToString(), receipt.TransactionHash);
            Assert.Empty(receipt.Logs);
            Assert.False(receipt.Success);
            Assert.True(receipt.GasUsed > GasPriceList.BaseCost);
            Assert.Null(receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.StartsWith("System.IndexOutOfRangeException", receipt.Error);
            Assert.Equal(preResponse.NewContractAddress, receipt.To);
        }
        public void CanChooseInputsForCall()
        {
            const int utxoIndex       = 0;
            uint256   utxoId          = uint256.Zero;
            uint256   utxoIdUnused    = uint256.One;
            string    senderAddress   = uint160.Zero.ToBase58Address(this.network);
            string    contractAddress = uint160.One.ToBase58Address(this.network);

            var request = new BuildCallContractTransactionRequest
            {
                Amount          = "0",
                AccountName     = "account 0",
                ContractAddress = contractAddress,
                FeeAmount       = "0.01",
                GasLimit        = 100_000,
                GasPrice        = 100,
                MethodName      = "TestMethod",
                WalletName      = "wallet",
                Password        = "******",
                Sender          = senderAddress,
                Outpoints       = new List <OutpointRequest>
                {
                    new OutpointRequest
                    {
                        Index         = utxoIndex,
                        TransactionId = utxoId.ToString()
                    },
                }
            };

            this.walletManager.Setup(x => x.GetAddressBalance(request.Sender))
            .Returns(new AddressBalance
            {
                Address         = senderAddress,
                AmountConfirmed = new Money(100, MoneyUnit.BTC)
            });

            this.walletManager.Setup(x => x.GetSpendableTransactionsInWallet(It.IsAny <string>(), 0))
            .Returns(new List <UnspentOutputReference>
            {
                new UnspentOutputReference
                {
                    Address = new HdAddress
                    {
                        Address = senderAddress
                    },
                    Transaction = new TransactionData
                    {
                        Id    = utxoId,
                        Index = utxoIndex,
                    }
                }, new UnspentOutputReference
                {
                    Address = new HdAddress
                    {
                        Address = senderAddress
                    },
                    Transaction = new TransactionData
                    {
                        Id    = utxoIdUnused,
                        Index = utxoIndex,
                    }
                }
            });

            this.walletManager.Setup(x => x.GetWallet(request.WalletName))
            .Returns(new Features.Wallet.Wallet
            {
                AccountsRoot = new List <AccountRoot>
                {
                    new AccountRoot
                    {
                        Accounts = new List <HdAccount>
                        {
                            new HdAccount
                            {
                                ExternalAddresses = new List <HdAddress>
                                {
                                    new HdAddress
                                    {
                                        Address = senderAddress
                                    }
                                },
                                Name = request.AccountName
                            }
                        }
                    }
                }
            });

            SmartContractTransactionService service = new SmartContractTransactionService(
                this.network,
                this.walletManager.Object,
                this.walletTransactionHandler.Object,
                this.stringSerializer.Object,
                this.callDataSerializer.Object,
                this.addressGenerator.Object,
                this.stateRepository.Object);

            BuildCallContractTransactionResponse result = service.BuildCallTx(request);

            this.walletTransactionHandler.Verify(x => x.BuildTransaction(It.Is <TransactionBuildContext>(y => y.SelectedInputs.Count == 1)));
        }
        public void ChoosingInvalidInputFails()
        {
            const int utxoIndex       = 0;
            uint256   utxoId          = uint256.Zero;
            uint256   utxoIdUnused    = uint256.One;
            string    senderAddress   = uint160.Zero.ToBase58Address(this.network);
            string    contractAddress = uint160.One.ToBase58Address(this.network);

            var request = new BuildCallContractTransactionRequest
            {
                Amount          = "0",
                AccountName     = "account 0",
                ContractAddress = contractAddress,
                FeeAmount       = "0.01",
                GasLimit        = 100_000,
                GasPrice        = 100,
                MethodName      = "TestMethod",
                WalletName      = "wallet",
                Password        = "******",
                Sender          = senderAddress,
                Outpoints       = new List <OutpointRequest>
                {
                    new OutpointRequest
                    {
                        Index         = utxoIndex,
                        TransactionId = new uint256(64).ToString() // A tx we don't have.
                    },
                }
            };

            this.walletManager.Setup(x => x.GetAddressBalance(request.Sender))
            .Returns(new AddressBalance
            {
                Address         = senderAddress,
                AmountConfirmed = new Money(100, MoneyUnit.BTC)
            });

            this.walletManager.Setup(x => x.GetSpendableTransactionsInWallet(It.IsAny <string>(), 0))
            .Returns(new List <UnspentOutputReference>
            {
                new UnspentOutputReference
                {
                    Address = new HdAddress
                    {
                        Address = senderAddress
                    },
                    Transaction = new TransactionData
                    {
                        Id    = utxoId,
                        Index = utxoIndex,
                    }
                }, new UnspentOutputReference
                {
                    Address = new HdAddress
                    {
                        Address = senderAddress
                    },
                    Transaction = new TransactionData
                    {
                        Id    = utxoIdUnused,
                        Index = utxoIndex,
                    }
                }
            });

            SmartContractTransactionService service = new SmartContractTransactionService(
                this.network,
                this.walletManager.Object,
                this.walletTransactionHandler.Object,
                this.stringSerializer.Object,
                this.callDataSerializer.Object,
                this.addressGenerator.Object,
                this.stateRepository.Object);

            BuildCallContractTransactionResponse result = service.BuildCallTx(request);

            Assert.False(result.Success);
            Assert.StartsWith("Invalid list of request outpoints", result.Message);
        }