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 }