github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/transactionpool/update.go (about)

     1  package transactionpool
     2  
     3  import (
     4  	"github.com/NebulousLabs/Sia/modules"
     5  	"github.com/NebulousLabs/Sia/types"
     6  
     7  	"github.com/NebulousLabs/bolt"
     8  )
     9  
    10  // purge removes all transactions from the transaction pool.
    11  func (tp *TransactionPool) purge() {
    12  	tp.knownObjects = make(map[ObjectID]TransactionSetID)
    13  	tp.transactionSets = make(map[TransactionSetID][]types.Transaction)
    14  	tp.transactionSetDiffs = make(map[TransactionSetID]modules.ConsensusChange)
    15  	tp.transactionListSize = 0
    16  }
    17  
    18  // ProcessConsensusChange gets called to inform the transaction pool of changes
    19  // to the consensus set.
    20  func (tp *TransactionPool) ProcessConsensusChange(cc modules.ConsensusChange) {
    21  	tp.mu.Lock()
    22  
    23  	// Update the database of confirmed transactions.
    24  	err := tp.db.Update(func(tx *bolt.Tx) error {
    25  		for _, block := range cc.RevertedBlocks {
    26  			for _, txn := range block.Transactions {
    27  				err := tp.deleteTransaction(tx, txn.ID())
    28  				if err != nil {
    29  					return err
    30  				}
    31  			}
    32  		}
    33  		for _, block := range cc.AppliedBlocks {
    34  			for _, txn := range block.Transactions {
    35  				err := tp.addTransaction(tx, txn.ID())
    36  				if err != nil {
    37  					return err
    38  				}
    39  			}
    40  		}
    41  		return tp.putRecentConsensusChange(tx, cc.ID)
    42  	})
    43  	if err != nil {
    44  		// TODO: Handle error
    45  	}
    46  
    47  	// Scan the applied blocks for transactions that got accepted. This will
    48  	// help to determine which transactions to remove from the transaction
    49  	// pool. Having this list enables both efficiency improvements and helps to
    50  	// clean out transactions with no dependencies, such as arbitrary data
    51  	// transactions from the host.
    52  	txids := make(map[types.TransactionID]struct{})
    53  	for _, block := range cc.AppliedBlocks {
    54  		for _, txn := range block.Transactions {
    55  			txids[txn.ID()] = struct{}{}
    56  		}
    57  	}
    58  
    59  	// TODO: Right now, transactions that were reverted to not get saved and
    60  	// retried, because some transactions such as storage proofs might be
    61  	// illegal, and there's no good way to preserve dependencies when illegal
    62  	// transactions are suddenly involved.
    63  	//
    64  	// One potential solution is to have modules manually do resubmission if
    65  	// something goes wrong. Another is to have the transaction pool remember
    66  	// recent transaction sets on the off chance that they become valid again
    67  	// due to a reorg.
    68  	//
    69  	// Another option is to scan through the blocks transactions one at a time
    70  	// check if they are valid. If so, lump them in a set with the next guy.
    71  	// When they stop being valid, you've found a guy to throw away. It's n^2
    72  	// in the number of transactions in the block.
    73  
    74  	// Save all of the current unconfirmed transaction sets into a list.
    75  	var unconfirmedSets [][]types.Transaction
    76  	for _, tSet := range tp.transactionSets {
    77  		// Compile a new transaction set the removes all transactions duplicated
    78  		// in the block. Though mostly handled by the dependency manager in the
    79  		// transaction pool, this should both improve efficiency and will strip
    80  		// out duplicate transactions with no dependencies (arbitrary data only
    81  		// transactions)
    82  		var newTSet []types.Transaction
    83  		for _, txn := range tSet {
    84  			_, exists := txids[txn.ID()]
    85  			if !exists {
    86  				newTSet = append(newTSet, txn)
    87  			}
    88  		}
    89  		unconfirmedSets = append(unconfirmedSets, newTSet)
    90  	}
    91  
    92  	// Purge the transaction pool. Some of the transactions sets may be invalid
    93  	// after the consensus change.
    94  	tp.purge()
    95  
    96  	// Add all of the unconfirmed transaction sets back to the transaction
    97  	// pool. The ones that are invalid will throw an error and will not be
    98  	// re-added.
    99  	//
   100  	// Accepting a transaction set requires locking the consensus set (to check
   101  	// validity). But, ProcessConsensusChange is only called when the consensus
   102  	// set is already locked, causing a deadlock problem. Therefore,
   103  	// transactions are readded to the pool in a goroutine, so that this
   104  	// function can finish and consensus can unlock. The tpool lock is held
   105  	// however until the goroutine completes.
   106  	//
   107  	// Which means that no other modules can require a tpool lock when
   108  	// processing consensus changes. Overall, the locking is pretty fragile and
   109  	// more rules need to be put in place.
   110  	for _, set := range unconfirmedSets {
   111  		tp.acceptTransactionSet(set) // Error is not checked.
   112  	}
   113  
   114  	// Inform subscribers that an update has executed.
   115  	tp.mu.Demote()
   116  	tp.updateSubscribersTransactions()
   117  	tp.mu.DemotedUnlock()
   118  }
   119  
   120  // PurgeTransactionPool deletes all transactions from the transaction pool.
   121  func (tp *TransactionPool) PurgeTransactionPool() {
   122  	tp.mu.Lock()
   123  	tp.purge()
   124  	tp.mu.Unlock()
   125  }