/// <summary> /// Used only in creation of the genesis block. /// </summary> internal TransactionInput(NetworkParameters @params, Transaction parentTransaction, byte[] scriptBytes) : base(@params) { ScriptBytes = scriptBytes; Outpoint = new TransactionOutPoint(@params, -1, null); _sequence = uint.MaxValue; ParentTransaction = parentTransaction; }
/// <summary> /// Creates an UNSIGNED input that links to the given output /// </summary> internal TransactionInput(NetworkParameters @params, Transaction parentTransaction, TransactionOutput output) : base(@params) { var outputIndex = output.Index; Outpoint = new TransactionOutPoint(@params, outputIndex, output.ParentTransaction); ScriptBytes = EmptyArray; _sequence = uint.MaxValue; ParentTransaction = parentTransaction; }
internal TransactionOutPoint(NetworkParameters @params, int index, Transaction fromTx) : base(@params) { Index = index; if (fromTx != null) { Hash = fromTx.Hash; FromTx = fromTx; } else { // This happens when constructing the genesis block. Hash = Sha256Hash.ZeroHash; } }
/// <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; } }
/// <summary> /// Call this when we have successfully transmitted the send tx to the network, to update the wallet. /// </summary> internal void ConfirmSend(Transaction tx) { lock (this) { Debug.Assert(!Pending.ContainsKey(tx.Hash), "confirmSend called on the same transaction twice"); _log.InfoFormat("confirmSend of {0}", tx.HashAsString); // Mark the outputs of the used transactions as spent, so we don't try and spend it again. foreach (var input in tx.Inputs) { var connectedOutput = input.Outpoint.ConnectedOutput; var connectedTx = connectedOutput.ParentTransaction; connectedOutput.MarkAsSpent(input); MaybeMoveTxToSpent(connectedTx, "spent tx"); } // Add to the pending pool. It'll be moved out once we receive this transaction on the best chain. Pending[tx.Hash] = tx; } }
/// <param name="deadTx">The transaction that is newly dead.</param> /// <param name="replacementTx">The transaction that killed it.</param> public WalletDeadTransactionEventArgs(Transaction deadTx, Transaction replacementTx) { DeadTx = deadTx; ReplacementTx = replacementTx; }
/// <param name="tx">The transaction which sent us the coins.</param> /// <param name="prevBalance">Balance before the coins were received.</param> /// <param name="newBalance">Current balance of the wallet.</param> public WalletCoinsReceivedEventArgs(Transaction tx, ulong prevBalance, ulong newBalance) { Tx = tx; PrevBalance = prevBalance; NewBalance = newBalance; }
/// <summary> /// Updates the wallet by checking if this TX spends any of our outputs. This is not used normally because /// when we receive our own spends, we've already marked the outputs as spent previously (during tx creation) so /// there's no need to go through and do it again. /// </summary> /// <exception cref="VerificationException"/> private void UpdateForSpends(Transaction tx) { // tx is on the best chain by this point. foreach (var input in tx.Inputs) { var result = input.Connect(Unspent, false); if (result == TransactionInput.ConnectionResult.NoSuchTx) { // Not found in the unspent map. Try again with the spent map. result = input.Connect(Spent, false); if (result == TransactionInput.ConnectionResult.NoSuchTx) { // Doesn't spend any of our outputs or is coinbase. continue; } } if (result == TransactionInput.ConnectionResult.AlreadySpent) { // Double spend! This must have overridden a pending tx, or the block is bad (contains transactions // that illegally double spend: should never occur if we are connected to an honest node). // // Work backwards like so: // // A -> spent by B [pending] // \-> spent by C [chain] var doubleSpent = input.Outpoint.FromTx; // == A Debug.Assert(doubleSpent != null); var index = input.Outpoint.Index; var output = doubleSpent.Outputs[index]; var spentBy = output.SpentBy; Debug.Assert(spentBy != null); var connected = spentBy.ParentTransaction; Debug.Assert(connected != null); if (Pending.Remove(connected.Hash)) { _log.InfoFormat("Saw double spend from chain override pending tx {0}", connected.HashAsString); _log.Info(" <-pending ->dead"); _dead[connected.Hash] = connected; // Now forcibly change the connection. input.Connect(Unspent, true); // Inform the event listeners of the newly dead tx. if (DeadTransaction != null) { lock (DeadTransaction) { DeadTransaction(this, new WalletDeadTransactionEventArgs(connected, tx)); } } } } else if (result == TransactionInput.ConnectionResult.Success) { // Otherwise we saw a transaction spend our coins, but we didn't try and spend them ourselves yet. // The outputs are already marked as spent by the connect call above, so check if there are any more for // us to use. Move if not. var connected = input.Outpoint.FromTx; MaybeMoveTxToSpent(connected, "prevtx"); } } }
/// <summary> /// Handle when a transaction becomes newly active on the best chain, either due to receiving a new block or a /// re-org making inactive transactions active. /// </summary> /// <exception cref="VerificationException"/> private void ProcessTxFromBestChain(Transaction tx) { // This TX may spend our existing outputs even though it was not pending. This can happen in unit // tests and if keys are moved between wallets. UpdateForSpends(tx); if (!tx.GetValueSentToMe(this).Equals(0)) { // It's sending us coins. _log.Info(" new tx ->unspent"); Debug.Assert(!Unspent.ContainsKey(tx.Hash), "TX was received twice"); Unspent[tx.Hash] = tx; } else { // It spent some of our coins and did not send us any. _log.Info(" new tx ->spent"); Debug.Assert(!Spent.ContainsKey(tx.Hash), "TX was received twice"); Spent[tx.Hash] = tx; } }
/// <exception cref="ProtocolException"/> protected override void Parse() { _version = ReadUint32(); _prevBlockHash = ReadHash(); _merkleRoot = ReadHash(); _time = ReadUint32(); _difficultyTarget = ReadUint32(); _nonce = ReadUint32(); _hash = new Sha256Hash(Utils.ReverseBytes(Utils.DoubleDigest(Bytes, 0, Cursor))); if (Cursor == Bytes.Length) { // This message is just a header, it has no transactions. return; } var numTransactions = (int) ReadVarInt(); Transactions = new List<Transaction>(numTransactions); for (var i = 0; i < numTransactions; i++) { var tx = new Transaction(Params, Bytes, Cursor); Transactions.Add(tx); Cursor += tx.MessageSize; } }
/// <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; }
/// <summary> /// Adds a transaction to this block. /// </summary> internal void AddTransaction(Transaction t) { if (Transactions == null) { Transactions = new List<Transaction>(); } Transactions.Add(t); // Force a recalculation next time the values are needed. _merkleRoot = null; _hash = null; }
/// <summary> /// Adds a coinbase transaction to the block. This exists for unit tests. /// </summary> internal void AddCoinbaseTransaction(byte[] pubKeyTo) { 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. coinbase.AddInput(new TransactionInput(Params, coinbase, new[] {(byte) _txCounter++})); coinbase.AddOutput(new TransactionOutput(Params, coinbase, Script.CreateOutputScript(pubKeyTo))); Transactions.Add(coinbase); }
private static Block CreateGenesis(NetworkParameters n) { var genesisBlock = new Block(n); var t = new Transaction(n); // A script containing the difficulty bits and the following message: // // "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks" var bytes = Hex.Decode("04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73"); t.AddInput(new TransactionInput(n, t, bytes)); using (var scriptPubKeyBytes = new MemoryStream()) { Script.WriteBytes(scriptPubKeyBytes, Hex.Decode("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")); scriptPubKeyBytes.Write(Script.OpCheckSig); t.AddOutput(new TransactionOutput(n, t, scriptPubKeyBytes.ToArray())); } genesisBlock.AddTransaction(t); return genesisBlock; }
/// <summary> /// Called by the <see cref="BlockChain"/> when we receive a new block that sends coins to one of our addresses or /// spends coins from one of our addresses (note that a single transaction can do both). /// </summary> /// <remarks> /// This is necessary for the internal book-keeping Wallet does. When a transaction is received that sends us /// coins it is added to a pool so we can use it later to create spends. When a transaction is received that /// consumes outputs they are marked as spent so they won't be used in future.<p/> /// A transaction that spends our own coins can be received either because a spend we created was accepted by the /// network and thus made it into a block, or because our keys are being shared between multiple instances and /// some other node spent the coins instead. We still have to know about that to avoid accidentally trying to /// double spend.<p/> /// A transaction may be received multiple times if is included into blocks in parallel chains. The blockType /// parameter describes whether the containing block is on the main/best chain or whether it's on a presently /// inactive side chain. We must still record these transactions and the blocks they appear in because a future /// block might change which chain is best causing a reorganize. A re-org can totally change our balance! /// </remarks> /// <exception cref="VerificationException"/> /// <exception cref="ScriptException"/> internal void Receive(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType) { lock (this) { Receive(tx, block, blockType, false); } }
/// <summary> /// Broadcast a transaction to all connected peers. /// </summary> /// <returns>Whether we sent to at least one peer.</returns> public bool BroadcastTransaction(Transaction tx) { var success = false; lock (_peers) { foreach (var peer in _peers) { try { peer.BroadcastTransaction(tx); success = true; } catch (IOException e) { _log.Error("failed to broadcast to " + peer, e); } } } return success; }
/// <summary> /// If the transactions outputs are all marked as spent, and it's in the unspent map, move it. /// </summary> private void MaybeMoveTxToSpent(Transaction tx, String context) { if (tx.IsEveryOutputSpent()) { // There's nothing left I can spend in this transaction. if (Unspent.Remove(tx.Hash)) { if (_log.IsInfoEnabled) { _log.Info(" " + context + " <-unspent"); _log.Info(" " + context + " ->spent"); } Spent[tx.Hash] = tx; } } }
private void ReprocessTxAfterReorg(IDictionary<Sha256Hash, Transaction> pool, Transaction tx) { _log.InfoFormat(" TX {0}", tx.HashAsString); var numInputs = tx.Inputs.Count; var noSuchTx = 0; var success = 0; var isDead = false; foreach (var input in tx.Inputs) { if (input.IsCoinBase) { // Input is not in our wallet so there is "no such input tx", bit of an abuse. noSuchTx++; continue; } var result = input.Connect(pool, false); if (result == TransactionInput.ConnectionResult.Success) { success++; } else if (result == TransactionInput.ConnectionResult.NoSuchTx) { noSuchTx++; } else if (result == TransactionInput.ConnectionResult.AlreadySpent) { isDead = true; // This transaction was replaced by a double spend on the new chain. Did you just reverse // your own transaction? I hope not!! _log.Info(" ->dead, will not confirm now unless there's another re-org"); var doubleSpent = input.GetConnectedOutput(pool); var replacement = doubleSpent.SpentBy.ParentTransaction; _dead[tx.Hash] = tx; Pending.Remove(tx.Hash); // Inform the event listeners of the newly dead tx. if (DeadTransaction != null) { lock (DeadTransaction) { DeadTransaction(this, new WalletDeadTransactionEventArgs(tx, replacement)); } } break; } } if (isDead) return; if (noSuchTx == numInputs) { _log.Info(" ->inactive"); _inactive[tx.Hash] = tx; } else if (success == numInputs - noSuchTx) { // All inputs are either valid for spending or don't come from us. Miners are trying to re-include it. _log.Info(" ->pending"); Pending[tx.Hash] = tx; _dead.Remove(tx.Hash); } }
/// <exception cref="VerificationException"/> /// <exception cref="ScriptException"/> private void Receive(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, bool reorg) { lock (this) { // Runs in a peer thread. var prevBalance = GetBalance(); var txHash = tx.Hash; var bestChain = blockType == BlockChain.NewBlockType.BestChain; var sideChain = blockType == BlockChain.NewBlockType.SideChain; var valueSentFromMe = tx.GetValueSentFromMe(this); var valueSentToMe = tx.GetValueSentToMe(this); var valueDifference = (long) (valueSentToMe - valueSentFromMe); if (!reorg) { _log.InfoFormat("Received tx{0} for {1} BTC: {2}", sideChain ? " on a side chain" : "", Utils.BitcoinValueToFriendlyString(valueDifference), tx.HashAsString); } // If this transaction is already in the wallet we may need to move it into a different pool. At the very // least we need to ensure we're manipulating the canonical object rather than a duplicate. Transaction wtx; if (Pending.TryGetValue(txHash, out wtx)) { Pending.Remove(txHash); _log.Info(" <-pending"); // A transaction we created appeared in a block. Probably this is a spend we broadcast that has been // accepted by the network. // // Mark the tx as appearing in this block so we can find it later after a re-org. wtx.AddBlockAppearance(block); if (bestChain) { if (valueSentToMe.Equals(0)) { // There were no change transactions so this tx is fully spent. _log.Info(" ->spent"); Debug.Assert(!Spent.ContainsKey(wtx.Hash), "TX in both pending and spent pools"); Spent[wtx.Hash] = wtx; } else { // There was change back to us, or this tx was purely a spend back to ourselves (perhaps for // anonymization purposes). _log.Info(" ->unspent"); Debug.Assert(!Unspent.ContainsKey(wtx.Hash), "TX in both pending and unspent pools"); Unspent[wtx.Hash] = wtx; } } else if (sideChain) { // The transaction was accepted on an inactive side chain, but not yet by the best chain. _log.Info(" ->inactive"); // It's OK for this to already be in the inactive pool because there can be multiple independent side // chains in which it appears: // // b1 --> b2 // \-> b3 // \-> b4 (at this point it's already present in 'inactive' if (_inactive.ContainsKey(wtx.Hash)) _log.Info("Saw a transaction be incorporated into multiple independent side chains"); _inactive[wtx.Hash] = wtx; // Put it back into the pending pool, because 'pending' means 'waiting to be included in best chain'. Pending[wtx.Hash] = wtx; } } else { if (!reorg) { // Mark the tx as appearing in this block so we can find it later after a re-org. tx.AddBlockAppearance(block); } // This TX didn't originate with us. It could be sending us coins and also spending our own coins if keys // are being shared between different wallets. if (sideChain) { _log.Info(" ->inactive"); _inactive[tx.Hash] = tx; } else if (bestChain) { ProcessTxFromBestChain(tx); } } _log.InfoFormat("Balance is now: {0}", Utils.BitcoinValueToFriendlyString(GetBalance())); // Inform anyone interested that we have new coins. Note: we may be re-entered by the event listener, // so we must not make assumptions about our state after this loop returns! For example, // the balance we just received might already be spent! if (!reorg && bestChain && valueDifference > 0 && CoinsReceived != null) { lock (CoinsReceived) { CoinsReceived(this, new WalletCoinsReceivedEventArgs(tx, prevBalance, GetBalance())); } } } }
/// <summary> /// Deserializes an input message. This is usually part of a transaction message. /// </summary> /// <exception cref="ProtocolException"/> public TransactionInput(NetworkParameters @params, Transaction parentTransaction, byte[] payload, int offset) : base(@params, payload, offset) { ParentTransaction = parentTransaction; }