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()));
}
}
}
}