/// <summary> /// Creates a PeerGroup with the given parameters and a default 5 second connection timeout. /// </summary> public PeerGroup(IBlockStore blockStore, NetworkParameters @params, BlockChain chain) : this(blockStore, @params, chain, DefaultConnectionDelayMillis) { }
/// <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> /// 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> /// Construct a peer that handles the given network connection and reads/writes from the given block chain. Note that /// communication won't occur until you call connect(). /// </summary> public Peer(NetworkParameters @params, PeerAddress address, BlockChain blockChain) : this(@params, address, 0, blockChain) { }
/// <summary> /// Creates a PeerGroup with the given parameters. The connectionDelayMillis parameter controls how long the /// PeerGroup will wait between attempts to connect to nodes or read from any added peer discovery sources. /// </summary> public PeerGroup(IBlockStore blockStore, NetworkParameters @params, BlockChain chain, int connectionDelayMillis) { _blockStore = blockStore; _params = @params; _chain = chain; _connectionDelayMillis = connectionDelayMillis; _inactives = new LinkedBlockingQueue<PeerAddress>(); _peers = new SynchronizedHashSet<Peer>(); _peerDiscoverers = new SynchronizedHashSet<IPeerDiscovery>(); _peerPool = new ThreadPoolExecutor(_coreThreads, _defaultConnections, TimeSpan.FromSeconds(_threadKeepAliveSeconds), new LinkedBlockingQueue<IRunnable>(1), new PeerGroupThreadFactory()); }