github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/core/tx_journal.go (about)

     1  package core
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/quickchainproject/quickchain/common"
     9  	"github.com/quickchainproject/quickchain/core/types"
    10  	"github.com/quickchainproject/quickchain/log"
    11  	"github.com/quickchainproject/quickchain/rlp"
    12  )
    13  
    14  // errNoActiveJournal is returned if a transaction is attempted to be inserted
    15  // into the journal, but no such file is currently open.
    16  var errNoActiveJournal = errors.New("no active journal")
    17  
    18  // devNull is a WriteCloser that just discards anything written into it. Its
    19  // goal is to allow the transaction journal to write into a fake journal when
    20  // loading transactions on startup without printing warnings due to no file
    21  // being readt for write.
    22  type devNull struct{}
    23  
    24  func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
    25  func (*devNull) Close() error                      { return nil }
    26  
    27  // txJournal is a rotating log of transactions with the aim of storing locally
    28  // created transactions to allow non-executed ones to survive node restarts.
    29  type txJournal struct {
    30  	path   string         // Filesystem path to store the transactions at
    31  	writer io.WriteCloser // Output stream to write new transactions into
    32  }
    33  
    34  // newTxJournal creates a new transaction journal to
    35  func newTxJournal(path string) *txJournal {
    36  	return &txJournal{
    37  		path: path,
    38  	}
    39  }
    40  
    41  // load parses a transaction journal dump from disk, loading its contents into
    42  // the specified pool.
    43  func (journal *txJournal) load(add func(*types.Transaction) error) error {
    44  	// Skip the parsing if the journal file doens't exist at all
    45  	if _, err := os.Stat(journal.path); os.IsNotExist(err) {
    46  		return nil
    47  	}
    48  	// Open the journal for loading any past transactions
    49  	input, err := os.Open(journal.path)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	defer input.Close()
    54  
    55  	// Temporarily discard any journal additions (don't double add on load)
    56  	journal.writer = new(devNull)
    57  	defer func() { journal.writer = nil }()
    58  
    59  	// Inject all transactions from the journal into the pool
    60  	stream := rlp.NewStream(input, 0)
    61  	total, dropped := 0, 0
    62  
    63  	var failure error
    64  	for {
    65  		// Parse the next transaction and terminate on error
    66  		tx := new(types.Transaction)
    67  		if err = stream.Decode(tx); err != nil {
    68  			if err != io.EOF {
    69  				failure = err
    70  			}
    71  			break
    72  		}
    73  		// Import the transaction and bump the appropriate progress counters
    74  		total++
    75  		if err = add(tx); err != nil {
    76  			log.Debug("Failed to add journaled transaction", "err", err)
    77  			dropped++
    78  			continue
    79  		}
    80  	}
    81  	log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
    82  
    83  	return failure
    84  }
    85  
    86  // insert adds the specified transaction to the local disk journal.
    87  func (journal *txJournal) insert(tx *types.Transaction) error {
    88  	if journal.writer == nil {
    89  		return errNoActiveJournal
    90  	}
    91  	if err := rlp.Encode(journal.writer, tx); err != nil {
    92  		return err
    93  	}
    94  	return nil
    95  }
    96  
    97  // rotate regenerates the transaction journal based on the current contents of
    98  // the transaction pool.
    99  func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error {
   100  	// Close the current journal (if any is open)
   101  	if journal.writer != nil {
   102  		if err := journal.writer.Close(); err != nil {
   103  			return err
   104  		}
   105  		journal.writer = nil
   106  	}
   107  	// Generate a new journal with the contents of the current pool
   108  	replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	journaled := 0
   113  	for _, txs := range all {
   114  		for _, tx := range txs {
   115  			if err = rlp.Encode(replacement, tx); err != nil {
   116  				replacement.Close()
   117  				return err
   118  			}
   119  		}
   120  		journaled += len(txs)
   121  	}
   122  	replacement.Close()
   123  
   124  	// Replace the live journal with the newly generated one
   125  	if err = os.Rename(journal.path+".new", journal.path); err != nil {
   126  		return err
   127  	}
   128  	sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	journal.writer = sink
   133  	log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all))
   134  
   135  	return nil
   136  }
   137  
   138  // close flushes the transaction journal contents to disk and closes the file.
   139  func (journal *txJournal) close() error {
   140  	var err error
   141  
   142  	if journal.writer != nil {
   143  		err = journal.writer.Close()
   144  		journal.writer = nil
   145  	}
   146  	return err
   147  }