github.com/klaytn/klaytn@v1.12.1/node/sc/bridgepool/bridge_tx_pool.go (about) 1 // Modifications Copyright 2019 The klaytn Authors 2 // Copyright 2014 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_pool.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package bridgepool 22 23 import ( 24 "errors" 25 "fmt" 26 "math/big" 27 "sync" 28 "time" 29 30 "github.com/klaytn/klaytn/blockchain/types" 31 "github.com/klaytn/klaytn/common" 32 "github.com/klaytn/klaytn/log" 33 "github.com/rcrowley/go-metrics" 34 ) 35 36 var logger = log.NewModuleLogger(log.ServiceChain) 37 38 var ( 39 ErrKnownTx = errors.New("Known Transaction") 40 ErrUnknownTx = errors.New("Unknown Transaction") 41 ErrDuplicatedNonceTx = errors.New("Duplicated Nonce Transaction") 42 ) 43 44 // TODO-Klaytn-Servicechain Add Metrics 45 var ( 46 // Metrics for the pending pool 47 refusedTxCounter = metrics.NewRegisteredCounter("bridgeTxpool/refuse", nil) 48 ) 49 50 // BridgeTxPoolConfig are the configuration parameters of the transaction pool. 51 type BridgeTxPoolConfig struct { 52 ParentChainID *big.Int 53 Journal string // Journal of local transactions to survive node restarts 54 Rejournal time.Duration // Time interval to regenerate the local transaction journal 55 56 GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts 57 } 58 59 // DefaultBridgeTxPoolConfig contains the default configurations for the transaction 60 // pool. 61 var DefaultBridgeTxPoolConfig = BridgeTxPoolConfig{ 62 ParentChainID: big.NewInt(2018), 63 Journal: "bridge_transactions.rlp", 64 Rejournal: 10 * time.Minute, 65 GlobalQueue: 8192, 66 } 67 68 // sanitize checks the provided user configurations and changes anything that's 69 // unreasonable or unworkable. 70 func (config *BridgeTxPoolConfig) sanitize() BridgeTxPoolConfig { 71 conf := *config 72 if conf.Rejournal < time.Second { 73 logger.Error("Sanitizing invalid bridgetxpool journal time", "provided", conf.Rejournal, "updated", time.Second) 74 conf.Rejournal = time.Second 75 } 76 77 if conf.Journal == "" { 78 logger.Error("Sanitizing invalid bridgetxpool journal file name", "updated", DefaultBridgeTxPoolConfig.Journal) 79 conf.Journal = DefaultBridgeTxPoolConfig.Journal 80 } 81 82 return conf 83 } 84 85 // BridgeTxPool contains all currently known chain transactions. 86 type BridgeTxPool struct { 87 config BridgeTxPoolConfig 88 // TODO-Klaytn-Servicechain consider to remove singer. For now, caused of value transfer tx which don't have `from` value, I leave it. 89 signer types.Signer 90 mu sync.RWMutex 91 // txMu sync.RWMutex // TODO-Klaytn-Servicechain: implement fine-grained locks 92 93 journal *bridgeTxJournal // Journal of transaction to back up to disk 94 95 queue map[common.Address]*ItemSortedMap // Queued but non-processable transactions 96 // TODO-Klaytn-Servicechain refine heartbeat for the tx not for account. 97 all map[common.Hash]*types.Transaction // All transactions to allow lookups 98 99 wg sync.WaitGroup // for shutdown sync 100 closed chan struct{} 101 } 102 103 // NewBridgeTxPool creates a new transaction pool to gather, sort and filter inbound 104 // transactions from the network. 105 func NewBridgeTxPool(config BridgeTxPoolConfig) *BridgeTxPool { 106 // Sanitize the input to ensure no vulnerable gas prices are set 107 config = (&config).sanitize() 108 109 // Create the transaction pool with its initial settings 110 pool := &BridgeTxPool{ 111 config: config, 112 queue: make(map[common.Address]*ItemSortedMap), 113 all: make(map[common.Hash]*types.Transaction), 114 closed: make(chan struct{}), 115 } 116 117 pool.SetLatestSigner(config.ParentChainID) 118 119 // load from disk 120 pool.journal = newBridgeTxJournal(config.Journal) 121 122 if err := pool.journal.load(pool.AddLocals); err != nil { 123 logger.Error("Failed to load chain transaction journal", "err", err) 124 } 125 if err := pool.journal.rotate(pool.Pending()); err != nil { 126 logger.Error("Failed to rotate chain transaction journal", "err", err) 127 } 128 129 // Start the event loop and return 130 pool.wg.Add(1) 131 go pool.loop() 132 133 return pool 134 } 135 136 // Deprecated: This function is deprecated. Use SetLatestSigner instead. 137 // SetEIP155Signer set signer of txpool. 138 func (pool *BridgeTxPool) SetEIP155Signer(chainID *big.Int) { 139 pool.signer = types.NewEIP155Signer(chainID) 140 } 141 142 // SetLatestSigner set latest signer to txpool 143 func (pool *BridgeTxPool) SetLatestSigner(chainID *big.Int) { 144 pool.signer = types.LatestSignerForChainID(chainID) 145 } 146 147 // loop is the transaction pool's main event loop, waiting for and reacting to 148 // outside blockchain events as well as for various reporting and transaction 149 // eviction events. 150 func (pool *BridgeTxPool) loop() { 151 defer pool.wg.Done() 152 153 journal := time.NewTicker(pool.config.Rejournal) 154 defer journal.Stop() 155 156 // Keep waiting for and reacting to the various events 157 for { 158 select { 159 // Handle local transaction journal rotation 160 case <-journal.C: 161 if pool.journal != nil { 162 pool.mu.Lock() 163 if err := pool.journal.rotate(pool.pending()); err != nil { 164 logger.Error("Failed to rotate local tx journal", "err", err) 165 } 166 pool.mu.Unlock() 167 } 168 case <-pool.closed: 169 // update journal file with txs in pool. 170 // if txpool close without the rotate process, 171 // when loading the txpool with the journal file again, 172 // there is a limit to the size of pool so that not all tx will be 173 // loaded and especially the latest tx will not be loaded 174 if pool.journal != nil { 175 pool.mu.Lock() 176 if err := pool.journal.rotate(pool.pending()); err != nil { 177 logger.Error("Failed to rotate local tx journal", "err", err) 178 } 179 pool.mu.Unlock() 180 } 181 logger.Info("BridgeTxPool loop is closing") 182 return 183 } 184 } 185 } 186 187 // Stop terminates the transaction pool. 188 func (pool *BridgeTxPool) Stop() { 189 close(pool.closed) 190 pool.wg.Wait() 191 192 if pool.journal != nil { 193 pool.journal.close() 194 } 195 logger.Info("Transaction pool stopped") 196 } 197 198 // Stats retrieves the current pool stats, namely the number of pending transactions. 199 func (pool *BridgeTxPool) Stats() int { 200 pool.mu.RLock() 201 defer pool.mu.RUnlock() 202 203 queued := 0 204 for _, list := range pool.queue { 205 queued += list.Len() 206 } 207 return queued 208 } 209 210 // Content retrieves the data content of the transaction pool, returning all the 211 // queued transactions, grouped by account and sorted by nonce. 212 func (pool *BridgeTxPool) Content() map[common.Address]types.Transactions { 213 pool.mu.Lock() 214 defer pool.mu.Unlock() 215 216 queued := make(map[common.Address]types.Transactions) 217 for addr, list := range pool.queue { 218 var queuedTxs []*types.Transaction 219 txs := list.Flatten() 220 for _, tx := range txs { 221 queuedTxs = append(queuedTxs, tx.(*types.Transaction)) 222 } 223 queued[addr] = queuedTxs 224 } 225 return queued 226 } 227 228 // GetTx get the tx by tx hash. 229 func (pool *BridgeTxPool) GetTx(txHash common.Hash) (*types.Transaction, error) { 230 pool.mu.RLock() 231 defer pool.mu.RUnlock() 232 233 tx, ok := pool.all[txHash] 234 235 if ok { 236 return tx, nil 237 } else { 238 return nil, ErrUnknownTx 239 } 240 } 241 242 // Pending returns all pending transactions by calling internal pending method. 243 func (pool *BridgeTxPool) Pending() map[common.Address]types.Transactions { 244 pool.mu.Lock() 245 defer pool.mu.Unlock() 246 247 pending := pool.pending() 248 return pending 249 } 250 251 // Pending retrieves all pending transactions, grouped by origin 252 // account and sorted by nonce. 253 func (pool *BridgeTxPool) pending() map[common.Address]types.Transactions { 254 pending := make(map[common.Address]types.Transactions) 255 for addr, list := range pool.queue { 256 var pendingTxs []*types.Transaction 257 txs := list.Flatten() 258 for _, tx := range txs { 259 pendingTxs = append(pendingTxs, tx.(*types.Transaction)) 260 } 261 pending[addr] = pendingTxs 262 } 263 return pending 264 } 265 266 // PendingTxsByAddress retrieves pending transactions of from. They are sorted by nonce. 267 func (pool *BridgeTxPool) PendingTxsByAddress(from *common.Address, limit int) types.Transactions { 268 pool.mu.Lock() 269 defer pool.mu.Unlock() 270 271 var pendingTxs types.Transactions 272 273 if list, exist := pool.queue[*from]; exist { 274 txs := list.FlattenByCount(limit) 275 for _, tx := range txs { 276 pendingTxs = append(pendingTxs, tx.(*types.Transaction)) 277 } 278 return pendingTxs 279 } 280 return nil 281 } 282 283 // PendingTxHashesByAddress retrieves pending transaction hashes of from. They are sorted by nonce. 284 func (pool *BridgeTxPool) PendingTxHashesByAddress(from *common.Address, limit int) []common.Hash { 285 pool.mu.Lock() 286 defer pool.mu.Unlock() 287 288 if list, exist := pool.queue[*from]; exist { 289 pendingTxHashes := make([]common.Hash, limit) 290 txs := list.FlattenByCount(limit) 291 for _, tx := range txs { 292 pendingTxHashes = append(pendingTxHashes, tx.(*types.Transaction).Hash()) 293 } 294 return pendingTxHashes 295 } 296 return nil 297 } 298 299 // GetMaxTxNonce finds max nonce of the address. 300 func (pool *BridgeTxPool) GetMaxTxNonce(from *common.Address) uint64 { 301 pool.mu.RLock() 302 defer pool.mu.RUnlock() 303 304 maxNonce := uint64(0) 305 if list, exist := pool.queue[*from]; exist { 306 for _, t := range list.items { 307 if maxNonce < t.Nonce() { 308 maxNonce = t.Nonce() 309 } 310 } 311 } 312 return maxNonce 313 } 314 315 // add validates a transaction and inserts it into the non-executable queue for 316 // later pending promotion and execution. If the transaction is a replacement for 317 // an already pending or queued one, it overwrites the previous and returns this 318 // so outer code doesn't uselessly call promote. 319 func (pool *BridgeTxPool) add(tx *types.Transaction) error { 320 // If the transaction is already known, discard it 321 hash := tx.Hash() 322 if pool.all[hash] != nil { 323 logger.Trace("Discarding already known transaction", "hash", hash) 324 return ErrKnownTx 325 } 326 327 from, err := types.Sender(pool.signer, tx) 328 if err != nil { 329 return err 330 } 331 332 if uint64(len(pool.all)) >= pool.config.GlobalQueue { 333 logger.Trace("Rejecting a new Tx, because BridgeTxPool is full and there is no room for the account", "hash", tx.Hash(), "account", from) 334 refusedTxCounter.Inc(1) 335 return fmt.Errorf("txpool is full: %d", uint64(len(pool.all))) 336 } 337 338 if pool.queue[from] == nil { 339 pool.queue[from] = NewItemSortedMap(UnlimitedItemSortedMap) 340 } else { 341 if pool.queue[from].Get(tx.Nonce()) != nil { 342 return ErrDuplicatedNonceTx 343 } 344 } 345 346 pool.queue[from].Put(tx) 347 348 if pool.all[hash] == nil { 349 pool.all[hash] = tx 350 } 351 352 // Mark journal transactions 353 pool.journalTx(from, tx) 354 355 logger.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To()) 356 return nil 357 } 358 359 // journalTx adds the specified transaction to the local disk journal if it is 360 // deemed to have been sent from a service chain account. 361 func (pool *BridgeTxPool) journalTx(from common.Address, tx *types.Transaction) { 362 // Only journal if it's enabled 363 if pool.journal == nil { 364 return 365 } 366 if err := pool.journal.insert(tx); err != nil { 367 logger.Error("Failed to journal local transaction", "err", err) 368 } 369 } 370 371 // AddLocal enqueues a single transaction into the pool if it is valid, marking 372 // the sender as a local one. 373 func (pool *BridgeTxPool) AddLocal(tx *types.Transaction) error { 374 pool.mu.Lock() 375 defer pool.mu.Unlock() 376 377 return pool.addTx(tx) 378 } 379 380 // AddLocals enqueues a batch of transactions into the pool if they are valid, 381 // marking the senders as a local ones. 382 func (pool *BridgeTxPool) AddLocals(txs []*types.Transaction) []error { 383 pool.mu.Lock() 384 defer pool.mu.Unlock() 385 386 return pool.addTxs(txs) 387 } 388 389 // addTx enqueues a single transaction into the pool if it is valid. 390 func (pool *BridgeTxPool) addTx(tx *types.Transaction) error { 391 // senderCacher.recover(pool.signer, []*types.Transaction{tx}) 392 // Try to inject the transaction and update any state 393 return pool.add(tx) 394 } 395 396 // addTxs attempts to queue a batch of transactions if they are valid. 397 func (pool *BridgeTxPool) addTxs(txs []*types.Transaction) []error { 398 // senderCacher.recover(pool.signer, txs) 399 // Add the batch of transaction, tracking the accepted ones 400 errs := make([]error, len(txs)) 401 402 for i, tx := range txs { 403 errs[i] = pool.add(tx) 404 } 405 406 return errs 407 } 408 409 // Get returns a transaction if it is contained in the pool 410 // and nil otherwise. 411 func (pool *BridgeTxPool) Get(hash common.Hash) *types.Transaction { 412 pool.mu.RLock() 413 defer pool.mu.RUnlock() 414 415 return pool.all[hash] 416 } 417 418 // removeTx removes a single transaction from the queue. 419 func (pool *BridgeTxPool) removeTx(hash common.Hash) error { 420 // Fetch the transaction we wish to delete 421 tx, ok := pool.all[hash] 422 if !ok { 423 return ErrUnknownTx 424 } 425 426 addr, err := types.Sender(pool.signer, tx) 427 if err != nil { 428 return err 429 } 430 431 // Remove it from the list of known transactions 432 delete(pool.all, hash) 433 434 // Transaction is in the future queue 435 if future := pool.queue[addr]; future != nil { 436 future.Remove(tx.Nonce()) 437 if future.Len() == 0 { 438 delete(pool.queue, addr) 439 } 440 } 441 442 return nil 443 } 444 445 // Remove removes transactions from the queue. 446 func (pool *BridgeTxPool) Remove(txs types.Transactions) []error { 447 pool.mu.Lock() 448 defer pool.mu.Unlock() 449 450 errs := make([]error, len(txs)) 451 for i, tx := range txs { 452 errs[i] = pool.removeTx(tx.Hash()) 453 } 454 return errs 455 } 456 457 // RemoveTx removes a single transaction from the queue. 458 func (pool *BridgeTxPool) RemoveTx(tx *types.Transaction) error { 459 pool.mu.Lock() 460 defer pool.mu.Unlock() 461 462 err := pool.removeTx(tx.Hash()) 463 if err != nil { 464 logger.Error("RemoveTx", "err", err) 465 return err 466 } 467 return nil 468 }