Пример #1
0
        public void InternalTransfer_FromConstructor()
        {
            // Ensure fixture is funded.
            this.node1.MineBlocks(1);

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

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

            Assert.True(compilationResult.Success);
            uint160 walletUint160 = new uint160(1);
            string  address       = walletUint160.ToAddress(this.mockChain.Network);

            string[] parameters = new string[] { string.Format("{0}#{1}", (int)MethodParameterDataType.Address, address) };
            BuildCreateContractTransactionResponse response = this.node1.SendCreateContractTransaction(compilationResult.Compilation, amount, parameters);

            this.node2.WaitMempoolCount(1);
            this.node2.MineBlocks(1);
            Assert.NotNull(this.node1.GetCode(response.NewContractAddress));
            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.Equal(2, condensingTransaction.Outputs.Count);

            // 1 output which is contract maintaining its balance
            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(response.NewContractAddress, toAddress.ToAddress(this.mockChain.Network).Value);
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC) / 2, condensingTransaction.Outputs[1].Value);

            // 1 output to address sent in params
            uint160 transferReceiver = this.senderRetriever.GetAddressFromScript(condensingTransaction.Outputs[1].ScriptPubKey).Sender;

            Assert.Equal(walletUint160, transferReceiver);
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC) / 2, condensingTransaction.Outputs[1].Value);
            Money fee = lastBlock.Transactions[0].Outputs[0].Value - new Money(50, MoneyUnit.BTC);

            // Amount in wallet is reduced by amount sent and fee.
            Assert.Equal(senderBalanceBefore - this.node1.WalletSpendableBalance, fee + new Money((long)amount, MoneyUnit.BTC));

            // Contract maintains half the balance
            Assert.Equal((ulong)new Money((long)amount, MoneyUnit.BTC) / 2, this.node1.GetContractBalance(response.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.Equal(response.NewContractAddress, receipt.NewContractAddress);
            Assert.Equal(this.node1.MinerAddress.Address, receipt.From);
            Assert.Null(receipt.Error);
            Assert.Null(receipt.To);
        }
Пример #2
0
        public void InternalTransfer_ToContractAddress()
        {
            // Ensure fixture is funded.
            this.node1.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.node1.WaitMempoolCount(1);
            this.node1.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.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();

            // 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.MineBlocks(1);

            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(), ((SmartContractBlockHeader)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.ToAddress(this.mockChain.Network).Value);
            Assert.Equal(new Money((long)amount, MoneyUnit.BTC), condensingTransaction.Outputs[0].Value);
            Money fee = lastBlock.Transactions[0].Outputs[0].Value - new Money(50, MoneyUnit.BTC);

            // Amount in wallet is reduced by amount sent and fee.
            Assert.Equal(senderBalanceBefore - this.node1.WalletSpendableBalance, fee + new Money((long)amount, MoneyUnit.BTC));

            // 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);
        }
Пример #3
0
        public void Transfers_Summed_Correctly()
        {
            uint160 contractAddress = new uint160(1);
            uint160 receiverAddress = new uint160(2);
            uint160 thirdAddress    = new uint160(3);

            // Has balance
            var stateMock = new Mock <IContractState>();

            stateMock.Setup(x => x.GetAccountState(contractAddress)).Returns(new AccountState
            {
                CodeHash    = new byte[32],
                StateRoot   = new byte[32],
                TypeName    = "Mock",
                UnspentHash = new byte[32]
            });
            stateMock.Setup(x => x.GetUnspent(contractAddress)).Returns(new ContractUnspentOutput
            {
                Hash  = new uint256(1),
                Nvout = 1,
                Value = 100
            });

            // no tx value
            var txContextMock = new Mock <ISmartContractTransactionContext>();

            txContextMock.SetupGet(p => p.TxOutValue).Returns(0);
            txContextMock.SetupGet(p => p.Time).Returns(12345);

            // several transfers
            var transferInfos = new List <TransferInfo>
            {
                new TransferInfo
                {
                    From  = contractAddress,
                    To    = receiverAddress,
                    Value = 75
                },
                new TransferInfo
                {
                    From  = receiverAddress,
                    To    = contractAddress,
                    Value = 20
                },
                new TransferInfo
                {
                    From  = receiverAddress,
                    To    = thirdAddress,
                    Value = 5
                }
            };

            // End result should be Contract: 45, Receiver: 50, ThirdAddress: 5

            var result = new SmartContractExecutionResult();

            // Condensing tx generated. 1 input. 3 outputs with consolidated balances.
            Transaction internalTransaction = this.transferProcessor.Process(stateMock.Object, contractAddress, txContextMock.Object, transferInfos, false);

            Assert.NotNull(internalTransaction);
            Assert.Equal(txContextMock.Object.Time, internalTransaction.Time);
            Assert.Single(internalTransaction.Inputs);
            Assert.Equal(3, internalTransaction.Outputs.Count);
            Assert.Equal(new uint256(1), internalTransaction.Inputs[0].PrevOut.Hash);
            Assert.Equal((uint)1, internalTransaction.Inputs[0].PrevOut.N);
            string output1Address = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(internalTransaction.Outputs[0].ScriptPubKey).GetAddress(this.network).ToString();

            Assert.Equal(receiverAddress.ToAddress(this.network).Value, output1Address);
            Assert.Equal(50, internalTransaction.Outputs[0].Value);
            Assert.True(internalTransaction.Outputs[1].ScriptPubKey.IsSmartContractInternalCall());
            Assert.Equal(45, internalTransaction.Outputs[1].Value);
            string output3Address = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(internalTransaction.Outputs[2].ScriptPubKey).GetAddress(this.network).ToString();

            Assert.Equal(thirdAddress.ToAddress(this.network).Value, output3Address);
            Assert.Equal(5, internalTransaction.Outputs[2].Value);

            // Ensure db updated
            stateMock.Verify(x => x.SetUnspent(contractAddress, It.Is <ContractUnspentOutput>(unspent => unspent.Value == 45)), Times.Once);
        }
Пример #4
0
        private BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractTransactionRequest request)
        {
            this.logger.LogTrace(request.ToString());

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

            if (addressBalance.AmountConfirmed == 0)
            {
                return(BuildCreateContractTransactionResponse.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);

            ContractTxData txData;

            if (request.Parameters != null && request.Parameters.Any())
            {
                var methodParameters = this.callDataSerializer.MethodParamSerializer.Deserialize(request.Parameters);
                txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)gasPrice, (Gas)gasLimit, request.ContractCode.HexToByteArray(), methodParameters);
            }
            else
            {
                txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)gasPrice, (Gas)gasLimit, request.ContractCode.HexToByteArray());
            }

            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   walletAccountReference = new WalletAccountReference(request.WalletName, request.AccountName);
            var   recipient = new Recipient {
                Amount = request.Amount ?? "0", ScriptPubKey = new Script(this.callDataSerializer.Serialize(txData))
            };
            var context = new TransactionBuildContext(this.network)
            {
                AccountReference = walletAccountReference,
                TransactionFee   = totalFee,
                ChangeAddress    = senderAddress,
                SelectedInputs   = selectedInputs,
                MinConfirmations = MinConfirmationsAllChecks,
                WalletPassword   = request.Password,
                Recipients       = new[] { recipient }.ToList()
            };

            try
            {
                Transaction transaction     = this.walletTransactionHandler.BuildTransaction(context);
                uint160     contractAddress = this.addressGenerator.GenerateAddress(transaction.GetHash(), 0);
                return(BuildCreateContractTransactionResponse.Succeeded(transaction, context.TransactionFee, contractAddress.ToAddress(this.network)));
            }
            catch (Exception exception)
            {
                return(BuildCreateContractTransactionResponse.Failed(exception.Message));
            }
        }
Пример #5
0
        public void HasBalance_TxValue1_TransferValue1()
        {
            uint160 contractAddress = new uint160(1);
            uint160 receiverAddress = new uint160(2);

            // Has balance
            var stateMock = new Mock <IContractState>();

            stateMock.Setup(x => x.GetAccountState(contractAddress)).Returns(new AccountState
            {
                CodeHash    = new byte[32],
                StateRoot   = new byte[32],
                TypeName    = "Mock",
                UnspentHash = new byte[32]
            });
            stateMock.Setup(x => x.GetUnspent(contractAddress)).Returns(new ContractUnspentOutput
            {
                Hash  = new uint256(1),
                Nvout = 1,
                Value = 100
            });

            // no tx value
            var txContextMock = new Mock <ISmartContractTransactionContext>();

            txContextMock.SetupGet(p => p.TxOutValue).Returns(100);
            txContextMock.SetupGet(p => p.TransactionHash).Returns(new uint256(123));
            txContextMock.SetupGet(p => p.Nvout).Returns(1);
            txContextMock.SetupGet(p => p.Time).Returns(12345);

            // transfer 75
            var transferInfos = new List <TransferInfo>
            {
                new TransferInfo
                {
                    From  = contractAddress,
                    To    = receiverAddress,
                    Value = 75
                }
            };

            var result = new SmartContractExecutionResult();

            // Condensing tx generated. 2 inputs from currently stored utxo and current tx. 2 outputs for each receiver and contract.
            Transaction internalTransaction = this.transferProcessor.Process(stateMock.Object, contractAddress, txContextMock.Object, transferInfos, false);

            Assert.NotNull(internalTransaction);
            Assert.Equal(txContextMock.Object.Time, internalTransaction.Time);
            Assert.Equal(2, internalTransaction.Inputs.Count);
            Assert.Equal(2, internalTransaction.Outputs.Count);
            Assert.Equal(new uint256(123), internalTransaction.Inputs[0].PrevOut.Hash);
            Assert.Equal((uint)1, internalTransaction.Inputs[0].PrevOut.N);
            Assert.Equal(new uint256(1), internalTransaction.Inputs[1].PrevOut.Hash);
            Assert.Equal((uint)1, internalTransaction.Inputs[1].PrevOut.N);

            Assert.True(internalTransaction.Outputs[0].ScriptPubKey.IsSmartContractInternalCall());
            Assert.Equal(125, internalTransaction.Outputs[0].Value);

            string output2Address = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(internalTransaction.Outputs[1].ScriptPubKey).GetAddress(this.network).ToString();

            Assert.Equal(receiverAddress.ToAddress(this.network).Value, output2Address);
            Assert.Equal(75, internalTransaction.Outputs[1].Value);

            // Ensure db updated
            stateMock.Verify(x => x.SetUnspent(contractAddress, It.Is <ContractUnspentOutput>(unspent => unspent.Value == 125)), Times.Once);
        }