/// <exception cref="BitcoinSharp.Core.Exceptions.VerificationException"/> /// <exception cref="BitcoinSharp.Core.Exceptions.ScriptException"/> private void Receive(Transaction transaction, StoredBlock storedBlock, BlockChain.NewBlockType blockType, bool reorg) { lock (this) { // Runs in a peer thread. var previousBalance = GetBalance(); var transactionHash = transaction.Hash; var bestChain = blockType == BlockChain.NewBlockType.BestChain; var sideChain = blockType == BlockChain.NewBlockType.SideChain; var valueSentFromMe = transaction.GetValueSentFromMe(this); var valueSentToMe = transaction.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), transaction.HashAsString); } // If this transaction is already in the wallet we may need to move it into a different WalletPool. At the very // least we need to ensure we're manipulating the canonical object rather than a duplicate. Transaction walletTransaction; if (Pending.TryGetValue(transactionHash, out walletTransaction)) { Pending.Remove(transactionHash); 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. walletTransaction.AddBlockAppearance(storedBlock); if (bestChain) { if (valueSentToMe.Equals(0)) { // There were no change transactions so this tx is fully spent. Log.Info(" ->spent"); Debug.Assert(!Spent.ContainsKey(walletTransaction.Hash), "TX in both pending and spent pools"); Spent[walletTransaction.Hash] = walletTransaction; } 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(walletTransaction.Hash), "TX in both pending and unspent pools"); Unspent[walletTransaction.Hash] = walletTransaction; } } 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 WalletPool 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(walletTransaction.Hash)) Log.Info("Saw a transaction be incorporated into multiple independent side chains"); _inactive[walletTransaction.Hash] = walletTransaction; // Put it back into the pending WalletPool, because 'pending' means 'waiting to be included in best chain'. Pending[walletTransaction.Hash] = walletTransaction; } } else { if (!reorg) { // Mark the tx as appearing in this block so we can find it later after a re-org. transaction.AddBlockAppearance(storedBlock); } // 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[transaction.Hash] = transaction; } else if (bestChain) { ProcessTransactionFromBestChain(transaction); } } 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(transaction, previousBalance, 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="BitcoinSharp.Core.Exceptions.VerificationException"/> private void ProcessTransactionFromBestChain(Transaction transaction) { // 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(transaction); if (!transaction.GetValueSentToMe(this).Equals(0)) { // It's sending us coins. Log.Info(" new tx ->unspent"); Debug.Assert(!Unspent.ContainsKey(transaction.Hash), "TX was received twice"); Unspent[transaction.Hash] = transaction; } else { // It spent some of our coins and did not send us any. Log.Info(" new tx ->spent"); Debug.Assert(!Spent.ContainsKey(transaction.Hash), "TX was received twice"); Spent[transaction.Hash] = transaction; } }