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  }