/// <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> /// 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; } }