public void Actual_Fee_Without_OpReturnData_Should_Be_Less_Than_Actual_Fee_With_Costly_OpReturnData() { (Wallet wallet, (ExtKey ExtKey, string ExtPubKey)accountKeys, (PubKey PubKey, BitcoinPubKeyAddress Address)destinationKeys, TransactionData addressTransaction, WalletTransactionHandler walletTransactionHandler, WalletAccountReference walletReference) = this.SetupWallet(); // Context with OpReturnData TransactionBuildContext contextWithOpReturn = CreateContext(walletReference, "password", destinationKeys.PubKey.ScriptPubKey, new Money(7500), FeeType.Low, 0, this.CostlyOpReturnData); walletTransactionHandler.BuildTransaction(contextWithOpReturn); // Context without OpReturnData TransactionBuildContext contextWithoutOpReturn = CreateContext(walletReference, "password", destinationKeys.PubKey.ScriptPubKey, new Money(7500), FeeType.Low, 0, null); walletTransactionHandler.BuildTransaction(contextWithoutOpReturn); contextWithoutOpReturn.TransactionFee.Should().NotBe(contextWithOpReturn.TransactionFee); contextWithoutOpReturn.TransactionFee.Satoshi.Should().BeLessThan(contextWithOpReturn.TransactionFee.Satoshi); }
/// <summary> /// Adjusted to allow smart contract transactions with zero value through. /// </summary> protected override void AddRecipients(TransactionBuildContext context) { if (context.Recipients.Any(recipient => recipient.Amount == Money.Zero && !recipient.ScriptPubKey.IsSmartContractExec())) { throw new WalletException("No amount specified."); } if (context.Recipients.Any(a => a.SubtractFeeFromAmount)) { throw new NotImplementedException("Substracting the fee from the recipient is not supported yet."); } foreach (Recipient recipient in context.Recipients) { context.TransactionBuilder.Send(recipient.ScriptPubKey, recipient.Amount); } }
/// <inheritdoc /> public Transaction BuildWithdrawalTransaction(uint256 depositId, uint blockTime, Recipient recipient) { try { this.logger.LogInformation("BuildDeterministicTransaction depositId(opReturnData)={0} recipient.ScriptPubKey={1} recipient.Amount={2}", depositId, recipient.ScriptPubKey, recipient.Amount); // Build the multisig transaction template. uint256 opReturnData = depositId; string walletPassword = this.federationWalletManager.Secret.WalletPassword; bool sign = (walletPassword ?? "") != ""; var multiSigContext = new TransactionBuildContext(new[] { recipient }.ToList(), opReturnData: opReturnData.ToBytes()) { OrderCoinsDeterministic = true, TransactionFee = this.federationGatewaySettings.TransactionFee, MinConfirmations = this.federationGatewaySettings.MinCoinMaturity, Shuffle = false, IgnoreVerify = true, WalletPassword = walletPassword, Sign = sign, }; Transaction transaction = this.federationWalletTransactionHandler.BuildTransaction(multiSigContext); // Build the transaction. if (this.network.Consensus.IsProofOfStake) { transaction.Time = blockTime; if (sign) { transaction = multiSigContext.TransactionBuilder.SignTransaction(transaction); } } this.logger.LogInformation("transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer)); return(transaction); } catch (Exception error) { this.logger.LogError("Could not create transaction for deposit {0}: {1}", depositId, error.Message); } this.logger.LogTrace("(-)[FAIL]"); return(null); }
/// <inheritdoc /> public Transaction BuildTransaction(TransactionBuildContext context) { this.InitializeTransactionBuilder(context); if (context.Shuffle) { context.TransactionBuilder.Shuffle(); } // build transaction context.Transaction = context.TransactionBuilder.BuildTransaction(context.Sign); // If this is a multisig transaction, then by definition we only (usually) possess one of the keys // and can therefore not immediately construct a transaction that passes verification if (!context.IgnoreVerify) { if (!context.TransactionBuilder.Verify(context.Transaction, out TransactionPolicyError[] errors))
public void Actual_Fee_Without_OpReturnData_Should_Be_Less_Than_Actual_Fee_With_Costly_OpReturnData() { WalletTransactionHandlerTestContext testContext = SetupWallet(); // Context with OpReturnData TransactionBuildContext contextWithOpReturn = CreateContext(this.Network, testContext.WalletReference, "password", testContext.DestinationKeys.PubKey.ScriptPubKey, new Money(7500), FeeType.Low, 0, this.CostlyOpReturnData); testContext.WalletTransactionHandler.BuildTransaction(contextWithOpReturn); // Context without OpReturnData TransactionBuildContext contextWithoutOpReturn = CreateContext(this.Network, testContext.WalletReference, "password", testContext.DestinationKeys.PubKey.ScriptPubKey, new Money(7500), FeeType.Low, 0, null); testContext.WalletTransactionHandler.BuildTransaction(contextWithoutOpReturn); contextWithoutOpReturn.TransactionFee.Should().NotBe(contextWithOpReturn.TransactionFee); contextWithoutOpReturn.TransactionFee.Satoshi.Should().BeLessThan(contextWithOpReturn.TransactionFee.Satoshi); }
public Task <CreateTransactionResponse> GetTransferAllTransaction(BitcoinAddress @from, BitcoinAddress to, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); var uncoloredCoins = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(from.ToString())).ToList(); var coloredCoins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(from.ToString())).ToList(); if (uncoloredCoins.Count == 0 && coloredCoins.Count == 0) { throw new BackendException("Address has no unspent outputs", ErrorCode.NoCoinsFound); } if (uncoloredCoins.Count > 0) { _transactionBuildHelper.SendWithChange(builder, context, uncoloredCoins, to, uncoloredCoins.Sum(o => o.TxOut.Value), from); } foreach (var assetGroup in coloredCoins.GroupBy(o => o.AssetId)) { var sum = new AssetMoney(assetGroup.Key); foreach (var coloredCoin in assetGroup) { sum += coloredCoin.Amount; } _transactionBuildHelper.SendAssetWithChange(builder, context, assetGroup.ToList(), to, sum, from); } await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await SaveSpentOutputs(transactionId, buildedTransaction); await _signRequestRepository.InsertTransactionId(transactionId); await SaveNewOutputs(transactionId, buildedTransaction, context); return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId); }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
public Task <CreateTransactionResponse> GetIssueTransaction(BitcoinAddress bitcoinAddres, decimal amount, IAsset asset, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); var queue = _pregeneratedOutputsQueueFactory.Create(asset.BlockChainAssetId); var coin = await queue.DequeueCoin(); try { var issueCoin = new IssuanceCoin(coin) { DefinitionUrl = new Uri(asset.DefinitionUrl) }; var assetId = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId; builder.AddCoins(issueCoin) .IssueAsset(bitcoinAddres, new AssetMoney(assetId, amount, asset.MultiplierPower)); context.IssueAsset(assetId); await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await SaveSpentOutputs(transactionId, buildedTransaction); await SaveNewOutputs(transactionId, buildedTransaction, context); return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId); } catch (Exception) { await queue.EnqueueOutputs(coin); throw; } }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
public void SendAssetWithChange(TransactionBuilder builder, TransactionBuildContext context, List <ColoredCoin> coins, IDestination destination, AssetMoney amount, IDestination changeDestination) { if (amount.Quantity <= 0) { throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter); } Action throwError = () => { throw new BackendException($"The sum of total applicable outputs is less than the required: {amount.Quantity} {amount.Id}.", ErrorCode.NotEnoughAssetAvailable); }; var selectedCoins = OpenAssetsHelper.CoinSelect(coins, amount); if (selectedCoins == null) { throwError(); } var orderedCounts = selectedCoins.Cast <ColoredCoin>().OrderBy(o => o.Amount).ToList(); var sendAmount = new AssetMoney(amount.Id); var cnt = 0; while (sendAmount < amount && cnt < orderedCounts.Count) { sendAmount += orderedCounts[cnt].Amount; cnt++; } if (sendAmount < amount) { throwError(); } builder.AddCoins(orderedCounts.Take(cnt)); context.AddCoins(orderedCounts.Take(cnt)); builder.SendAsset(destination, amount); if ((sendAmount - amount).Quantity > 0) { builder.SendAsset(changeDestination, sendAmount - amount); } }
public async Task <decimal> SendWithChange(TransactionBuilder builder, TransactionBuildContext context, List <ICoin> coins, IDestination destination, Money amount, IDestination changeDestination, bool addDust = true) { if (amount.Satoshi <= 0) { throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter); } Action throwError = () => { throw new BackendException($"The sum of total applicable outputs is less than the required: {amount.Satoshi} satoshis.", ErrorCode.NotEnoughBitcoinAvailable); }; var selectedCoins = OpenAssetsHelper.CoinSelect(coins, amount); if (selectedCoins == null) { throwError(); } var orderedCoins = selectedCoins.OrderBy(o => o.Amount).ToList(); var sendAmount = Money.Zero; var cnt = 0; while (sendAmount < amount && cnt < orderedCoins.Count) { sendAmount += orderedCoins[cnt].TxOut.Value; cnt++; } if (sendAmount < amount) { throwError(); } context.AddCoins(orderedCoins.Take(cnt)); builder.AddCoins(orderedCoins.Take(cnt)); var sent = await Send(builder, context, destination, amount, addDust); if (sendAmount - amount > 0) { await Send(builder, context, changeDestination, sendAmount - amount, addDust); } return(sent); }
public IActionResult BuildTransaction([FromBody] BuildTransactionRequest request) { Guard.NotNull(request, nameof(request)); // checks the request is valid if (!this.ModelState.IsValid) { var errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage)); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors))); } var destination = BitcoinAddress.Create(request.DestinationAddress, this.network).ScriptPubKey; try { var context = new TransactionBuildContext( new WalletAccountReference(request.WalletName, request.AccountName), new[] { new Recipient { Amount = request.Amount, ScriptPubKey = destination } }.ToList(), request.Password) { FeeType = FeeParser.Parse(request.FeeType), MinConfirmations = request.AllowUnconfirmed ? 0 : 1, Shuffle = true }; var transactionResult = this.walletTransactionHandler.BuildTransaction(context); var model = new WalletBuildTransactionModel { Hex = transactionResult.ToHex(), Fee = context.TransactionFee, TransactionId = transactionResult.GetHash() }; return(this.Json(model)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
public async Task SpendCommitmemtByMultisig(ICommitment commitment, ICoin spendingCoin, string destination) { TransactionBuildContext context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); var destinationAddress = BitcoinAddress.Create(destination); await context.Build(async() => { TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(spendingCoin); if (OpenAssetsHelper.IsBitcoin(commitment.AssetId)) { builder.Send(destinationAddress, spendingCoin.Amount); } else { builder.SendAsset(destinationAddress, ((ColoredCoin)spendingCoin).Amount); } await _transactionBuildHelper.AddFee(builder, context); var tr = builder.BuildTransaction(false); var scriptParams = new OffchainScriptParams { IsMultisig = true, RedeemScript = commitment.LockedScript.ToScript().ToBytes(), Pushes = new[] { new byte[0], new byte[0], new byte[0] } }; tr.Inputs[0].ScriptSig = OffchainScriptCommitmentTemplate.GenerateScriptSig(scriptParams); var signed = await _signatureApiProvider.SignTransaction(tr.ToHex()); var signedTr = new Transaction(signed); var id = Guid.NewGuid(); await _rpcBitcoinClient.BroadcastTransaction(signedTr, id); await _lykkeTransactionBuilderService.SaveSpentOutputs(id, signedTr); return(Task.CompletedTask); }); }
public IActionResult BuildTransaction([FromBody] BuildTransactionRequest request) { Guard.NotNull(request, nameof(request)); // checks the request is valid if (!this.ModelState.IsValid) { return(BuildErrorResponse(this.ModelState)); } try { var destination = BitcoinAddress.Create(request.DestinationAddress, this.network).ScriptPubKey; var context = new TransactionBuildContext( new WalletAccountReference(request.WalletName, request.AccountName), new[] { new Recipient { Amount = request.Amount, ScriptPubKey = destination } }.ToList(), request.Password) { FeeType = FeeParser.Parse(request.FeeType), MinConfirmations = request.AllowUnconfirmed ? 0 : 1, Shuffle = request.ShuffleOutputs ?? true // We shuffle transaction outputs by default as it's better for anonymity. }; var transactionResult = this.walletTransactionHandler.BuildTransaction(context); var model = new WalletBuildTransactionModel { Hex = transactionResult.ToHex(), Fee = context.TransactionFee, TransactionId = transactionResult.GetHash() }; return(this.Json(model)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
private async Task TransferOneDirection(TransactionBuilder builder, TransactionBuildContext context, BitcoinAddress @from, decimal amount, IAsset asset, BitcoinAddress to) { var fromStr = from.ToString(); if (OpenAssetsHelper.IsBitcoin(asset.Id)) { var coins = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(fromStr)).ToList(); _transactionBuildHelper.SendWithChange(builder, context, coins, to, new Money(amount, MoneyUnit.BTC), from); } else { var assetIdObj = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId; var assetAmount = new AssetMoney(assetIdObj, amount, asset.MultiplierPower); var coins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(fromStr, assetIdObj)).ToList(); _transactionBuildHelper.SendAssetWithChange(builder, context, coins, to, assetAmount, @from); } }
private void a_nulldata_transaction() { var transactionBuildContext = new TransactionBuildContext( this.sendingWalletAccountReference, new List <Recipient>() { new Recipient() { Amount = this.transferAmount, ScriptPubKey = this.receiverAddress.ScriptPubKey } }, this.password, this.opReturnContent) { MinConfirmations = 2 }; this.transaction = this.senderNode.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); this.transaction.Outputs.Single(t => t.ScriptPubKey.IsUnspendable).Value.Should().Be(Money.Zero); }
/// <summary> /// The initialization of the builder is overridden as smart contracts calls allow dust and does not group /// inputs by ScriptPubKey. /// </summary> protected override void InitializeTransactionBuilder(TransactionBuildContext context) { Guard.NotNull(context, nameof(context)); Guard.NotNull(context.Recipients, nameof(context.Recipients)); Guard.NotNull(context.AccountReference, nameof(context.AccountReference)); context.TransactionBuilder.CoinSelector = new DefaultCoinSelector { GroupByScriptPubKey = false }; context.TransactionBuilder.DustPrevention = false; context.TransactionBuilder.StandardTransactionPolicy = this.TransactionPolicy; this.AddRecipients(context); this.AddOpReturnOutput(context); this.AddCoins(context); this.FindChangeAddress(context); this.AddFee(context); }
/// <summary> /// Send a normal transaction. /// </summary> public WalletSendTransactionModel SendTransaction(Script scriptPubKey, Money amount) { var txBuildContext = new TransactionBuildContext(this.chain.Network) { AccountReference = new WalletAccountReference(this.WalletName, this.AccountName), MinConfirmations = 1, FeeType = FeeType.Medium, WalletPassword = Password, Recipients = new[] { new Recipient { Amount = amount, ScriptPubKey = scriptPubKey } }.ToList() }; Transaction trx = (this.CoreNode.FullNode.NodeService <IWalletTransactionHandler>() as SmartContractWalletTransactionHandler).BuildTransaction(txBuildContext); // Broadcast to the other node. JsonResult response = (JsonResult)this.smartContractWalletController.SendTransaction(new SendTransactionRequest(trx.ToHex())); return((WalletSendTransactionModel)response.Value); }
private TransactionBuildContext GetOfflineWithdrawalBuildContext(string receivingAddress, string walletName, string accountName, Money amount, Money feeAmount, bool subtractFeeFromAmount) { // We presume that the amount given by the user is accurate and optimistically pass it to the build context. var recipient = new List <Recipient>() { new Recipient() { Amount = amount, ScriptPubKey = BitcoinAddress.Create(receivingAddress, this.network).ScriptPubKey, SubtractFeeFromAmount = subtractFeeFromAmount } }; var hotAccountReference = new WalletAccountReference(walletName, accountName); // As a simplification, the change address is defaulted to be the same cold staking script the UTXOs originate from. UnspentOutputReference coldStakingUtxo = this.GetSpendableTransactionsInAccount(hotAccountReference, 0).FirstOrDefault(); if (coldStakingUtxo == null) { throw new WalletException("No unspent transactions found in cold staking hot account."); } var context = new TransactionBuildContext(this.network) { AccountReference = hotAccountReference, TransactionFee = feeAmount, MinConfirmations = 0, Shuffle = true, // We shuffle transaction outputs by default as it's better for anonymity. Recipients = recipient, ChangeScript = coldStakingUtxo.Transaction.ScriptPubKey, // We specifically use this instead of ChangeAddress. Sign = false }; // As we don't actually know when the signed offline transaction will be broadcast, give it the highest chance of success if no fee was specified. if (context.TransactionFee == null) { context.FeeType = FeeType.High; } return(context); }
public void a_real_transaction() { var transactionBuildContext = new TransactionBuildContext( this.miningWalletAccountReference, new List <Recipient>() { new Recipient() { Amount = this.transferAmount, ScriptPubKey = this.receiverAddress.ScriptPubKey } }, this.password) { MinConfirmations = 2 }; this.transaction = this.node.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); this.node.FullNode.NodeService <WalletController>() .SendTransaction(new SendTransactionRequest(this.transaction.ToHex())); }
private void node2_sends_funds_to_node1_FROM_fifty_addresses() { HdAddress sendToNodeOne = this.firstNode.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, WalletAccountName)); // Send 49 coins to node1: this.transactionBuildContext = new TransactionBuildContext(this.firstNode.FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, WalletAccountName), WalletPassword = WalletPassword, Recipients = new[] { new Recipient { Amount = Money.Coins(50) - Money.COIN, ScriptPubKey = sendToNodeOne.ScriptPubKey } }.ToList() }; Transaction transaction = this.secondNode.FullNode.WalletTransactionHandler().BuildTransaction(this.transactionBuildContext); transaction.Inputs.Count.Should().Be(50); this.secondNode.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); TestHelper.AreNodesSynced(this.firstNode, this.secondNode); }
public async Task AddFeeWithoutChange(Transaction tr, TransactionBuildContext context, int maxCoins = int.MaxValue) { Money fee = Money.Zero; var providedAmount = Money.Zero; var queue = _pregeneratedOutputsQueueFactory.CreateFeeQueue(); int count = 0; do { var feeInput = await queue.DequeueCoin(); context.AddCoins(true, feeInput); count++; tr.Inputs.Add(new TxIn { PrevOut = feeInput.Outpoint }); providedAmount += feeInput.Amount; fee = await _feeProvider.CalcFeeForTransaction(tr); } while (fee > providedAmount && count < maxCoins); }
private void a_real_transaction() { var transactionBuildContext = new TransactionBuildContext(this.node.FullNode.Network) { AccountReference = this.miningWalletAccountReference, MinConfirmations = (int)this.node.FullNode.Network.Consensus.CoinbaseMaturity, WalletPassword = password, Recipients = new List <Recipient>() { new Recipient() { Amount = this.transferAmount, ScriptPubKey = this.receiverAddress.ScriptPubKey } } }; this.transaction = this.node.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); this.node.FullNode.NodeService <WalletController>() .SendTransaction(new SendTransactionRequest(this.transaction.ToHex())); }
public static JoinFederationRequestResult BuildTransaction(IWalletTransactionHandler walletTransactionHandler, Network network, JoinFederationRequest request, JoinFederationRequestEncoder encoder, string walletName, string walletAccount, string walletPassword) { byte[] encodedVotingRequest = encoder.Encode(request); var votingOutputScript = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(encodedVotingRequest)); var context = new TransactionBuildContext(network) { AccountReference = new WalletAccountReference(walletName, walletAccount), MinConfirmations = 0, FeeType = FeeType.High, WalletPassword = walletPassword, Recipients = new[] { new Recipient { Amount = new Money(VotingRequestTransferAmount, MoneyUnit.BTC), ScriptPubKey = votingOutputScript } }.ToList() }; Transaction transaction = walletTransactionHandler.BuildTransaction(context); Guard.Assert(IsVotingRequestTransaction(transaction, encoder)); if (context.TransactionBuilder.Verify(transaction, out TransactionPolicyError[] errors))
private void node2_sends_funds_to_node1_FROM_fifty_addresses() { HdAddress sendToNodeOne = this.nodes[NodeOne].FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, WalletAccountName)); this.transactionBuildContext = new TransactionBuildContext(this.nodes[NodeOne].FullNode.Network) { AccountReference = new WalletAccountReference(WalletName, WalletAccountName), WalletPassword = WalletPassword, Recipients = new[] { new Recipient { Amount = this.nodeTwoBalance - Money.COIN, ScriptPubKey = sendToNodeOne.ScriptPubKey } }.ToList() }; Transaction transaction = this.nodes[NodeTwo].FullNode.WalletTransactionHandler().BuildTransaction(this.transactionBuildContext); this.transactionFee = this.nodes[NodeTwo].GetFee(this.transactionBuildContext); transaction.Inputs.Count.Should().Be(50); this.nodes[NodeTwo].FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); }
private void a_nulldata_transaction() { var maturity = (int)this.senderNode.FullNode.Network.Consensus.CoinbaseMaturity; var transactionBuildContext = new TransactionBuildContext(this.senderNode.FullNode.Network) { AccountReference = this.sendingWalletAccountReference, MinConfirmations = maturity, OpReturnData = this.opReturnContent, WalletPassword = this.password, Recipients = new List <Recipient>() { new Recipient() { Amount = this.transferAmount, ScriptPubKey = this.receiverAddress.ScriptPubKey } } }; this.transaction = this.senderNode.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); this.transaction.Outputs.Single(t => t.ScriptPubKey.IsUnspendable).Value.Should().Be(Money.Zero); }
public void SendOneMillionCoinsFromPowWalletToPosWallet() { TransactionBuildContext context = SharedSteps.CreateTransactionBuildContext( this.PowWallet, this.WalletAccount, this.PowWalletPassword, new List <Recipient>() { new Recipient { Amount = Money.COIN * 1000000, ScriptPubKey = this.posReceiverAddress.ScriptPubKey } }, FeeType.Medium, 101); context.OverrideFeeRate = new FeeRate(Money.Satoshis(20000)); IEnumerable <UnspentOutputReference> unspent = this.nodes[this.PowMiner].FullNode.WalletManager().GetSpendableTransactionsInWallet(this.PowWallet); var coins = new List <Coin>(); DateTimeOffset blockTimestamp = unspent.OrderBy(u => u.Transaction.CreationTime).Select(ts => ts.Transaction.CreationTime).First(); Transaction transaction = this.nodes[this.PowMiner].FullNode.Network.Consensus.ConsensusFactory.CreateTransaction(); transaction.Time = (uint)blockTimestamp.ToUnixTimeSeconds(); foreach (UnspentOutputReference item in unspent.OrderByDescending(a => a.Transaction.Amount)) { coins.Add(new Coin(item.Transaction.Id, (uint)item.Transaction.Index, item.Transaction.Amount, item.Transaction.ScriptPubKey)); } Coin coin = coins.First(); TxIn txIn = transaction.AddInput(new TxIn(coin.Outpoint, this.powSenderAddress.ScriptPubKey)); transaction.AddOutput(new TxOut(new Money(9699999999995400), this.powSenderAddress.ScriptPubKey)); transaction.AddOutput(new TxOut(new Money(100000000000000), this.posReceiverAddress.ScriptPubKey)); transaction.Sign(this.nodes[this.PowMiner].FullNode.Network, this.powSenderPrivateKey, new[] { coin }); this.nodes[this.PowMiner].FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); }
public void BuildTransaction_When_OpReturnData_Is_Neither_Null_Nor_Empty_Should_Add_Extra_Output_With_Data() { WalletTransactionHandlerTestContext testContext = SetupWallet(); string opReturnData = "some extra transaction info"; byte[] expectedBytes = Encoding.UTF8.GetBytes(opReturnData); TransactionBuildContext context = CreateContext(this.Network, testContext.WalletReference, "password", testContext.DestinationKeys.PubKey.ScriptPubKey, new Money(7500), FeeType.Low, 0, opReturnData); Transaction transactionResult = testContext.WalletTransactionHandler.BuildTransaction(context); IEnumerable <TxOut> unspendableOutputs = transactionResult.Outputs.Where(o => o.ScriptPubKey.IsUnspendable).ToList(); unspendableOutputs.Count().Should().Be(1); unspendableOutputs.Single().Value.Should().Be(Money.Zero); IEnumerable <Op> ops = unspendableOutputs.Single().ScriptPubKey.ToOps(); ops.Count().Should().Be(2); ops.First().Code.Should().Be(OpcodeType.OP_RETURN); ops.Last().PushData.Should().BeEquivalentTo(expectedBytes); }
private void bob_creates_a_transaction_and_broadcasts() { HdAddress nodeCReceivingAddress = this.GetSecondUnusedAddressToAvoidClashWithMiningAddress(this.nodes[Charlie]); TransactionBuildContext transactionBuildContext = SharedSteps.CreateTransactionBuildContext( WalletZero, AccountZero, WalletPassword, new[] { new Recipient { Amount = Money.COIN * 1, ScriptPubKey = nodeCReceivingAddress.ScriptPubKey } }, FeeType.Medium , 1); this.shorterChainTransaction = this.nodes[Bob].FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); this.shortChainTransactionFee = this.nodes[Bob].FullNode.WalletTransactionHandler().EstimateFee(transactionBuildContext); this.nodes[Bob].FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(this.shorterChainTransaction.ToHex())); }
public void BuildTransactionUsesGivenChangeAddress() { WalletTransactionHandlerTestContext testContext = SetupWallet(); TransactionBuildContext context = CreateContext(this.Network, testContext.WalletReference, "password", testContext.DestinationKeys.PubKey.ScriptPubKey, new Money(7500), FeeType.Low, 0); var key = new Key(); BitcoinPubKeyAddress address = key.PubKey.GetAddress(this.Network); HdAddress changeAddress = context.ChangeAddress = new HdAddress { Index = 0, HdPath = $"m/44'/0'/0'/0/0", Address = address.ToString(), Pubkey = key.PubKey.ScriptPubKey, ScriptPubKey = address.ScriptPubKey, //Transactions = new List<TransactionData>() }; Transaction transactionResult = testContext.WalletTransactionHandler.BuildTransaction(context); Transaction result = this.Network.CreateTransaction(transactionResult.ToHex()); Assert.Single(result.Inputs); Assert.Equal(testContext.AddressTransaction.Id, result.Inputs[0].PrevOut.Hash); Assert.Equal(2, result.Outputs.Count); TxOut output = result.Outputs[0]; Assert.Equal((testContext.AddressTransaction.Amount - context.TransactionFee - 7500), output.Value); Assert.Equal(changeAddress.ScriptPubKey, output.ScriptPubKey); output = result.Outputs[1]; Assert.Equal(7500, output.Value); Assert.Equal(testContext.DestinationKeys.PubKey.ScriptPubKey, output.ScriptPubKey); Assert.Equal(testContext.AddressTransaction.Amount - context.TransactionFee, result.TotalOut); Assert.NotNull(transactionResult.GetHash()); Assert.Equal(result.GetHash(), transactionResult.GetHash()); }
private void node1_sends_funds_to_node2_TO_fifty_addresses() { this.Mine100Coins(this.nodes[NodeOne]); IEnumerable <HdAddress> nodeTwoAddresses = this.nodes[NodeTwo].FullNode.WalletManager().GetUnusedAddresses(new WalletAccountReference(WalletName, WalletAccountName), 50); List <Recipient> nodeTwoRecipients = nodeTwoAddresses.Select(address => new Recipient { ScriptPubKey = address.ScriptPubKey, Amount = Money.COIN }).ToList(); this.transactionBuildContext = SharedSteps.CreateTransactionBuildContext(this.nodes[NodeOne].FullNode.Network, WalletName, WalletAccountName, WalletPassword, nodeTwoRecipients, FeeType.Medium, this.CoinBaseMaturity + 1); Transaction transaction = this.nodes[NodeOne].FullNode.WalletTransactionHandler().BuildTransaction(this.transactionBuildContext); // Returns 50 outputs, including one extra output for change. transaction.Outputs.Count.Should().Be(UnspentTransactionOutputs + 1); this.transactionFee = this.nodes[NodeOne].GetFee(this.transactionBuildContext); this.nodes[NodeOne].FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); }
private void bob_creates_a_transaction_and_broadcasts() { HdAddress charlieAddress = this.GetSecondUnusedAddressToAvoidClashWithMiningAddress(this.charlieNode); TransactionBuildContext transactionBuildContext = TestHelper.CreateTransactionBuildContext( this.bobNode.FullNode.Network, WalletZero, AccountZero, WalletPassword, new[] { new Recipient { Amount = Money.COIN * 1, ScriptPubKey = charlieAddress.ScriptPubKey } }, FeeType.Medium , 1); this.shorterChainTransaction = this.bobNode.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); Money shortChainTransactionFee = this.bobNode.FullNode.WalletTransactionHandler().EstimateFee(transactionBuildContext); this.bobNode.FullNode.NodeController <WalletController>().SendTransactionAsync(new SendTransactionRequest(this.shorterChainTransaction.ToHex())); }