github.com/cgcardona/r-subnet-evm@v0.1.5/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 "io/fs" 33 "os" 34 35 "github.com/cgcardona/r-subnet-evm/core/types" 36 "github.com/ethereum/go-ethereum/common" 37 "github.com/ethereum/go-ethereum/log" 38 "github.com/ethereum/go-ethereum/rlp" 39 ) 40 41 // errNoActiveJournal is returned if a transaction is attempted to be inserted 42 // into the journal, but no such file is currently open. 43 var errNoActiveJournal = errors.New("no active journal") 44 45 // devNull is a WriteCloser that just discards anything written into it. Its 46 // goal is to allow the transaction journal to write into a fake journal when 47 // loading transactions on startup without printing warnings due to no file 48 // being read for write. 49 type devNull struct{} 50 51 func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil } 52 func (*devNull) Close() error { return nil } 53 54 // txJournal is a rotating log of transactions with the aim of storing locally 55 // created transactions to allow non-executed ones to survive node restarts. 56 type txJournal struct { 57 path string // Filesystem path to store the transactions at 58 writer io.WriteCloser // Output stream to write new transactions into 59 } 60 61 // newTxJournal creates a new transaction journal to 62 func newTxJournal(path string) *txJournal { 63 return &txJournal{ 64 path: path, 65 } 66 } 67 68 // load parses a transaction journal dump from disk, loading its contents into 69 // the specified pool. 70 func (journal *txJournal) load(add func([]*types.Transaction) []error) error { 71 // Open the journal for loading any past transactions 72 input, err := os.Open(journal.path) 73 if errors.Is(err, fs.ErrNotExist) { 74 // Skip the parsing if the journal file doesn't exist at all 75 return nil 76 } 77 if err != nil { 78 return err 79 } 80 defer input.Close() 81 82 // Temporarily discard any journal additions (don't double add on load) 83 journal.writer = new(devNull) 84 defer func() { journal.writer = nil }() 85 86 // Inject all transactions from the journal into the pool 87 stream := rlp.NewStream(input, 0) 88 total, dropped := 0, 0 89 90 // Create a method to load a limited batch of transactions and bump the 91 // appropriate progress counters. Then use this method to load all the 92 // journaled transactions in small-ish batches. 93 loadBatch := func(txs types.Transactions) { 94 for _, err := range add(txs) { 95 if err != nil { 96 log.Debug("Failed to add journaled transaction", "err", err) 97 dropped++ 98 } 99 } 100 } 101 var ( 102 failure error 103 batch types.Transactions 104 ) 105 for { 106 // Parse the next transaction and terminate on error 107 tx := new(types.Transaction) 108 if err = stream.Decode(tx); err != nil { 109 if err != io.EOF { 110 failure = err 111 } 112 if batch.Len() > 0 { 113 loadBatch(batch) 114 } 115 break 116 } 117 // New transaction parsed, queue up for later, import if threshold is reached 118 total++ 119 120 if batch = append(batch, tx); batch.Len() > 1024 { 121 loadBatch(batch) 122 batch = batch[:0] 123 } 124 } 125 log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) 126 127 return failure 128 } 129 130 // insert adds the specified transaction to the local disk journal. 131 func (journal *txJournal) insert(tx *types.Transaction) error { 132 if journal.writer == nil { 133 return errNoActiveJournal 134 } 135 if err := rlp.Encode(journal.writer, tx); err != nil { 136 return err 137 } 138 return nil 139 } 140 141 // rotate regenerates the transaction journal based on the current contents of 142 // the transaction pool. 143 func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { 144 // Close the current journal (if any is open) 145 if journal.writer != nil { 146 if err := journal.writer.Close(); err != nil { 147 return err 148 } 149 journal.writer = nil 150 } 151 // Generate a new journal with the contents of the current pool 152 replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 153 if err != nil { 154 return err 155 } 156 journaled := 0 157 for _, txs := range all { 158 for _, tx := range txs { 159 if err = rlp.Encode(replacement, tx); err != nil { 160 replacement.Close() 161 return err 162 } 163 } 164 journaled += len(txs) 165 } 166 replacement.Close() 167 168 // Replace the live journal with the newly generated one 169 if err = os.Rename(journal.path+".new", journal.path); err != nil { 170 return err 171 } 172 sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0644) 173 if err != nil { 174 return err 175 } 176 journal.writer = sink 177 log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) 178 179 return nil 180 } 181 182 // close flushes the transaction journal contents to disk and closes the file. 183 func (journal *txJournal) close() error { 184 var err error 185 186 if journal.writer != nil { 187 err = journal.writer.Close() 188 journal.writer = nil 189 } 190 return err 191 }