예제 #1
0
 internal TransactionOutput(NetworkParameters @params, Transaction parent, ulong value, Address to)
     : base(@params)
 {
     _value = value;
     _scriptBytes = Script.CreateOutputScript(to);
     ParentTransaction = parent;
     _availableForSpending = true;
 }
예제 #2
0
 internal static byte[] CreateOutputScript(Address to)
 {
     using (var bits = new MemoryStream())
     {
         // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes.
         bits.Write(OpDup);
         bits.Write(OpHash160);
         WriteBytes(bits, to.Hash160);
         bits.Write(OpEqualVerify);
         bits.Write(OpCheckSig);
         return bits.ToArray();
     }
 }
예제 #3
0
        /// <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;
            }
        }
예제 #4
0
        /// <returns>A human readable version of the transaction useful for debugging.</returns>
        public override string ToString()
        {
            var s = new StringBuilder();
            s.Append("  ");
            s.Append(HashAsString);
            s.AppendLine();
            if (IsCoinBase)
            {
                string script;
                string script2;
                try
                {
                    script = _inputs[0].ScriptSig.ToString();
                    script2 = _outputs[0].ScriptPubKey.ToString();
                }
                catch (ScriptException)
                {
                    script = "???";
                    script2 = "???";
                }
                return "     == COINBASE TXN (scriptSig " + script + ")  (scriptPubKey " + script2 + ")";
            }
            foreach (var @in in _inputs)
            {
                s.Append("     ");
                s.Append("from ");

                try
                {
                    s.Append(@in.ScriptSig.FromAddress.ToString());
                }
                catch (Exception e)
                {
                    s.Append("[exception: ").Append(e.Message).Append("]");
                    throw;
                }
                s.AppendLine();
            }
            foreach (var @out in _outputs)
            {
                s.Append("       ");
                s.Append("to ");
                try
                {
                    var toAddr = new Address(Params, @out.ScriptPubKey.PubKeyHash);
                    s.Append(toAddr.ToString());
                    s.Append(" ");
                    s.Append(Utils.BitcoinValueToFriendlyString(@out.Value));
                    s.Append(" BTC");
                }
                catch (Exception e)
                {
                    s.Append("[exception: ").Append(e.Message).Append("]");
                }
                s.AppendLine();
            }
            return s.ToString();
        }
예제 #5
0
 /// <summary>
 /// Sends coins to the given address, via the given <see cref="Peer"/>.
 /// Change is returned to the first key in the wallet.
 /// </summary>
 /// <param name="peer">The peer to send via.</param>
 /// <param name="to">Which address to send coins to.</param>
 /// <param name="nanocoins">How many nanocoins to send. You can use Utils.ToNanoCoins() to calculate this.</param>
 /// <returns>The <see cref="Transaction"/> that was created or null if there was insufficient balance to send the coins.</returns>
 /// <exception cref="IOException">If there was a problem broadcasting the transaction.</exception>
 public Transaction SendCoins(Peer peer, Address to, ulong nanocoins)
 {
     lock (this)
     {
         var tx = CreateSend(to, nanocoins);
         if (tx == null) // Not enough money! :-(
             return null;
         peer.BroadcastTransaction(tx);
         ConfirmSend(tx);
         return tx;
     }
 }
예제 #6
0
 /// <summary>
 /// Statelessly creates a transaction that sends the given number of nanocoins to address. The change is sent to
 /// the first address in the wallet, so you must have added at least one key.
 /// </summary>
 /// <remarks>
 /// This method is stateless in the sense that calling it twice with the same inputs will result in two
 /// Transaction objects which are equal. The wallet is not updated to track its pending status or to mark the
 /// coins as spent until confirmSend is called on the result.
 /// </remarks>
 internal Transaction CreateSend(Address address, ulong nanocoins)
 {
     lock (this)
     {
         // For now let's just pick the first key in our keychain. In future we might want to do something else to
         // give the user better privacy here, eg in incognito mode.
         Debug.Assert(Keychain.Count > 0, "Can't send value without an address to use for receiving change");
         var first = Keychain[0];
         return CreateSend(address, nanocoins, first.ToAddress(_params));
     }
 }
예제 #7
0
        /// <summary>
        /// Sends coins to the given address, via the given <see cref="PeerGroup"/>.
        /// Change is returned to the first key in the wallet.
        /// </summary>
        /// <param name="peerGroup">The peer group to send via.</param>
        /// <param name="to">Which address to send coins to.</param>
        /// <param name="nanocoins">How many nanocoins to send. You can use Utils.toNanoCoins() to calculate this.</param>
        /// <returns>
        /// The <see cref="Transaction"/> that was created or null if there was insufficient balance to send the coins.
        /// </returns>
        /// <exception cref="IOException">If there was a problem broadcasting the transaction.</exception>
        public Transaction SendCoins(PeerGroup peerGroup, Address to, ulong nanocoins)
        {
            lock (this)
            {
                var tx = CreateSend(to, nanocoins);
                if (tx == null) // Not enough money! :-(
                    return null;
                if (!peerGroup.BroadcastTransaction(tx))
                {
                    throw new IOException("Failed to broadcast tx to all connected peers");
                }

                // TODO - retry logic
                ConfirmSend(tx);
                return tx;
            }
        }
예제 #8
0
 internal Block CreateNextBlock(Address to)
 {
     return CreateNextBlock(to, (uint) UnixTime.ToUnixTime(DateTime.UtcNow));
 }
예제 #9
0
 /// <summary>
 /// Returns a solved block that builds on top of this one. This exists for unit tests.
 /// </summary>
 internal Block CreateNextBlock(Address to, uint time)
 {
     var b = new Block(Params);
     b.DifficultyTarget = _difficultyTarget;
     b.AddCoinbaseTransaction(to);
     b.PrevBlockHash = Hash;
     b.Time = time;
     b.Solve();
     b.Verify();
     return b;
 }
예제 #10
0
 /// <summary>
 /// Adds a coinbase transaction to the block. This exists for unit tests.
 /// </summary>
 internal void AddCoinbaseTransaction(Address to)
 {
     Transactions = new List<Transaction>();
     var coinbase = new Transaction(Params);
     // A real coinbase transaction has some stuff in the scriptSig like the extraNonce and difficulty. The
     // transactions are distinguished by every TX output going to a different key.
     //
     // Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
     // counter in the scriptSig so every transaction has a different hash. The output is also different.
     // Real coinbase transactions use <pubkey> OP_CHECKSIG rather than a send to an address though there's
     // nothing in the system that enforces that and both are just as valid.
     coinbase.AddInput(new TransactionInput(Params, coinbase, new[] {(byte) _coinbaseCounter++}));
     coinbase.AddOutput(new TransactionOutput(Params, coinbase, Utils.ToNanoCoins(50, 0), to));
     Transactions.Add(coinbase);
 }
예제 #11
0
        /// <summary>
        /// Returns a solved block that builds on top of this one. This exists for unit tests.
        /// </summary>
        internal Block CreateNextBlock(Address to, uint time)
        {
            var b = new Block(Params);
            b.DifficultyTarget = _difficultyTarget;
            b.AddCoinbaseTransaction(_emptyBytes);

            // Add a transaction paying 50 coins to the "to" address.
            var t = new Transaction(Params);
            t.AddOutput(new TransactionOutput(Params, t, Utils.ToNanoCoins(50, 0), to));
            // The input does not really need to be a valid signature, as long as it has the right general form.
            var input = new TransactionInput(Params, t, Script.CreateInputScript(_emptyBytes, _emptyBytes));
            // Importantly the outpoint hash cannot be zero as that's how we detect a coinbase transaction in isolation
            // but it must be unique to avoid 'different' transactions looking the same.
            var counter = new byte[32];
            counter[0] = (byte) _txCounter++;
            input.Outpoint.Hash = new Sha256Hash(counter);
            t.AddInput(input);
            b.AddTransaction(t);

            b.PrevBlockHash = Hash;
            b.TimeSeconds = time;
            b.Solve();
            b.VerifyHeader();
            return b;
        }