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 }