github.com/klaytn/klaytn@v1.12.1/node/sc/bridge_addr_journal.go (about)

     1  // Modifications Copyright 2019 The klaytn Authors
     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  // This file is derived from core/tx_journal.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package sc
    22  
    23  import (
    24  	"errors"
    25  	"io"
    26  	"os"
    27  	"strings"
    28  	"sync"
    29  
    30  	"github.com/klaytn/klaytn/common"
    31  	"github.com/klaytn/klaytn/node/sc/bridgepool"
    32  	"github.com/klaytn/klaytn/rlp"
    33  )
    34  
    35  var (
    36  	ErrNoActiveAddressJournal = errors.New("no active address journal")
    37  	ErrDuplicatedJournal      = errors.New("duplicated journal is inserted")
    38  	ErrDuplicatedAlias        = errors.New("duplicated alias")
    39  	ErrEmptyBridgeAddress     = errors.New("empty bridge address is not allowed")
    40  	ErrEmptyJournalCache      = errors.New("empty bridge journal")
    41  	ErrEmptyBridgeAlias       = errors.New("empty bridge Alias")
    42  	ErrNotAllowedAliasFormat  = errors.New("Not allowed bridge alias format")
    43  )
    44  
    45  // bridgeAddrJournal is a rotating log of addresses with the aim of storing locally
    46  // created addresses to allow deployed bridge contracts to survive node restarts.
    47  type bridgeAddrJournal struct {
    48  	path       string         // Filesystem path to store the addresses at
    49  	writer     io.WriteCloser // Output stream to write new addresses into
    50  	cache      map[common.Address]*BridgeJournal
    51  	aliasCache map[string]common.Address
    52  	writerMu   *sync.Mutex
    53  	cacheMu    *sync.RWMutex
    54  }
    55  
    56  // newBridgeAddrJournal creates a new bridge addr journal to
    57  func newBridgeAddrJournal(path string) *bridgeAddrJournal {
    58  	return &bridgeAddrJournal{
    59  		path:       path,
    60  		cache:      make(map[common.Address]*BridgeJournal),
    61  		aliasCache: make(map[string]common.Address),
    62  		writerMu:   &sync.Mutex{},
    63  		cacheMu:    &sync.RWMutex{},
    64  	}
    65  }
    66  
    67  // load parses a address journal dump from disk, loading its contents into
    68  // the specified pool.
    69  func (journal *bridgeAddrJournal) load(add func(journal BridgeJournal) error) error {
    70  	journal.writerMu.Lock()
    71  	defer journal.writerMu.Unlock()
    72  	// Skip the parsing if the journal file doens't exist at all
    73  	if _, err := os.Stat(journal.path); os.IsNotExist(err) {
    74  		return nil
    75  	}
    76  	// Open the journal for loading any past addresses
    77  	input, err := os.Open(journal.path)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	defer input.Close()
    82  
    83  	// Temporarily discard any journal additions (don't double add on load)
    84  	journal.writer = new(bridgepool.DevNull)
    85  	defer func() { journal.writer = nil }()
    86  
    87  	// Inject all addresses from the journal into the pool
    88  	stream := rlp.NewStream(input, 0)
    89  	total, dropped := 0, 0
    90  
    91  	var (
    92  		failure              error
    93  		aliasBridgeDecodeErr = false
    94  	)
    95  	for {
    96  		// Parse the next address and terminate on error
    97  		addr := new(BridgeJournal)
    98  		if aliasBridgeDecodeErr {
    99  			addr.isLegacyBridgeJournal = true
   100  		}
   101  		if err = stream.Decode(addr); err != nil {
   102  			if err == io.EOF {
   103  				break
   104  			} else if err == ErrBridgeAliasFormatDecode {
   105  				input.Close()
   106  				input, err = os.Open(journal.path)
   107  				if err != nil {
   108  					failure = err
   109  					break
   110  				}
   111  				aliasBridgeDecodeErr = true
   112  				stream.Reset(input, 0)
   113  				continue
   114  			} else {
   115  				failure = err
   116  				break
   117  			}
   118  		}
   119  
   120  		total++
   121  
   122  		if err := add(*addr); err != nil {
   123  			failure = err
   124  			dropped++
   125  		}
   126  	}
   127  	logger.Info("Loaded local bridge journal", "addrs", total, "dropped", dropped)
   128  
   129  	return failure
   130  }
   131  
   132  // ChangeBridgeAlias changes oldBridgeAlias to newBridgeAlias
   133  func (journal *bridgeAddrJournal) ChangeBridgeAlias(oldBridgeAlias, newBridgeAlias string) error {
   134  	journal.cacheMu.Lock()
   135  	defer journal.cacheMu.Unlock()
   136  	if addr, ok := journal.aliasCache[oldBridgeAlias]; ok {
   137  		delete(journal.aliasCache, oldBridgeAlias)
   138  		journal.aliasCache[newBridgeAlias] = addr
   139  		journal.cache[addr].BridgeAlias = newBridgeAlias
   140  		return nil
   141  	}
   142  	return ErrEmptyBridgeAlias
   143  }
   144  
   145  // insert adds the specified address to the local disk journal.
   146  func (journal *bridgeAddrJournal) insert(bridgeAlias string, localAddress common.Address, remoteAddress common.Address) error {
   147  	// lock order is important
   148  	journal.cacheMu.Lock()
   149  	journal.writerMu.Lock()
   150  
   151  	defer func() {
   152  		journal.cacheMu.Unlock()
   153  		journal.writerMu.Unlock()
   154  	}()
   155  
   156  	if strings.HasPrefix(bridgeAlias, "0x") {
   157  		return ErrNotAllowedAliasFormat
   158  	}
   159  	if len(bridgeAlias) != 0 && journal.aliasCache[bridgeAlias] != (common.Address{}) {
   160  		return ErrDuplicatedAlias
   161  	}
   162  
   163  	if journal.cache[localAddress] != nil {
   164  		return ErrDuplicatedJournal
   165  	}
   166  	if journal.writer == nil {
   167  		return ErrNoActiveAddressJournal
   168  	}
   169  	empty := common.Address{}
   170  	if localAddress == empty || remoteAddress == empty {
   171  		return ErrEmptyBridgeAddress
   172  	}
   173  	// TODO-Klaytn-ServiceChain: support false paired
   174  	item := BridgeJournal{
   175  		bridgeAlias,
   176  		localAddress,
   177  		remoteAddress,
   178  		false,
   179  		false,
   180  	}
   181  	if err := rlp.Encode(journal.writer, &item); err != nil {
   182  		return err
   183  	}
   184  
   185  	journal.cache[localAddress] = &item
   186  	if len(bridgeAlias) != 0 {
   187  		journal.aliasCache[bridgeAlias] = localAddress
   188  	}
   189  	return nil
   190  }
   191  
   192  // rotate regenerates the addresses journal based on the current contents of
   193  // the address pool.
   194  func (journal *bridgeAddrJournal) rotate(all []*BridgeJournal) error {
   195  	journal.writerMu.Lock()
   196  	defer journal.writerMu.Unlock()
   197  	// Close the current journal (if any is open)
   198  	if journal.writer != nil {
   199  		if err := journal.writer.Close(); err != nil {
   200  			return err
   201  		}
   202  		journal.writer = nil
   203  	}
   204  	// Generate a new journal with the contents of the current pool
   205  	replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	journaled := 0
   210  	for _, journal := range all {
   211  		if err = rlp.Encode(replacement, journal); err != nil {
   212  			replacement.Close()
   213  			return err
   214  		}
   215  		journaled++
   216  	}
   217  	replacement.Close()
   218  
   219  	// Replace the live journal with the newly generated one
   220  	if err = os.Rename(journal.path+".new", journal.path); err != nil {
   221  		return err
   222  	}
   223  	sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0o755)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	journal.writer = sink
   228  	logger.Info("Regenerated local addr journal", "addrs", journaled, "accounts", len(all))
   229  
   230  	return nil
   231  }
   232  
   233  // close flushes the addresses journal contents to disk and closes the file.
   234  func (journal *bridgeAddrJournal) close() error {
   235  	journal.writerMu.Lock()
   236  	defer journal.writerMu.Unlock()
   237  
   238  	var err error
   239  	if journal.writer != nil {
   240  		err = journal.writer.Close()
   241  		journal.writer = nil
   242  	}
   243  	return err
   244  }