|
19 | 19 | use std::any::type_name;
|
20 | 20 | use std::cell::LazyCell;
|
21 | 21 | use std::path::{Path, PathBuf};
|
22 |
| -use std::time::Instant; |
| 22 | +use std::time::{Duration, Instant}; |
23 | 23 | use std::{env, fs, io, process, thread};
|
24 | 24 |
|
25 | 25 | use clarity::types::chainstate::SortitionId;
|
| 26 | +use clarity::vm::costs::ExecutionCost; |
26 | 27 | use db::blocks::DummyEventDispatcher;
|
27 | 28 | use db::ChainstateTx;
|
28 | 29 | use regex::Regex;
|
@@ -484,11 +485,9 @@ pub fn command_try_mine(mut argv: Vec<String>, opts: &StacksInspectOpts) {
|
484 | 485 |
|
485 | 486 | let min_fee = try_mine_opts.min_fee.unwrap_or(0);
|
486 | 487 | let max_time = try_mine_opts.max_time.unwrap_or(u64::MAX);
|
487 |
| - let _max_blocks = try_mine_opts.max_blocks.unwrap_or(1); |
| 488 | + let max_blocks = try_mine_opts.max_blocks.unwrap_or(1); |
488 | 489 | let reset_tenure = try_mine_opts.reset_tenure.unwrap_or(false);
|
489 | 490 |
|
490 |
| - let start = Instant::now(); |
491 |
| - |
492 | 491 | let conf = opts.config.as_ref().unwrap_or(&DEFAULT_MAINNET_CONFIG);
|
493 | 492 |
|
494 | 493 | let burnchain_path = format!("{db_path}/burnchain");
|
@@ -539,129 +538,180 @@ pub fn command_try_mine(mut argv: Vec<String>, opts: &StacksInspectOpts) {
|
539 | 538 | let miner_pubkey_hash = Hash160::from_node_public_key(&miner_pubkey);
|
540 | 539 | let miner_nonce = 0;
|
541 | 540 |
|
542 |
| - let result = match &parent_stacks_header.anchored_header { |
543 |
| - StacksBlockHeaderTypes::Epoch2(..) => { |
544 |
| - let mut tx_auth = TransactionAuth::from_p2pkh(&miner_privk).unwrap(); |
545 |
| - tx_auth.set_origin_nonce(miner_nonce); |
546 |
| - |
547 |
| - let mut coinbase_tx = StacksTransaction::new( |
548 |
| - TransactionVersion::Mainnet, |
549 |
| - tx_auth, |
550 |
| - TransactionPayload::Coinbase(CoinbasePayload([0u8; 32]), None, None), |
551 |
| - ); |
| 541 | + // Keep important stats for each block |
| 542 | + struct BlockStats { |
| 543 | + block_hash: BlockHeaderHash, |
| 544 | + time_elapsed: Duration, |
| 545 | + num_txs: usize, |
| 546 | + fees: u64, |
| 547 | + cost: ExecutionCost, |
| 548 | + size: u64, |
| 549 | + } |
| 550 | + let mut block_stats = vec![]; |
552 | 551 |
|
553 |
| - coinbase_tx.chain_id = conf.burnchain.chain_id; |
554 |
| - coinbase_tx.anchor_mode = TransactionAnchorMode::OnChainOnly; |
555 |
| - let mut tx_signer = StacksTransactionSigner::new(&coinbase_tx); |
556 |
| - tx_signer.sign_origin(&miner_privk).unwrap(); |
557 |
| - let coinbase_tx = tx_signer.get_tx().unwrap(); |
558 |
| - |
559 |
| - StacksBlockBuilder::build_anchored_block( |
560 |
| - &chainstate, |
561 |
| - &burn_dbconn, |
562 |
| - &mut mempool_db, |
563 |
| - &parent_stacks_header, |
564 |
| - chain_tip.total_burn, |
565 |
| - VRFProof::empty(), |
566 |
| - Hash160([0; 20]), |
567 |
| - &coinbase_tx, |
568 |
| - settings, |
569 |
| - None, |
570 |
| - &Burnchain::new( |
571 |
| - &burnchain_path, |
572 |
| - &burnchain.chain_name, |
573 |
| - &burnchain.network_name, |
574 |
| - ) |
575 |
| - .unwrap_or_else(|e| panic!("Failed to instantiate burnchain: {e}")), |
576 |
| - ) |
577 |
| - .map(|(block, cost, size)| (block.block_hash(), block.txs, cost, size)) |
578 |
| - } |
579 |
| - StacksBlockHeaderTypes::Nakamoto(..) => { |
580 |
| - let tenure_info = if reset_tenure { |
581 |
| - let num_blocks_so_far = NakamotoChainState::get_nakamoto_tenure_length( |
582 |
| - chainstate.db(), |
583 |
| - &parent_block_id, |
584 |
| - ) |
585 |
| - .unwrap_or_else(|e| panic!("Error getting tenure length: {e}")); |
586 |
| - let payload = TenureChangePayload { |
587 |
| - tenure_consensus_hash: parent_consensus_hash, |
588 |
| - prev_tenure_consensus_hash: parent_consensus_hash, |
589 |
| - burn_view_consensus_hash: parent_consensus_hash, |
590 |
| - previous_tenure_end: parent_block_id, |
591 |
| - previous_tenure_blocks: num_blocks_so_far, |
592 |
| - cause: TenureChangeCause::Extended, |
593 |
| - pubkey_hash: miner_pubkey_hash, |
594 |
| - }; |
595 |
| - let tenure_change_tx_payload = TransactionPayload::TenureChange(payload); |
| 552 | + for _ in 0..max_blocks { |
| 553 | + // Timer for THIS block |
| 554 | + let start = Instant::now(); |
596 | 555 |
|
| 556 | + let result = match &parent_stacks_header.anchored_header { |
| 557 | + StacksBlockHeaderTypes::Epoch2(..) => { |
597 | 558 | let mut tx_auth = TransactionAuth::from_p2pkh(&miner_privk).unwrap();
|
598 | 559 | tx_auth.set_origin_nonce(miner_nonce);
|
599 | 560 |
|
600 |
| - let version = if conf.is_mainnet() { |
601 |
| - TransactionVersion::Mainnet |
| 561 | + let mut coinbase_tx = StacksTransaction::new( |
| 562 | + TransactionVersion::Mainnet, |
| 563 | + tx_auth, |
| 564 | + TransactionPayload::Coinbase(CoinbasePayload([0u8; 32]), None, None), |
| 565 | + ); |
| 566 | + |
| 567 | + coinbase_tx.chain_id = conf.burnchain.chain_id; |
| 568 | + coinbase_tx.anchor_mode = TransactionAnchorMode::OnChainOnly; |
| 569 | + let mut tx_signer = StacksTransactionSigner::new(&coinbase_tx); |
| 570 | + tx_signer.sign_origin(&miner_privk).unwrap(); |
| 571 | + let coinbase_tx = tx_signer.get_tx().unwrap(); |
| 572 | + |
| 573 | + StacksBlockBuilder::build_anchored_block( |
| 574 | + &chainstate, |
| 575 | + &burn_dbconn, |
| 576 | + &mut mempool_db, |
| 577 | + &parent_stacks_header, |
| 578 | + chain_tip.total_burn, |
| 579 | + VRFProof::empty(), |
| 580 | + Hash160([0; 20]), |
| 581 | + &coinbase_tx, |
| 582 | + settings.clone(), |
| 583 | + None, |
| 584 | + &Burnchain::new( |
| 585 | + &burnchain_path, |
| 586 | + &burnchain.chain_name, |
| 587 | + &burnchain.network_name, |
| 588 | + ) |
| 589 | + .unwrap_or_else(|e| panic!("Failed to instantiate burnchain: {e}")), |
| 590 | + ) |
| 591 | + .map(|(block, cost, size)| (block.block_hash(), block.txs, cost, size)) |
| 592 | + } |
| 593 | + StacksBlockHeaderTypes::Nakamoto(..) => { |
| 594 | + let tenure_info = if reset_tenure { |
| 595 | + let num_blocks_so_far = NakamotoChainState::get_nakamoto_tenure_length( |
| 596 | + chainstate.db(), |
| 597 | + &parent_block_id, |
| 598 | + ) |
| 599 | + .unwrap_or_else(|e| panic!("Error getting tenure length: {e}")); |
| 600 | + let payload = TenureChangePayload { |
| 601 | + tenure_consensus_hash: parent_consensus_hash, |
| 602 | + prev_tenure_consensus_hash: parent_consensus_hash, |
| 603 | + burn_view_consensus_hash: parent_consensus_hash, |
| 604 | + previous_tenure_end: parent_block_id, |
| 605 | + previous_tenure_blocks: num_blocks_so_far, |
| 606 | + cause: TenureChangeCause::Extended, |
| 607 | + pubkey_hash: miner_pubkey_hash, |
| 608 | + }; |
| 609 | + let tenure_change_tx_payload = TransactionPayload::TenureChange(payload); |
| 610 | + |
| 611 | + let mut tx_auth = TransactionAuth::from_p2pkh(&miner_privk).unwrap(); |
| 612 | + tx_auth.set_origin_nonce(miner_nonce); |
| 613 | + |
| 614 | + let version = if conf.is_mainnet() { |
| 615 | + TransactionVersion::Mainnet |
| 616 | + } else { |
| 617 | + TransactionVersion::Testnet |
| 618 | + }; |
| 619 | + |
| 620 | + let mut tx = StacksTransaction::new(version, tx_auth, tenure_change_tx_payload); |
| 621 | + |
| 622 | + tx.chain_id = conf.burnchain.chain_id; |
| 623 | + tx.anchor_mode = TransactionAnchorMode::OnChainOnly; |
| 624 | + let mut tx_signer = StacksTransactionSigner::new(&tx); |
| 625 | + tx_signer |
| 626 | + .sign_origin(&miner_privk) |
| 627 | + .unwrap_or_else(|e| panic!("Failed to sign transaction: {e}")); |
| 628 | + |
| 629 | + let tenure_change_tx = Some(tx_signer.get_tx().expect("Failed to get tx")); |
| 630 | + NakamotoTenureInfo { |
| 631 | + coinbase_tx: None, |
| 632 | + tenure_change_tx, |
| 633 | + } |
602 | 634 | } else {
|
603 |
| - TransactionVersion::Testnet |
| 635 | + NakamotoTenureInfo::default() |
604 | 636 | };
|
| 637 | + NakamotoBlockBuilder::build_nakamoto_block( |
| 638 | + &chainstate, |
| 639 | + &burn_dbconn, |
| 640 | + &mut mempool_db, |
| 641 | + &parent_stacks_header, |
| 642 | + // tenure ID consensus hash of this block |
| 643 | + &parent_stacks_header.consensus_hash, |
| 644 | + // the burn so far on the burnchain (i.e. from the last burnchain block) |
| 645 | + chain_tip.total_burn, |
| 646 | + tenure_info, |
| 647 | + settings.clone(), |
| 648 | + None, |
| 649 | + 0, |
| 650 | + ) |
| 651 | + .map(|(block, cost, size, _)| (block.header.block_hash(), block.txs, cost, size)) |
| 652 | + } |
| 653 | + }; |
605 | 654 |
|
606 |
| - let mut tx = StacksTransaction::new(version, tx_auth, tenure_change_tx_payload); |
607 |
| - |
608 |
| - tx.chain_id = conf.burnchain.chain_id; |
609 |
| - tx.anchor_mode = TransactionAnchorMode::OnChainOnly; |
610 |
| - let mut tx_signer = StacksTransactionSigner::new(&tx); |
611 |
| - tx_signer |
612 |
| - .sign_origin(&miner_privk) |
613 |
| - .unwrap_or_else(|e| panic!("Failed to sign transaction: {e}")); |
614 |
| - |
615 |
| - let tenure_change_tx = Some(tx_signer.get_tx().expect("Failed to get tx")); |
616 |
| - NakamotoTenureInfo { |
617 |
| - coinbase_tx: None, |
618 |
| - tenure_change_tx, |
619 |
| - } |
620 |
| - } else { |
621 |
| - NakamotoTenureInfo::default() |
622 |
| - }; |
623 |
| - NakamotoBlockBuilder::build_nakamoto_block( |
624 |
| - &chainstate, |
625 |
| - &burn_dbconn, |
626 |
| - &mut mempool_db, |
627 |
| - &parent_stacks_header, |
628 |
| - // tenure ID consensus hash of this block |
629 |
| - &parent_stacks_header.consensus_hash, |
630 |
| - // the burn so far on the burnchain (i.e. from the last burnchain block) |
631 |
| - chain_tip.total_burn, |
632 |
| - tenure_info, |
633 |
| - settings, |
634 |
| - None, |
635 |
| - 0, |
636 |
| - ) |
637 |
| - .map(|(block, cost, size, _)| (block.header.block_hash(), block.txs, cost, size)) |
638 |
| - } |
639 |
| - }; |
| 655 | + let time_elapsed = start.elapsed(); |
| 656 | + let summary = format!( |
| 657 | + "block @ height = {h} off of {parent_block_id} ({parent_consensus_hash}/{pbh}) in {t}ms. Min-fee: {min_fee}, Max-time: {max_time}", |
| 658 | + h=parent_stacks_header.stacks_block_height + 1, |
| 659 | + pbh=&parent_stacks_header.anchored_header.block_hash(), |
| 660 | + t=time_elapsed.as_millis(), |
| 661 | + ); |
640 | 662 |
|
641 |
| - let elapsed = start.elapsed(); |
642 |
| - let summary = format!( |
643 |
| - "block @ height = {h} off of {parent_block_id} ({parent_consensus_hash}/{pbh}) in {t}ms. Min-fee: {min_fee}, Max-time: {max_time}", |
644 |
| - h=parent_stacks_header.stacks_block_height + 1, |
645 |
| - pbh=&parent_stacks_header.anchored_header.block_hash(), |
646 |
| - t=elapsed.as_millis(), |
647 |
| - ); |
| 663 | + match result { |
| 664 | + Ok((block_hash, txs, cost, size)) => { |
| 665 | + let num_txs: usize = txs.len(); |
| 666 | + let fees: u64 = txs.into_iter().map(|tx| tx.get_tx_fee()).sum(); |
| 667 | + |
| 668 | + println!("Successfully mined {summary}"); |
| 669 | + println!( |
| 670 | + "Block {block_hash}: {num_txs} txs, {fees} uSTX, {size} bytes, cost {cost:?}" |
| 671 | + ); |
| 672 | + block_stats.push(BlockStats { |
| 673 | + block_hash, |
| 674 | + time_elapsed, |
| 675 | + num_txs, |
| 676 | + fees, |
| 677 | + cost, |
| 678 | + size, |
| 679 | + }) |
| 680 | + } |
| 681 | + Err(e) => { |
| 682 | + println!("Failed to mine {summary}"); |
| 683 | + println!("Error: {e}"); |
| 684 | + process::exit(1); |
| 685 | + } |
| 686 | + }; |
| 687 | + } |
648 | 688 |
|
649 |
| - let code = match result { |
650 |
| - Ok((block_hash, txs, cost, size)) => { |
651 |
| - let total_fees: u64 = txs.iter().map(|tx| tx.get_tx_fee()).sum(); |
| 689 | + // Print summary |
| 690 | + let blocks_mined = block_stats.len(); |
| 691 | + let (time_elapsed, num_txs, fees, cost, size) = block_stats.into_iter().fold( |
| 692 | + (Duration::ZERO, 0usize, 0u64, ExecutionCost::ZERO, 0u64), |
| 693 | + |acc, bs| { |
| 694 | + let (time_elapsed, num_txs, total_fees, mut cost, size) = acc; |
| 695 | + cost.add(&bs.cost).expect("Cost overflow"); |
| 696 | + ( |
| 697 | + time_elapsed.saturating_add(bs.time_elapsed), |
| 698 | + num_txs.saturating_add(bs.num_txs), |
| 699 | + total_fees.saturating_add(bs.fees), |
| 700 | + cost, |
| 701 | + size.saturating_add(bs.size), |
| 702 | + ) |
| 703 | + }, |
| 704 | + ); |
652 | 705 |
|
653 |
| - println!("Successfully mined {summary}"); |
654 |
| - println!("Block {block_hash}: {total_fees} uSTX, {size} bytes, cost {cost:?}"); |
655 |
| - 0 |
656 |
| - } |
657 |
| - Err(e) => { |
658 |
| - println!("Failed to mine {summary}"); |
659 |
| - println!("Error: {e}"); |
660 |
| - 1 |
661 |
| - } |
662 |
| - }; |
| 706 | + println!("Successfully mined {blocks_mined} blocks from mempool"); |
| 707 | + println!("Totals:"); |
| 708 | + println!(" Time Elapsed: {} ms", time_elapsed.as_millis()); |
| 709 | + println!(" Transactions: {num_txs}"); |
| 710 | + println!(" Fees: {fees}"); |
| 711 | + println!(" Size: {size}"); |
| 712 | + println!(" Cost: {cost:?}"); |
663 | 713 |
|
664 |
| - process::exit(code); |
| 714 | + process::exit(0); |
665 | 715 | }
|
666 | 716 |
|
667 | 717 | /// Fetch and process a `StagingBlock` from database and call `replay_block()` to validate
|
|
0 commit comments