github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/core/tx_journal.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-wtc library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-wtc library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "errors" 21 "io" 22 "os" 23 24 "github.com/wtc/go-wtc/common" 25 "github.com/wtc/go-wtc/core/types" 26 "github.com/wtc/go-wtc/log" 27 "github.com/wtc/go-wtc/rlp" 28 ) 29 30 // errNoActiveJournal is returned if a transaction is attempted to be inserted 31 // into the journal, but no such file is currently open. 32 var errNoActiveJournal = errors.New("no active journal") 33 34 // txJournal is a rotating log of transactions with the aim of storing locally 35 // created transactions to allow non-executed ones to survive node restarts. 36 type txJournal struct { 37 path string // Filesystem path to store the transactions at 38 writer io.WriteCloser // Output stream to write new transactions into 39 } 40 41 // newTxJournal creates a new transaction journal to 42 func newTxJournal(path string) *txJournal { 43 return &txJournal{ 44 path: path, 45 } 46 } 47 48 // load parses a transaction journal dump from disk, loading its contents into 49 // the specified pool. 50 func (journal *txJournal) load(add func(*types.Transaction) error) error { 51 // Skip the parsing if the journal file doens't exist at all 52 if _, err := os.Stat(journal.path); os.IsNotExist(err) { 53 return nil 54 } 55 // Open the journal for loading any past transactions 56 input, err := os.Open(journal.path) 57 if err != nil { 58 return err 59 } 60 defer input.Close() 61 62 // Inject all transactions from the journal into the pool 63 stream := rlp.NewStream(input, 0) 64 total, dropped := 0, 0 65 66 var failure error 67 for { 68 // Parse the next transaction and terminate on error 69 tx := new(types.Transaction) 70 if err = stream.Decode(tx); err != nil { 71 if err != io.EOF { 72 failure = err 73 } 74 break 75 } 76 // Import the transaction and bump the appropriate progress counters 77 total++ 78 if err = add(tx); err != nil { 79 log.Debug("Failed to add journaled transaction", "err", err) 80 dropped++ 81 continue 82 } 83 } 84 // log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) 85 86 return failure 87 } 88 89 // insert adds the specified transaction to the local disk journal. 90 func (journal *txJournal) insert(tx *types.Transaction) error { 91 if journal.writer == nil { 92 return errNoActiveJournal 93 } 94 if err := rlp.Encode(journal.writer, tx); err != nil { 95 return err 96 } 97 return nil 98 } 99 100 // rotate regenerates the transaction journal based on the current contents of 101 // the transaction pool. 102 func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { 103 // Close the current journal (if any is open) 104 if journal.writer != nil { 105 if err := journal.writer.Close(); err != nil { 106 return err 107 } 108 journal.writer = nil 109 } 110 // Generate a new journal with the contents of the current pool 111 replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 112 if err != nil { 113 return err 114 } 115 journaled := 0 116 for _, txs := range all { 117 for _, tx := range txs { 118 if err = rlp.Encode(replacement, tx); err != nil { 119 replacement.Close() 120 return err 121 } 122 } 123 journaled += len(txs) 124 } 125 replacement.Close() 126 127 // Replace the live journal with the newly generated one 128 if err = os.Rename(journal.path+".new", journal.path); err != nil { 129 return err 130 } 131 sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755) 132 if err != nil { 133 return err 134 } 135 journal.writer = sink 136 // log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) 137 138 return nil 139 } 140 141 // close flushes the transaction journal contents to disk and closes the file. 142 func (journal *txJournal) close() error { 143 var err error 144 145 if journal.writer != nil { 146 err = journal.writer.Close() 147 journal.writer = nil 148 } 149 return err 150 }