A BitCoin address is fundamentally derived from an elliptic curve public key and a set of network parameters.
It has several possible representations:

  1. The raw public key bytes themselves.
  2. RIPEMD160 hash of the public key bytes.
  3. A base58 encoded "human form" that includes a version and check code, to guard against typos.

One may question whether the base58 form is really an improvement over the hash160 form, given they are both very unfriendly for typists. More useful representations might include QR codes and identicons.

Note that an address is specific to a network because the first byte is a discriminator value.

Inheritance: VersionedChecksummedBytes
コード例 #1
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;
            }
        }
コード例 #2
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;
     }
 }
コード例 #3
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));
     }
 }
コード例 #4
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;
            }
        }
コード例 #5
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();
     }
 }
コード例 #6
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();
        }
コード例 #7
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;
        }
コード例 #8
0
 // Visible for testing.
 public Block CreateNextBlock(Address to)
 {
     return CreateNextBlock(to, (uint) UnixTime.ToUnixTime(DateTime.UtcNow));
 }