github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/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-ethereum 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-ethereum 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/neatlab/neatio/chain/core/types" 25 "github.com/neatlab/neatio/chain/log" 26 neatAbi "github.com/neatlab/neatio/neatabi/abi" 27 "github.com/neatlab/neatio/utilities/common" 28 "github.com/neatlab/neatio/utilities/rlp" 29 ) 30 31 // errNoActiveJournal is returned if a transaction is attempted to be inserted 32 // into the journal, but no such file is currently open. 33 var errNoActiveJournal = errors.New("no active journal") 34 35 // devNull is a WriteCloser that just discards anything written into it. Its 36 // goal is to allow the transaction journal to write into a fake journal when 37 // loading transactions on startup without printing warnings due to no file 38 // being readt for write. 39 type devNull struct{} 40 41 func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil } 42 func (*devNull) Close() error { return nil } 43 44 // txJournal is a rotating log of transactions with the aim of storing locally 45 // created transactions to allow non-executed ones to survive node restarts. 46 type txJournal struct { 47 path string // Filesystem path to store the transactions at 48 writer io.WriteCloser // Output stream to write new transactions into 49 } 50 51 // newTxJournal creates a new transaction journal to 52 func newTxJournal(path string) *txJournal { 53 return &txJournal{ 54 path: path, 55 } 56 } 57 58 // load parses a transaction journal dump from disk, loading its contents into 59 // the specified pool. 60 func (journal *txJournal) load(add func(*types.Transaction) error) error { 61 // Skip the parsing if the journal file doens't exist at all 62 if _, err := os.Stat(journal.path); os.IsNotExist(err) { 63 return nil 64 } 65 // Open the journal for loading any past transactions 66 input, err := os.Open(journal.path) 67 if err != nil { 68 return err 69 } 70 defer input.Close() 71 72 // Temporarily discard any journal additions (don't double add on load) 73 journal.writer = new(devNull) 74 defer func() { journal.writer = nil }() 75 76 // Inject all transactions from the journal into the pool 77 stream := rlp.NewStream(input, 0) 78 total, dropped := 0, 0 79 80 var failure error 81 for { 82 // Parse the next transaction and terminate on error 83 tx := new(types.Transaction) 84 if err = stream.Decode(tx); err != nil { 85 if err != io.EOF { 86 failure = err 87 } 88 break 89 } 90 // TODO: make our own tx to work. 91 // can't pass custom validate logic since Ethereum is still initializing. 92 if neatAbi.IsNeatChainContractAddr(tx.To()) { 93 continue 94 } 95 // Import the transaction and bump the appropriate progress counters 96 total++ 97 if err = add(tx); err != nil { 98 log.Debug("Failed to add journaled transaction", "err", err) 99 dropped++ 100 continue 101 } 102 } 103 //log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) 104 105 return failure 106 } 107 108 // insert adds the specified transaction to the local disk journal. 109 func (journal *txJournal) insert(tx *types.Transaction) error { 110 if journal.writer == nil { 111 return errNoActiveJournal 112 } 113 if err := rlp.Encode(journal.writer, tx); err != nil { 114 return err 115 } 116 return nil 117 } 118 119 // rotate regenerates the transaction journal based on the current contents of 120 // the transaction pool. 121 func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { 122 // Close the current journal (if any is open) 123 if journal.writer != nil { 124 if err := journal.writer.Close(); err != nil { 125 return err 126 } 127 journal.writer = nil 128 } 129 // Generate a new journal with the contents of the current pool 130 replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 131 if err != nil { 132 return err 133 } 134 journaled := 0 135 for _, txs := range all { 136 for _, tx := range txs { 137 if err = rlp.Encode(replacement, tx); err != nil { 138 replacement.Close() 139 return err 140 } 141 } 142 journaled += len(txs) 143 } 144 replacement.Close() 145 146 // Replace the live journal with the newly generated one 147 if err = os.Rename(journal.path+".new", journal.path); err != nil { 148 return err 149 } 150 sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755) 151 if err != nil { 152 return err 153 } 154 journal.writer = sink 155 //log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) 156 157 return nil 158 } 159 160 // close flushes the transaction journal contents to disk and closes the file. 161 func (journal *txJournal) close() error { 162 var err error 163 164 if journal.writer != nil { 165 err = journal.writer.Close() 166 journal.writer = nil 167 } 168 return err 169 }