gitlab.com/flarenetwork/coreth@v0.1.1/core/tx_journal.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package core 28 29 import ( 30 "errors" 31 "io" 32 "os" 33 34 "github.com/ethereum/go-ethereum/common" 35 "github.com/ethereum/go-ethereum/log" 36 "github.com/ethereum/go-ethereum/rlp" 37 "gitlab.com/flarenetwork/coreth/core/types" 38 ) 39 40 // errNoActiveJournal is returned if a transaction is attempted to be inserted 41 // into the journal, but no such file is currently open. 42 var errNoActiveJournal = errors.New("no active journal") 43 44 // devNull is a WriteCloser that just discards anything written into it. Its 45 // goal is to allow the transaction journal to write into a fake journal when 46 // loading transactions on startup without printing warnings due to no file 47 // being read for write. 48 type devNull struct{} 49 50 func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil } 51 func (*devNull) Close() error { return nil } 52 53 // txJournal is a rotating log of transactions with the aim of storing locally 54 // created transactions to allow non-executed ones to survive node restarts. 55 type txJournal struct { 56 path string // Filesystem path to store the transactions at 57 writer io.WriteCloser // Output stream to write new transactions into 58 } 59 60 // newTxJournal creates a new transaction journal to 61 func newTxJournal(path string) *txJournal { 62 return &txJournal{ 63 path: path, 64 } 65 } 66 67 // load parses a transaction journal dump from disk, loading its contents into 68 // the specified pool. 69 func (journal *txJournal) load(add func([]*types.Transaction) []error) error { 70 // Skip the parsing if the journal file doesn't exist at all 71 if _, err := os.Stat(journal.path); os.IsNotExist(err) { 72 return nil 73 } 74 // Open the journal for loading any past transactions 75 input, err := os.Open(journal.path) 76 if err != nil { 77 return err 78 } 79 defer input.Close() 80 81 // Temporarily discard any journal additions (don't double add on load) 82 journal.writer = new(devNull) 83 defer func() { journal.writer = nil }() 84 85 // Inject all transactions from the journal into the pool 86 stream := rlp.NewStream(input, 0) 87 total, dropped := 0, 0 88 89 // Create a method to load a limited batch of transactions and bump the 90 // appropriate progress counters. Then use this method to load all the 91 // journaled transactions in small-ish batches. 92 loadBatch := func(txs types.Transactions) { 93 for _, err := range add(txs) { 94 if err != nil { 95 log.Debug("Failed to add journaled transaction", "err", err) 96 dropped++ 97 } 98 } 99 } 100 var ( 101 failure error 102 batch types.Transactions 103 ) 104 for { 105 // Parse the next transaction and terminate on error 106 tx := new(types.Transaction) 107 if err = stream.Decode(tx); err != nil { 108 if err != io.EOF { 109 failure = err 110 } 111 if batch.Len() > 0 { 112 loadBatch(batch) 113 } 114 break 115 } 116 // New transaction parsed, queue up for later, import if threshold is reached 117 total++ 118 119 if batch = append(batch, tx); batch.Len() > 1024 { 120 loadBatch(batch) 121 batch = batch[:0] 122 } 123 } 124 log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) 125 126 return failure 127 } 128 129 // insert adds the specified transaction to the local disk journal. 130 func (journal *txJournal) insert(tx *types.Transaction) error { 131 if journal.writer == nil { 132 return errNoActiveJournal 133 } 134 if err := rlp.Encode(journal.writer, tx); err != nil { 135 return err 136 } 137 return nil 138 } 139 140 // rotate regenerates the transaction journal based on the current contents of 141 // the transaction pool. 142 func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { 143 // Close the current journal (if any is open) 144 if journal.writer != nil { 145 if err := journal.writer.Close(); err != nil { 146 return err 147 } 148 journal.writer = nil 149 } 150 // Generate a new journal with the contents of the current pool 151 replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 152 if err != nil { 153 return err 154 } 155 journaled := 0 156 for _, txs := range all { 157 for _, tx := range txs { 158 if err = rlp.Encode(replacement, tx); err != nil { 159 replacement.Close() 160 return err 161 } 162 } 163 journaled += len(txs) 164 } 165 replacement.Close() 166 167 // Replace the live journal with the newly generated one 168 if err = os.Rename(journal.path+".new", journal.path); err != nil { 169 return err 170 } 171 sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0644) 172 if err != nil { 173 return err 174 } 175 journal.writer = sink 176 log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) 177 178 return nil 179 } 180 181 // close flushes the transaction journal contents to disk and closes the file. 182 func (journal *txJournal) close() error { 183 var err error 184 185 if journal.writer != nil { 186 err = journal.writer.Close() 187 journal.writer = nil 188 } 189 return err 190 }