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  }