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); }
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); }
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); }
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)); } }
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); }