github.com/aergoio/aergo@v1.3.1/mempool/mempool.go (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package mempool 7 8 import ( 9 "bufio" 10 "bytes" 11 "encoding/binary" 12 "io" 13 "math/big" 14 "os" 15 "sync" 16 "sync/atomic" 17 "time" 18 19 "github.com/aergoio/aergo-actor/actor" 20 "github.com/aergoio/aergo-actor/router" 21 "github.com/aergoio/aergo-lib/log" 22 "github.com/aergoio/aergo/account/key" 23 "github.com/aergoio/aergo/chain" 24 cfg "github.com/aergoio/aergo/config" 25 "github.com/aergoio/aergo/contract/enterprise" 26 "github.com/aergoio/aergo/contract/name" 27 "github.com/aergoio/aergo/contract/system" 28 "github.com/aergoio/aergo/fee" 29 "github.com/aergoio/aergo/internal/common" 30 "github.com/aergoio/aergo/internal/enc" 31 "github.com/aergoio/aergo/message" 32 "github.com/aergoio/aergo/pkg/component" 33 "github.com/aergoio/aergo/state" 34 "github.com/aergoio/aergo/types" 35 "github.com/golang/protobuf/proto" 36 ) 37 38 const ( 39 initial = iota 40 loading = iota 41 running = iota 42 ) 43 44 var ( 45 evictInterval = time.Minute 46 evictPeriod = time.Hour * types.DefaultEvictPeriod 47 metricInterval = time.Second 48 ) 49 50 // MemPool is main structure of mempool service 51 type MemPool struct { 52 *component.BaseComponent 53 54 sync.RWMutex 55 cfg *cfg.Config 56 57 sdb *state.ChainStateDB 58 bestBlockID types.BlockID 59 bestBlockNo types.BlockNo 60 stateDB *state.StateDB 61 verifier *actor.PID 62 orphan int 63 //cache map[types.TxID]types.Transaction 64 cache sync.Map 65 length int 66 pool map[types.AccountID]*TxList 67 dumpPath string 68 status int32 69 coinbasefee *big.Int 70 chainIdHash []byte 71 isPublic bool 72 whitelist *whitelistConf 73 // followings are for test 74 testConfig bool 75 deadtx int 76 77 quit chan bool 78 wg sync.WaitGroup // wait for internal loop 79 } 80 81 // NewMemPoolService create and return new MemPool 82 func NewMemPoolService(cfg *cfg.Config, cs *chain.ChainService) *MemPool { 83 84 var sdb *state.ChainStateDB 85 if cs != nil { 86 sdb = cs.SDB() 87 } else { // Test 88 fee.EnableZeroFee() 89 } 90 91 actor := &MemPool{ 92 cfg: cfg, 93 sdb: sdb, 94 //cache: map[types.TxID]types.Transaction{}, 95 cache: sync.Map{}, 96 pool: map[types.AccountID]*TxList{}, 97 dumpPath: cfg.Mempool.DumpFilePath, 98 status: initial, 99 verifier: nil, 100 quit: make(chan bool), 101 } 102 actor.BaseComponent = component.NewBaseComponent(message.MemPoolSvc, actor, log.NewLogger("mempool")) 103 104 if cfg.Mempool.FadeoutPeriod > 0 { 105 evictPeriod = time.Duration(cfg.Mempool.FadeoutPeriod) * time.Hour 106 } 107 return actor 108 } 109 110 // Start runs mempool servivce 111 func (mp *MemPool) BeforeStart() { 112 if mp.testConfig { 113 initStubData() 114 mp.bestBlockID = getCurrentBestBlockNoMock() 115 } 116 //mp.Info("mempool start on: current Block :", mp.curBestBlockNo) 117 } 118 119 func (mp *MemPool) AfterStart() { 120 121 mp.Info().Bool("showmetric", mp.cfg.Mempool.ShowMetrics). 122 Bool("fadeout", mp.cfg.Mempool.EnableFadeout). 123 Str("evict period", evictPeriod.String()). 124 Int("number of verifier", mp.cfg.Mempool.VerifierNumber). 125 Msg("mempool init") 126 127 mp.verifier = actor.Spawn(router.NewRoundRobinPool(mp.cfg.Mempool.VerifierNumber). 128 WithInstance(NewTxVerifier(mp))) 129 130 rsp, err := mp.RequestToFuture(message.ChainSvc, &message.GetBestBlock{}, time.Second*2).Result() 131 if err != nil { 132 mp.Error().Err(err).Msg("failed to get best block") 133 panic("Mempool AfterStart Failed") 134 } 135 bestblock := rsp.(message.GetBestBlockRsp).Block 136 mp.setStateDB(bestblock) // nolint: errcheck 137 138 mp.wg.Add(1) 139 go mp.monitor() 140 } 141 142 // Stop handles clean-up for mempool service 143 func (mp *MemPool) BeforeStop() { 144 if mp.verifier != nil { 145 mp.verifier.GracefulStop() 146 } 147 mp.dumpTxsToFile() 148 mp.quit <- true 149 mp.wg.Wait() 150 } 151 152 func (mp *MemPool) monitor() { 153 defer mp.wg.Done() 154 155 evict := time.NewTicker(evictInterval) 156 defer evict.Stop() 157 158 showmetric := time.NewTicker(metricInterval) 159 defer showmetric.Stop() 160 161 for { 162 select { 163 // Log current counts on mempool 164 case <-showmetric.C: 165 if mp.cfg.Mempool.ShowMetrics { 166 l, o := mp.Size() 167 mp.Info().Int("len", l).Int("orphan", o).Int("acc", len(mp.pool)).Msg("mempool metrics") 168 } 169 // Evict old enough transactions 170 case <-evict.C: 171 if mp.cfg.Mempool.EnableFadeout { 172 mp.evictTransactions() 173 } 174 175 // Graceful quit 176 case <-mp.quit: 177 return 178 } 179 } 180 181 } 182 183 func (mp *MemPool) evictTransactions() { 184 mp.Lock() 185 defer mp.Unlock() 186 187 total := 0 188 for acc, list := range mp.pool { 189 if time.Since(list.GetLastModifiedTime()) < evictPeriod { 190 continue 191 } 192 txs := list.GetAll() 193 total += len(txs) 194 orphan := len(txs) - list.Len() 195 196 for _, tx := range txs { 197 mp.cache.Delete(types.ToTxID(tx.GetHash())) 198 mp.length-- 199 } 200 201 mp.orphan -= orphan 202 delete(mp.pool, acc) 203 } 204 if total > 0 { 205 mp.Info().Int("num", total).Msg("evict transactions") 206 } 207 } 208 209 // Size returns current maintaining number of transactions 210 // and number of orphan transaction 211 func (mp *MemPool) Size() (int, int) { 212 return mp.length, mp.orphan 213 } 214 215 // Receive handles requested messages from other services 216 func (mp *MemPool) Receive(context actor.Context) { 217 218 switch msg := context.Message().(type) { 219 case *message.MemPoolPut: 220 mp.verifier.Request(msg.Tx, context.Sender()) 221 case *message.MemPoolGet: 222 txs, err := mp.get(msg.MaxBlockBodySize) 223 context.Respond(&message.MemPoolGetRsp{ 224 Txs: txs, 225 Err: err, 226 }) 227 case *message.MemPoolDel: 228 errs := mp.removeOnBlockArrival(msg.Block) 229 context.Respond(&message.MemPoolDelRsp{ 230 Err: errs, 231 }) 232 case *message.MemPoolExist: 233 tx := mp.exist(msg.Hash) 234 context.Respond(&message.MemPoolExistRsp{ 235 Tx: tx, 236 }) 237 case *message.MemPoolExistEx: 238 txsnum, _ := mp.Size() 239 var bucketHash []types.TxHash 240 bucketHash = msg.Hashes 241 mp.Debug().Int("len", len(bucketHash)).Int("cached", txsnum).Msg("mempool existEx") 242 243 txs := mp.existEx(bucketHash) 244 context.Respond(&message.MemPoolExistExRsp{Txs: txs}) 245 246 case *message.MemPoolSetWhitelist: 247 mp.whitelist.SetWhitelist(msg.Accounts) 248 case *message.MemPoolEnableWhitelist: 249 mp.whitelist.Enable(msg.On) 250 251 case *actor.Started: 252 mp.loadTxs() // FIXME :work-around for actor settled 253 254 default: 255 //mp.Debug().Str("type", reflect.TypeOf(msg).String()).Msg("unhandled message") 256 } 257 } 258 259 func (mp *MemPool) Statistics() *map[string]interface{} { 260 ret := map[string]interface{}{ 261 "total": mp.length, 262 "orphan": mp.orphan, 263 "dead": mp.deadtx, 264 "config": mp.cfg.Mempool, 265 } 266 if !mp.isPublic { 267 ret["whitelist"] = mp.whitelist.GetWhitelist() 268 ret["whitelist_on"] = mp.whitelist.GetOn() 269 } 270 return &ret 271 } 272 273 func (mp *MemPool) get(maxBlockBodySize uint32) ([]types.Transaction, error) { 274 start := time.Now() 275 mp.RLock() 276 defer mp.RUnlock() 277 count := 0 278 size := 0 279 txs := make([]types.Transaction, 0) 280 Gather: 281 for _, list := range mp.pool { 282 for _, tx := range list.Get() { 283 if size += proto.Size(tx.GetTx()); uint32(size) > maxBlockBodySize { 284 break Gather 285 } 286 txs = append(txs, tx) 287 count++ 288 } 289 } 290 elapsed := time.Since(start) 291 mp.Debug().Str("elapsed", elapsed.String()).Int("len", mp.length).Int("orphan", mp.orphan).Int("count", count).Msg("total tx returned") 292 return txs, nil 293 } 294 295 // check existence. 296 // validate 297 // add pool if possible, else pendings 298 func (mp *MemPool) put(tx types.Transaction) error { 299 id := types.ToTxID(tx.GetHash()) 300 acc := tx.GetBody().GetAccount() 301 if tx.HasVerifedAccount() { 302 acc = tx.GetVerifedAccount() 303 } 304 305 if _, ok := mp.cache.Load(id); ok { 306 return types.ErrTxAlreadyInMempool 307 } 308 /* 309 err := mp.verifyTx(tx) 310 if err != nil { 311 return err 312 } 313 */ 314 err := mp.validateTx(tx, acc) 315 if err != nil && err != types.ErrTxNonceToohigh { 316 return err 317 } 318 mp.Lock() 319 defer mp.Unlock() 320 321 list, err := mp.acquireMemPoolList(acc) 322 if err != nil { 323 return err 324 } 325 defer mp.releaseMemPoolList(list) 326 diff, err := list.Put(tx) 327 if err != nil { 328 mp.Error().Err(err).Msg("fail to put at a mempool list") 329 return err 330 } 331 332 mp.orphan -= diff 333 mp.cache.Store(id, tx) 334 mp.length++ 335 //mp.Debug().Str("tx_hash", enc.ToString(tx.GetHash())).Msgf("tx add-ed size(%d, %d)", len(mp.cache), mp.orphan) 336 337 if !mp.testConfig { 338 mp.notifyNewTx(tx) 339 } 340 return nil 341 } 342 func (mp *MemPool) puts(txs ...types.Transaction) []error { 343 errs := make([]error, len(txs)) 344 for i, tx := range txs { 345 errs[i] = mp.put(tx) 346 } 347 return errs 348 } 349 350 func (mp *MemPool) setStateDB(block *types.Block) bool { 351 if mp.testConfig { 352 return true 353 } 354 355 newBlockID := types.ToBlockID(block.BlockHash()) 356 parentBlockID := types.ToBlockID(block.GetHeader().GetPrevBlockHash()) 357 normal := true 358 359 if types.HashID(newBlockID).Compare(types.HashID(mp.bestBlockID)) != 0 { 360 if types.HashID(parentBlockID).Compare(types.HashID(mp.bestBlockID)) != 0 { 361 normal = false 362 } 363 mp.bestBlockID = newBlockID 364 mp.bestBlockNo = block.GetHeader().GetBlockNo() 365 stateRoot := block.GetHeader().GetBlocksRootHash() 366 if mp.stateDB == nil { 367 mp.stateDB = mp.sdb.OpenNewStateDB(stateRoot) 368 cid := types.NewChainID() 369 if err := cid.Read(block.GetHeader().GetChainID()); err != nil { 370 mp.Error().Err(err).Msg("failed to read chain ID") 371 } else { 372 mp.isPublic = cid.PublicNet 373 if !mp.isPublic { 374 conf, err := enterprise.GetConf(mp.stateDB, enterprise.AccountWhite) 375 if err != nil { 376 mp.Warn().Err(err).Msg("failed to init whitelist") 377 } 378 mp.whitelist = newWhitelistConf(mp, conf.GetValues(), conf.GetOn()) 379 } 380 } 381 mp.chainIdHash = common.Hasher(block.GetHeader().GetChainID()) 382 mp.Debug().Str("Hash", newBlockID.String()). 383 Str("StateRoot", types.ToHashID(stateRoot).String()). 384 Str("chainidhash", enc.ToString(mp.chainIdHash)). 385 Msg("new StateDB opened") 386 } else if !bytes.Equal(mp.stateDB.GetRoot(), stateRoot) { 387 if err := mp.stateDB.SetRoot(stateRoot); err != nil { 388 mp.Error().Err(err).Msg("failed to set root of StateDB") 389 } 390 } 391 } 392 return normal 393 } 394 395 // input tx based ? or pool based? 396 // concurrency consideration, 397 func (mp *MemPool) removeOnBlockArrival(block *types.Block) error { 398 var ag [2]time.Duration 399 start := time.Now() 400 mp.Lock() 401 defer mp.Unlock() 402 403 check := 0 404 all := false 405 dirty := map[types.AccountID]bool{} 406 407 if !mp.setStateDB(block) { 408 all = true 409 mp.Debug().Int("cnt", len(mp.pool)).Msg("going to check all account's state") 410 } else { 411 for _, tx := range block.GetBody().GetTxs() { 412 account := tx.GetBody().GetAccount() 413 recipient := tx.GetBody().GetRecipient() 414 if tx.HasNameAccount() { 415 account = mp.getAddress(account) 416 } 417 if tx.HasNameRecipient() { 418 recipient = mp.getAddress(recipient) 419 } 420 dirty[types.ToAccountID(account)] = true 421 dirty[types.ToAccountID(recipient)] = true 422 } 423 } 424 425 ag[0] = time.Since(start) 426 start = time.Now() 427 for acc, list := range mp.pool { 428 if !all && dirty[acc] == false { 429 continue 430 } 431 ns, err := mp.getAccountState(list.GetAccount()) 432 if err != nil { 433 mp.Error().Err(err).Msg("getting Account status failed during removal") 434 // TODO : ???? 435 continue 436 } 437 diff, delTxs := list.FilterByState(ns) 438 mp.orphan -= diff 439 for _, tx := range delTxs { 440 mp.cache.Delete(types.ToTxID(tx.GetHash())) 441 mp.length-- 442 } 443 mp.releaseMemPoolList(list) 444 check++ 445 } 446 447 ag[1] = time.Since(start) 448 mp.Debug().Int("given", len(block.GetBody().GetTxs())). 449 Int("check", check). 450 Str("elapse1", ag[0].String()). 451 Str("elapse2", ag[1].String()). 452 Msg("delete txs on block") 453 return nil 454 } 455 456 // signiture verification 457 func (mp *MemPool) verifyTx(tx types.Transaction) error { 458 err := tx.Validate(mp.chainIdHash, mp.isPublic) 459 if err != nil { 460 return err 461 } 462 if !tx.GetTx().NeedNameVerify() { 463 err = key.VerifyTx(tx.GetTx()) 464 if err != nil { 465 return err 466 } 467 } else { 468 mp.RLock() 469 account := mp.getAddress(tx.GetBody().GetAccount()) 470 mp.RUnlock() 471 err = key.VerifyTxWithAddress(tx.GetTx(), account) 472 if err != nil { 473 return err 474 } 475 if !tx.SetVerifedAccount(account) { 476 mp.Warn().Str("account", string(account)).Msg("could not set verifed account") 477 } 478 } 479 return nil 480 } 481 func (mp *MemPool) getAddress(account []byte) []byte { 482 if mp.testConfig { 483 return account 484 } 485 486 nameState, err := mp.getAccountState([]byte(types.AergoName)) 487 if err != nil { 488 mp.Error().Str("for name", string(account)).Msgf("failed to get state %s", types.AergoName) 489 return nil 490 } 491 scs, err := mp.stateDB.OpenContractState(types.ToAccountID([]byte(types.AergoName)), nameState) 492 if err != nil { 493 mp.Error().Str("for name", string(account)).Msgf("failed to open contract %s", types.AergoName) 494 return nil 495 } 496 return name.GetOwner(scs, account) 497 } 498 499 // check tx sanity 500 // check if sender has enough balance 501 // check if recipient is valid name 502 // check tx account is lower than known value 503 func (mp *MemPool) validateTx(tx types.Transaction, account types.Address) error { 504 if !mp.whitelist.Check(types.EncodeAddress(account)) { 505 return types.ErrTxNotAllowedAccount 506 } 507 ns, err := mp.getAccountState(account) 508 if err != nil { 509 return err 510 } 511 err = tx.ValidateWithSenderState(ns) 512 if err != nil && err != types.ErrTxNonceToohigh { 513 return err 514 } 515 516 //NOTE: don't overwrite err, if err == ErrTxNonceToohigh 517 //because err should be ErrNonceToohigh if following validation has passed 518 //this will be refactored soon 519 520 switch tx.GetBody().GetType() { 521 case types.TxType_REDEPLOY: 522 if chain.IsPublic() { 523 return types.ErrTxInvalidType 524 } 525 if tx.GetBody().GetRecipient() == nil { 526 return types.ErrTxInvalidRecipient 527 } 528 fallthrough 529 case types.TxType_NORMAL: 530 if tx.GetTx().HasNameRecipient() { 531 recipient := tx.GetBody().GetRecipient() 532 recipientAddr := mp.getAddress(recipient) 533 if recipientAddr == nil { 534 return types.ErrTxInvalidRecipient 535 } 536 } 537 case types.TxType_GOVERNANCE: 538 aergoState, err := mp.getAccountState(tx.GetBody().GetRecipient()) 539 if err != nil { 540 return err 541 } 542 aid := types.ToAccountID(tx.GetBody().GetRecipient()) 543 scs, err := mp.stateDB.OpenContractState(aid, aergoState) 544 if err != nil { 545 return err 546 } 547 switch string(tx.GetBody().GetRecipient()) { 548 case types.AergoSystem: 549 sender, err := mp.stateDB.GetAccountStateV(account) 550 if err != nil { 551 return err 552 } 553 if _, err := system.ValidateSystemTx(account, tx.GetBody(), 554 sender, scs, mp.bestBlockNo+1); err != nil { 555 return err 556 } 557 case types.AergoName: 558 systemcs, err := mp.stateDB.OpenContractStateAccount(types.ToAccountID([]byte(types.AergoSystem))) 559 if err != nil { 560 return err 561 } 562 sender, err := mp.stateDB.GetAccountStateV(account) 563 if err != nil { 564 return err 565 } 566 if _, err := name.ValidateNameTx(tx.GetBody(), sender, scs, systemcs); err != nil { 567 return err 568 } 569 case types.AergoEnterprise: 570 enterprisecs, err := mp.stateDB.OpenContractStateAccount(types.ToAccountID([]byte(types.AergoEnterprise))) 571 if err != nil { 572 return err 573 } 574 sender, err := mp.stateDB.GetAccountStateV(account) 575 if err != nil { 576 return err 577 } 578 if _, err := enterprise.ValidateEnterpriseTx(tx.GetBody(), sender, enterprisecs, mp.bestBlockNo+1); err != nil { 579 return err 580 } 581 } 582 583 } 584 return err 585 } 586 587 func (mp *MemPool) exist(hash []byte) *types.Tx { 588 v := make([]types.TxHash, 1) 589 v[0] = hash 590 txs := mp.existEx(v) 591 return txs[0] 592 } 593 func (mp *MemPool) existEx(hashes []types.TxHash) []*types.Tx { 594 595 if len(hashes) > message.MaxReqestHashes { 596 mp.Error().Int("size", len(hashes)). 597 Msg("request exceeds max hash length") 598 return nil 599 } 600 601 ret := make([]*types.Tx, len(hashes)) 602 for i, h := range hashes { 603 if v, ok := mp.cache.Load(types.ToTxID(h)); ok { 604 ret[i] = v.(types.Transaction).GetTx() 605 } 606 } 607 return ret 608 } 609 610 func (mp *MemPool) acquireMemPoolList(acc []byte) (*TxList, error) { 611 list := mp.getMemPoolList(acc) 612 if list != nil { 613 return list, nil 614 } 615 ns, err := mp.getAccountState(acc) 616 if err != nil { 617 return nil, err 618 } 619 id := types.ToAccountID(acc) 620 mp.pool[id] = NewTxList(acc, ns) 621 return mp.pool[id], nil 622 } 623 624 func (mp *MemPool) releaseMemPoolList(list *TxList) { 625 if list.Empty() { 626 id := types.ToAccountID(list.account) 627 delete(mp.pool, id) 628 } 629 } 630 631 func (mp *MemPool) getMemPoolList(acc []byte) *TxList { 632 id := types.ToAccountID(acc) 633 return mp.pool[id] 634 } 635 636 func (mp *MemPool) getAccountState(acc []byte) (*types.State, error) { 637 if mp.testConfig { 638 aid := types.ToAccountID(acc) 639 strAcc := aid.String() 640 bal := getBalanceByAccMock(strAcc) 641 nonce := getNonceByAccMock(strAcc) 642 //mp.Error().Str("acc:", strAcc).Int("nonce", int(nonce)).Msg("") 643 return &types.State{Balance: new(big.Int).SetUint64(bal).Bytes(), Nonce: nonce}, nil 644 } 645 646 state, err := mp.stateDB.GetAccountState(types.ToAccountID(acc)) 647 648 if err != nil { 649 mp.Fatal().Err(err).Str("sroot", enc.ToString(mp.stateDB.GetRoot())).Msg("failed to get state") 650 651 //FIXME PANIC? 652 //mp.Fatal().Err(err).Msg("failed to get state") 653 return nil, err 654 } 655 /* 656 if state.Balance == 0 { 657 strAcc := types.EncodeAddress(acc) 658 mp.Info().Str("address", strAcc).Msg("w t f") 659 660 } 661 */ 662 return state, nil 663 } 664 665 func (mp *MemPool) notifyNewTx(tx types.Transaction) { 666 mp.RequestTo(message.P2PSvc, &message.NotifyNewTransactions{ 667 Txs: []*types.Tx{tx.GetTx()}, 668 }) 669 } 670 671 func (mp *MemPool) isRunning() bool { 672 if atomic.LoadInt32(&mp.status) != running { 673 mp.Info().Msg("skip to dump txs because mempool is not running yet") 674 return false 675 } 676 return true 677 } 678 679 func (mp *MemPool) loadTxs() { 680 time.Sleep(time.Second) // FIXME 681 if !atomic.CompareAndSwapInt32(&mp.status, initial, loading) { 682 return 683 } 684 defer atomic.StoreInt32(&mp.status, running) 685 686 file, err := os.Open(mp.dumpPath) 687 if err != nil { 688 if !os.IsNotExist(err) { 689 mp.Error().Err(err).Msg("Unable to open dump file") 690 } 691 return 692 } 693 694 defer file.Close() // nolint: errcheck 695 696 reader := bufio.NewReader(file) 697 698 var count int 699 700 for { 701 buf := types.Tx{} 702 byteInt := make([]byte, 4) 703 _, err := io.ReadFull(reader, byteInt) 704 if err != nil { 705 if err != io.EOF { 706 mp.Error().Err(err).Msg("err on read file during loading") 707 } 708 break 709 } 710 711 reclen := binary.LittleEndian.Uint32(byteInt) 712 buffer := make([]byte, int(reclen)) 713 _, err = io.ReadFull(reader, buffer) 714 if err != nil { 715 if err != io.EOF { 716 mp.Error().Err(err).Msg("err on read file during loading") 717 } 718 break 719 } 720 721 err = proto.Unmarshal(buffer, &buf) 722 if err != nil { 723 mp.Error().Err(err).Msg("errr on unmarshalling tx during loading") 724 continue 725 } 726 count++ 727 mp.put(types.NewTransaction(&buf)) // nolint: errcheck 728 } 729 730 mp.Info().Int("try", count). 731 Int("drop", count-mp.length-mp.orphan). 732 Int("suceed", mp.length). 733 Int("orphan", mp.orphan). 734 Msg("loading mempool done") 735 } 736 737 func (mp *MemPool) dumpTxsToFile() { 738 if !mp.isRunning() { 739 return 740 } 741 mp.Info().Msg("start mempool dump") 742 743 file, err := os.Create(mp.dumpPath) 744 if err != nil { 745 mp.Error().Err(err).Msg("Unable to create file") 746 return 747 } 748 defer file.Close() // nolint: errcheck 749 750 writer := bufio.NewWriter(file) 751 defer writer.Flush() //nolint: errcheck 752 mp.Lock() 753 defer mp.Unlock() 754 755 var ag [2]time.Duration 756 count := 0 757 758 Dump: 759 for _, list := range mp.pool { 760 for _, v := range list.GetAll() { 761 762 var total_data []byte 763 start := time.Now() 764 data, err := proto.Marshal(v.GetTx()) 765 if err != nil { 766 mp.Error().Err(err).Msg("Marshal failed") 767 continue 768 } 769 770 byteInt := make([]byte, 4) 771 binary.LittleEndian.PutUint32(byteInt, uint32(len(data))) 772 total_data = append(total_data, byteInt...) 773 total_data = append(total_data, data...) 774 775 ag[0] += time.Since(start) 776 start = time.Now() 777 778 length := len(total_data) 779 for { 780 size, err := writer.Write(total_data) 781 if err != nil { 782 mp.Error().Err(err).Msg("writing encoded tx fail") 783 break Dump 784 } 785 if length != size { 786 total_data = total_data[size:] 787 length -= size 788 } else { 789 break 790 } 791 } 792 count++ 793 ag[1] += time.Since(start) 794 } 795 } 796 797 mp.Info().Int("count", count).Str("path", mp.dumpPath).Str("marshal", ag[0].String()). 798 Str("write", ag[1].String()).Msg("dump txs") 799 800 }