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