Skip to content

Commit c7ae9eb

Browse files
Merge branch 'develop' into feat/non-blocking-event-delivery
2 parents 6cc94e1 + 6c76d35 commit c7ae9eb

File tree

24 files changed

+1455
-264
lines changed

24 files changed

+1455
-264
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1717
### Added
1818

1919
- In the `/v3/transaction/{txid}` RPC endpoint, added `block_height` and `is_canonical` to the response.
20+
- New endpoint `/v3/blocks/simulate/{block_id}` allows to simulate the execution of a specific block with a brand new set of transactions
2021
- Improved block validation in `stacks-inspect`.
2122
- Allow non-blocking event dispatching. This is off by default, but can be enabled in the node configuration.
2223

docs/rpc/openapi.yaml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2415,3 +2415,62 @@ paths:
24152415
$ref: "#/components/responses/NotFound"
24162416
"500":
24172417
$ref: "#/components/responses/InternalServerError"
2418+
2419+
/v3/blocks/simulate/{block_id}:
2420+
get:
2421+
summary: Simulate mining of a block with the specified transactions and returns its content
2422+
tags:
2423+
- Blocks
2424+
security:
2425+
- rpcAuth: []
2426+
operationId: blockSimulate
2427+
description: |
2428+
Simulate the mining of a block (no data is written in the MARF) with specified transactions and returns its content.
2429+
parameters:
2430+
- name: block_id
2431+
in: path
2432+
description: The block ID hash
2433+
required: true
2434+
schema:
2435+
type: string
2436+
pattern: "^[0-9a-f]{64}$"
2437+
requestBody:
2438+
required: true
2439+
content:
2440+
application/json:
2441+
schema:
2442+
type: object
2443+
properties:
2444+
transactions_hex:
2445+
type: array
2446+
items:
2447+
type: string
2448+
description: SIP-003-encoded Transaction in hex format
2449+
mint:
2450+
type: array
2451+
items:
2452+
type: object
2453+
properties:
2454+
principal:
2455+
type: string
2456+
description: Principal address receiving STX
2457+
amount:
2458+
type: integer
2459+
description: amount of microSTX to mint for the block simulation
2460+
responses:
2461+
"200":
2462+
description: Content of the simulated block
2463+
content:
2464+
application/json:
2465+
schema:
2466+
$ref: "#/components/schemas/BlockReplay"
2467+
example:
2468+
$ref: "./components/examples/block-replay.example.json"
2469+
"400":
2470+
$ref: "#/components/responses/BadRequest"
2471+
"401":
2472+
$ref: "#/components/responses/Unauthorized"
2473+
"404":
2474+
$ref: "#/components/responses/NotFound"
2475+
"500":
2476+
$ref: "#/components/responses/InternalServerError"

libsigner/src/events.rs

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::time::SystemTime;
2424

2525
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
2626
use blockstack_lib::chainstate::stacks::boot::{MINERS_NAME, SIGNERS_NAME};
27-
use blockstack_lib::chainstate::stacks::events::StackerDBChunksEvent;
27+
use blockstack_lib::chainstate::stacks::events::{BurnBlockEvent, StackerDBChunksEvent};
2828
use blockstack_lib::chainstate::stacks::StacksTransaction;
2929
use blockstack_lib::net::api::postblock_proposal::BlockValidateResponse;
3030
use blockstack_lib::version_string;
@@ -568,28 +568,6 @@ impl<T: SignerEventTrait> TryFrom<BlockValidateResponse> for SignerEvent<T> {
568568
}
569569
}
570570

571-
/// Burn block JSON payload from the event receiver
572-
#[derive(Debug, Deserialize, Clone)]
573-
pub struct BurnBlockEvent {
574-
/// The hash of the burn block
575-
#[serde(with = "prefix_hex")]
576-
pub burn_block_hash: BurnchainHeaderHash,
577-
/// The height of the burn block
578-
pub burn_block_height: u64,
579-
/// The reward recipients
580-
pub reward_recipients: Vec<serde_json::Value>,
581-
/// The reward slot holders
582-
pub reward_slot_holders: Vec<String>,
583-
/// The amount of burn
584-
pub burn_amount: u64,
585-
/// The consensus hash of the burn block
586-
#[serde(with = "prefix_hex")]
587-
pub consensus_hash: ConsensusHash,
588-
/// The parent burn block hash
589-
#[serde(with = "prefix_hex")]
590-
pub parent_burn_block_hash: BurnchainHeaderHash,
591-
}
592-
593571
impl<T: SignerEventTrait> TryFrom<BurnBlockEvent> for SignerEvent<T> {
594572
type Error = EventError;
595573

libsigner/src/libsigner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ use stacks_common::versions::STACKS_SIGNER_VERSION;
5757

5858
pub use crate::error::{EventError, RPCError};
5959
pub use crate::events::{
60-
BlockProposal, BlockProposalData, BurnBlockEvent, EventReceiver, EventStopSignaler,
61-
SignerEvent, SignerEventReceiver, SignerEventTrait, SignerStopSignaler, StacksBlockEvent,
60+
BlockProposal, BlockProposalData, EventReceiver, EventStopSignaler, SignerEvent,
61+
SignerEventReceiver, SignerEventTrait, SignerStopSignaler, StacksBlockEvent,
6262
};
6363
pub use crate::runloop::{RunningSigner, Signer, SignerRunLoop};
6464
pub use crate::session::{SignerSession, StackerDBSession};

stacks-node/src/event_dispatcher.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use lazy_static::lazy_static;
3232
use serde_json::json;
3333
use stacks::burnchains::{PoxConstants, Txid};
3434
use stacks::chainstate::burn::ConsensusHash;
35-
use stacks::chainstate::coordinator::BlockEventDispatcher;
35+
use stacks::chainstate::coordinator::{BlockEventDispatcher, PoxTransactionReward};
3636
use stacks::chainstate::nakamoto::NakamotoBlock;
3737
use stacks::chainstate::stacks::address::PoxAddress;
3838
use stacks::chainstate::stacks::boot::RewardSetData;
@@ -370,6 +370,7 @@ impl BlockEventDispatcher for EventDispatcher {
370370
burn_block_height: u64,
371371
rewards: Vec<(PoxAddress, u64)>,
372372
burns: u64,
373+
pox_transactions: Vec<PoxTransactionReward>,
373374
recipient_info: Vec<PoxAddress>,
374375
consensus_hash: &ConsensusHash,
375376
parent_burn_block_hash: &BurnchainHeaderHash,
@@ -379,6 +380,7 @@ impl BlockEventDispatcher for EventDispatcher {
379380
burn_block_height,
380381
rewards,
381382
burns,
383+
pox_transactions,
382384
recipient_info,
383385
consensus_hash,
384386
parent_burn_block_hash,
@@ -478,6 +480,7 @@ impl EventDispatcher {
478480
burn_block_height: u64,
479481
rewards: Vec<(PoxAddress, u64)>,
480482
burns: u64,
483+
pox_transactions: Vec<PoxTransactionReward>,
481484
recipient_info: Vec<PoxAddress>,
482485
consensus_hash: &ConsensusHash,
483486
parent_burn_block_hash: &BurnchainHeaderHash,
@@ -493,6 +496,7 @@ impl EventDispatcher {
493496
burn_block_height,
494497
rewards,
495498
burns,
499+
pox_transactions,
496500
recipient_info,
497501
consensus_hash,
498502
parent_burn_block_hash,

stacks-node/src/event_dispatcher/payloads.rs

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ use stacks::chainstate::burn::operations::{
2626
BlockstackOperationType,
2727
};
2828
use stacks::chainstate::burn::ConsensusHash;
29+
use stacks::chainstate::coordinator::PoxTransactionReward;
2930
use stacks::chainstate::stacks::address::PoxAddress;
3031
use stacks::chainstate::stacks::boot::{
3132
NakamotoSignerEntry, PoxStartCycleInfo, RewardSet, RewardSetData,
3233
};
3334
use stacks::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksHeaderInfo};
3435
use stacks::chainstate::stacks::events::{
35-
StacksBlockEventData, StacksTransactionEvent, StacksTransactionReceipt, TransactionOrigin,
36+
BurnBlockEvent, BurnBlockEventRewardRecipient, StacksBlockEventData, StacksTransactionEvent,
37+
StacksTransactionReceipt, TransactionOrigin,
3638
};
3739
use stacks::chainstate::stacks::miner::TransactionEvent;
3840
use stacks::chainstate::stacks::{StacksTransaction, TransactionPayload};
@@ -208,34 +210,28 @@ pub fn make_new_burn_block_payload(
208210
burn_block_height: u64,
209211
rewards: Vec<(PoxAddress, u64)>,
210212
burns: u64,
213+
pox_transactions: Vec<PoxTransactionReward>,
211214
slot_holders: Vec<PoxAddress>,
212215
consensus_hash: &ConsensusHash,
213216
parent_burn_block_hash: &BurnchainHeaderHash,
214217
) -> serde_json::Value {
215-
let reward_recipients = rewards
216-
.into_iter()
217-
.map(|(pox_addr, amt)| {
218-
json!({
219-
"recipient": pox_addr.to_b58(),
220-
"amt": amt,
218+
serde_json::to_value(BurnBlockEvent {
219+
burn_block_hash: burn_block.clone(),
220+
burn_block_height,
221+
reward_recipients: rewards
222+
.into_iter()
223+
.map(|(pox_addr, amt)| BurnBlockEventRewardRecipient {
224+
recipient: pox_addr.clone(),
225+
amt,
221226
})
222-
})
223-
.collect();
224-
225-
let reward_slot_holders = slot_holders
226-
.into_iter()
227-
.map(|pox_addr| json!(pox_addr.to_b58()))
228-
.collect();
229-
230-
json!({
231-
"burn_block_hash": format!("0x{burn_block}"),
232-
"burn_block_height": burn_block_height,
233-
"reward_recipients": serde_json::Value::Array(reward_recipients),
234-
"reward_slot_holders": serde_json::Value::Array(reward_slot_holders),
235-
"burn_amount": burns,
236-
"consensus_hash": format!("0x{consensus_hash}"),
237-
"parent_burn_block_hash": format!("0x{parent_burn_block_hash}"),
227+
.collect(),
228+
reward_slot_holders: slot_holders,
229+
burn_amount: burns,
230+
consensus_hash: consensus_hash.clone(),
231+
parent_burn_block_hash: parent_burn_block_hash.clone(),
232+
pox_transactions,
238233
})
234+
.expect("FATAL: Failed to serialized burn block payload")
239235
}
240236

241237
const STATUS_RESP_TRUE: &str = "success";

stacks-node/src/tests/epoch_21.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,16 +1675,7 @@ fn transition_removes_pox_sunset() {
16751675
let recipients: Vec<(String, u64)> = block
16761676
.reward_recipients
16771677
.iter()
1678-
.map(|value| {
1679-
let recipient: String = value
1680-
.get("recipient")
1681-
.unwrap()
1682-
.as_str()
1683-
.unwrap()
1684-
.to_string();
1685-
let amount = value.get("amt").unwrap().as_u64().unwrap();
1686-
(recipient, amount)
1687-
})
1678+
.map(|value| (value.recipient.clone().to_b58(), value.amt))
16881679
.collect();
16891680

16901681
if (i as u64) < (sunset_start_rc * reward_cycle_len) {

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11406,7 +11406,7 @@ fn reload_miner_config() {
1140611406
let reward_amount = burn_block
1140711407
.reward_recipients
1140811408
.iter()
11409-
.map(|r| r.get("amt").unwrap().as_u64().unwrap())
11409+
.map(|r| r.amt)
1141011410
.sum::<u64>();
1141111411

1141211412
let burn_amount = burn_block.burn_amount;
@@ -11431,7 +11431,7 @@ fn reload_miner_config() {
1143111431
let reward_amount = burn_block
1143211432
.reward_recipients
1143311433
.iter()
11434-
.map(|r| r.get("amt").unwrap().as_u64().unwrap())
11434+
.map(|r| r.amt)
1143511435
.sum::<u64>();
1143611436

1143711437
let burn_amount = burn_block.burn_amount;

stacks-node/src/tests/neon_integrations.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,8 @@ pub mod test_observer {
296296
use std::sync::Mutex;
297297
use std::thread;
298298

299-
use libsigner::BurnBlockEvent;
300299
use stacks::chainstate::stacks::boot::RewardSet;
301-
use stacks::chainstate::stacks::events::StackerDBChunksEvent;
300+
use stacks::chainstate::stacks::events::{BurnBlockEvent, StackerDBChunksEvent};
302301
use stacks::chainstate::stacks::StacksTransaction;
303302
use stacks::codec::StacksMessageCodec;
304303
use stacks::config::{EventKeyType, EventObserverConfig};
@@ -5649,10 +5648,10 @@ fn pox_integration_test() {
56495648

56505649
for block in burn_blocks.iter() {
56515650
for holder in block.reward_slot_holders.iter() {
5652-
if let Some(current) = recipient_slots.get_mut(holder) {
5651+
if let Some(current) = recipient_slots.get_mut(&holder.clone().to_b58()) {
56535652
*current += 1;
56545653
} else {
5655-
recipient_slots.insert(holder.clone(), 1);
5654+
recipient_slots.insert(holder.clone().to_b58(), 1);
56565655
}
56575656
}
56585657
}

stacks-node/src/tests/signer/v0/mod.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8143,3 +8143,70 @@ fn signers_treat_signatures_as_precommits() {
81438143
info!("------------------------- Shutdown -------------------------");
81448144
signer_test.shutdown();
81458145
}
8146+
8147+
#[test]
8148+
#[ignore]
8149+
/// Ensure that the `/new_burn_block` payload includes data on individual PoX transactions.
8150+
fn burn_block_payload_includes_pox_transactions() {
8151+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
8152+
return;
8153+
}
8154+
let mut miners = MultipleMinerTest::new(5, 0);
8155+
8156+
let (conf_1, _conf_2) = miners.get_node_configs();
8157+
miners.boot_to_epoch_3();
8158+
let sortdb = conf_1.get_burnchain().open_sortition_db(true).unwrap();
8159+
8160+
info!("---- Starting test -----");
8161+
8162+
miners
8163+
.mine_bitcoin_blocks_and_confirm(&sortdb, 1, 30)
8164+
.expect("Failed to mine BTC block.");
8165+
miners.wait_for_chains(120);
8166+
8167+
let burn_blocks = test_observer::get_burn_blocks();
8168+
let new_burn_block = burn_blocks.last().unwrap();
8169+
8170+
info!("New burn block: {new_burn_block:?}";
8171+
"pox_transactions" => ?new_burn_block.pox_transactions,
8172+
);
8173+
8174+
let pox_transactions = new_burn_block.pox_transactions.clone();
8175+
assert_eq!(
8176+
pox_transactions.len(),
8177+
2,
8178+
"Expected 1 transaction from each miner"
8179+
);
8180+
8181+
let total_amt = new_burn_block
8182+
.reward_recipients
8183+
.iter()
8184+
.map(|r| r.amt)
8185+
.sum::<u64>();
8186+
8187+
let total_from_transactions = pox_transactions
8188+
.iter()
8189+
.map(|t| t.reward_recipients.iter().map(|r| r.amt).sum::<u64>())
8190+
.sum::<u64>();
8191+
8192+
assert_eq!(total_amt, total_from_transactions, "Expected the total sum from `reward_recipients` to match the total sum from `pox_transactions`");
8193+
8194+
let total_per_recipient: HashMap<PoxAddress, u64> = new_burn_block
8195+
.reward_recipients
8196+
.iter()
8197+
.map(|r| (r.recipient.clone(), r.amt))
8198+
.collect();
8199+
8200+
let mut total_per_recipient_from_transactions: HashMap<PoxAddress, u64> = HashMap::new();
8201+
8202+
for t in pox_transactions.iter() {
8203+
for r in t.reward_recipients.iter() {
8204+
total_per_recipient_from_transactions
8205+
.entry(r.recipient.clone())
8206+
.and_modify(|amt| *amt += r.amt)
8207+
.or_insert(r.amt);
8208+
}
8209+
}
8210+
8211+
assert_eq!(total_per_recipient, total_per_recipient_from_transactions);
8212+
}

0 commit comments

Comments
 (0)