github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/core/tx_journal.go (about) 1 package core 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 8 "github.com/quickchainproject/quickchain/common" 9 "github.com/quickchainproject/quickchain/core/types" 10 "github.com/quickchainproject/quickchain/log" 11 "github.com/quickchainproject/quickchain/rlp" 12 ) 13 14 // errNoActiveJournal is returned if a transaction is attempted to be inserted 15 // into the journal, but no such file is currently open. 16 var errNoActiveJournal = errors.New("no active journal") 17 18 // devNull is a WriteCloser that just discards anything written into it. Its 19 // goal is to allow the transaction journal to write into a fake journal when 20 // loading transactions on startup without printing warnings due to no file 21 // being readt for write. 22 type devNull struct{} 23 24 func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil } 25 func (*devNull) Close() error { return nil } 26 27 // txJournal is a rotating log of transactions with the aim of storing locally 28 // created transactions to allow non-executed ones to survive node restarts. 29 type txJournal struct { 30 path string // Filesystem path to store the transactions at 31 writer io.WriteCloser // Output stream to write new transactions into 32 } 33 34 // newTxJournal creates a new transaction journal to 35 func newTxJournal(path string) *txJournal { 36 return &txJournal{ 37 path: path, 38 } 39 } 40 41 // load parses a transaction journal dump from disk, loading its contents into 42 // the specified pool. 43 func (journal *txJournal) load(add func(*types.Transaction) error) error { 44 // Skip the parsing if the journal file doens't exist at all 45 if _, err := os.Stat(journal.path); os.IsNotExist(err) { 46 return nil 47 } 48 // Open the journal for loading any past transactions 49 input, err := os.Open(journal.path) 50 if err != nil { 51 return err 52 } 53 defer input.Close() 54 55 // Temporarily discard any journal additions (don't double add on load) 56 journal.writer = new(devNull) 57 defer func() { journal.writer = nil }() 58 59 // Inject all transactions from the journal into the pool 60 stream := rlp.NewStream(input, 0) 61 total, dropped := 0, 0 62 63 var failure error 64 for { 65 // Parse the next transaction and terminate on error 66 tx := new(types.Transaction) 67 if err = stream.Decode(tx); err != nil { 68 if err != io.EOF { 69 failure = err 70 } 71 break 72 } 73 // Import the transaction and bump the appropriate progress counters 74 total++ 75 if err = add(tx); err != nil { 76 log.Debug("Failed to add journaled transaction", "err", err) 77 dropped++ 78 continue 79 } 80 } 81 log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) 82 83 return failure 84 } 85 86 // insert adds the specified transaction to the local disk journal. 87 func (journal *txJournal) insert(tx *types.Transaction) error { 88 if journal.writer == nil { 89 return errNoActiveJournal 90 } 91 if err := rlp.Encode(journal.writer, tx); err != nil { 92 return err 93 } 94 return nil 95 } 96 97 // rotate regenerates the transaction journal based on the current contents of 98 // the transaction pool. 99 func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { 100 // Close the current journal (if any is open) 101 if journal.writer != nil { 102 if err := journal.writer.Close(); err != nil { 103 return err 104 } 105 journal.writer = nil 106 } 107 // Generate a new journal with the contents of the current pool 108 replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 109 if err != nil { 110 return err 111 } 112 journaled := 0 113 for _, txs := range all { 114 for _, tx := range txs { 115 if err = rlp.Encode(replacement, tx); err != nil { 116 replacement.Close() 117 return err 118 } 119 } 120 journaled += len(txs) 121 } 122 replacement.Close() 123 124 // Replace the live journal with the newly generated one 125 if err = os.Rename(journal.path+".new", journal.path); err != nil { 126 return err 127 } 128 sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755) 129 if err != nil { 130 return err 131 } 132 journal.writer = sink 133 log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) 134 135 return nil 136 } 137 138 // close flushes the transaction journal contents to disk and closes the file. 139 func (journal *txJournal) close() error { 140 var err error 141 142 if journal.writer != nil { 143 err = journal.writer.Close() 144 journal.writer = nil 145 } 146 return err 147 }