예제 #1
0
 /// <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)
 {
 }
예제 #2
0
        /// <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()));
                    }
                }
            }
        }
예제 #3
0
 /// <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);
     }
 }
예제 #4
0
 /// <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)
 {
 }
예제 #5
0
        /// <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());
        }
예제 #6
0
 /// <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)
 {
 }