/// <summary>
/// Creates a transaction that sends $coins.$cents BTC to the given address.
/// </summary>
/// <remarks>
/// IMPORTANT: This method does NOT update the wallet. If you call createSend again you may get two transactions
/// that spend the same coins. You have to call confirmSend on the created transaction to prevent this,
/// but that should only occur once the transaction has been accepted by the network. This implies you cannot have
/// more than one outstanding sending tx at once.
/// </remarks>
/// <param name="address">The BitCoin address to send the money to.</param>
/// <param name="nanocoins">How much currency to send, in nanocoins.</param>
/// <param name="changeAddress">
/// Which address to send the change to, in case we can't make exactly the right value from
/// our coins. This should be an address we own (is in the keychain).
/// </param>
/// <returns>
/// A new <see cref="Transaction"/> or null if we cannot afford this send.
/// </returns>
internal Transaction CreateSend(Address address, ulong nanocoins, Address changeAddress)
{
lock (this)
{
_log.Info("Creating send tx to " + address + " for " +
Utils.BitcoinValueToFriendlyString(nanocoins));
// To send money to somebody else, we need to do gather up transactions with unspent outputs until we have
// sufficient value. Many coin selection algorithms are possible, we use a simple but suboptimal one.
// TODO: Sort coins so we use the smallest first, to combat wallet fragmentation and reduce fees.
var valueGathered = 0UL;
var gathered = new LinkedList<TransactionOutput>();
foreach (var tx in Unspent.Values)
{
foreach (var output in tx.Outputs)
{
if (!output.IsAvailableForSpending) continue;
if (!output.IsMine(this)) continue;
gathered.AddLast(output);
valueGathered += output.Value;
}
if (valueGathered >= nanocoins) break;
}
// Can we afford this?
if (valueGathered < nanocoins)
{
_log.Info("Insufficient value in wallet for send, missing " +
Utils.BitcoinValueToFriendlyString(nanocoins - valueGathered));
// TODO: Should throw an exception here.
return null;
}
Debug.Assert(gathered.Count > 0);
var sendTx = new Transaction(_params);
sendTx.AddOutput(new TransactionOutput(_params, sendTx, nanocoins, address));
var change = (long) (valueGathered - nanocoins);
if (change > 0)
{
// The value of the inputs is greater than what we want to send. Just like in real life then,
// we need to take back some coins ... this is called "change". Add another output that sends the change
// back to us.
_log.Info(" with " + Utils.BitcoinValueToFriendlyString((ulong) change) + " coins change");
sendTx.AddOutput(new TransactionOutput(_params, sendTx, (ulong) change, changeAddress));
}
foreach (var output in gathered)
{
sendTx.AddInput(output);
}
// Now sign the inputs, thus proving that we are entitled to redeem the connected outputs.
sendTx.SignInputs(Transaction.SigHash.All, this);
_log.InfoFormat(" created {0}", sendTx.HashAsString);
return sendTx;
}
}