Skip to content

Commit 7794552

Browse files
committed
Merge branch 'develop' into feat/clarity-5
2 parents 3d081c3 + 6c76d35 commit 7794552

File tree

24 files changed

+1458
-265
lines changed

24 files changed

+1458
-265
lines changed

CHANGELOG.md

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

2525
- In the `/v3/transaction/{txid}` RPC endpoint, added `block_height` and `is_canonical` to the response.
26+
- New endpoint `/v3/blocks/simulate/{block_id}` allows to simulate the execution of a specific block with a brand new set of transactions
2627
- Improved block validation in `stacks-inspect`.
2728

2829
### Changed

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: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use rand::Rng;
3535
use serde_json::json;
3636
use stacks::burnchains::{PoxConstants, Txid};
3737
use stacks::chainstate::burn::ConsensusHash;
38-
use stacks::chainstate::coordinator::BlockEventDispatcher;
38+
use stacks::chainstate::coordinator::{BlockEventDispatcher, PoxTransactionReward};
3939
use stacks::chainstate::nakamoto::NakamotoBlock;
4040
use stacks::chainstate::stacks::address::PoxAddress;
4141
use stacks::chainstate::stacks::boot::RewardSetData;
@@ -393,6 +393,7 @@ impl BlockEventDispatcher for EventDispatcher {
393393
burn_block_height: u64,
394394
rewards: Vec<(PoxAddress, u64)>,
395395
burns: u64,
396+
pox_transactions: Vec<PoxTransactionReward>,
396397
recipient_info: Vec<PoxAddress>,
397398
consensus_hash: &ConsensusHash,
398399
parent_burn_block_hash: &BurnchainHeaderHash,
@@ -402,6 +403,7 @@ impl BlockEventDispatcher for EventDispatcher {
402403
burn_block_height,
403404
rewards,
404405
burns,
406+
pox_transactions,
405407
recipient_info,
406408
consensus_hash,
407409
parent_burn_block_hash,
@@ -439,6 +441,7 @@ impl EventDispatcher {
439441
burn_block_height: u64,
440442
rewards: Vec<(PoxAddress, u64)>,
441443
burns: u64,
444+
pox_transactions: Vec<PoxTransactionReward>,
442445
recipient_info: Vec<PoxAddress>,
443446
consensus_hash: &ConsensusHash,
444447
parent_burn_block_hash: &BurnchainHeaderHash,
@@ -454,6 +457,7 @@ impl EventDispatcher {
454457
burn_block_height,
455458
rewards,
456459
burns,
460+
pox_transactions,
457461
recipient_info,
458462
consensus_hash,
459463
parent_burn_block_hash,
@@ -1197,7 +1201,9 @@ impl EventDispatcher {
11971201
"attempts" => attempts
11981202
);
11991203
if disable_retries {
1200-
warn!("Observer is configured in disable_retries mode: skipping retry of payload");
1204+
warn!(
1205+
"Observer is configured in disable_retries mode: skipping retry of payload"
1206+
);
12011207
return Err(err.into());
12021208
}
12031209
#[cfg(test)]

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
@@ -11413,7 +11413,7 @@ fn reload_miner_config() {
1141311413
let reward_amount = burn_block
1141411414
.reward_recipients
1141511415
.iter()
11416-
.map(|r| r.get("amt").unwrap().as_u64().unwrap())
11416+
.map(|r| r.amt)
1141711417
.sum::<u64>();
1141811418

1141911419
let burn_amount = burn_block.burn_amount;
@@ -11438,7 +11438,7 @@ fn reload_miner_config() {
1143811438
let reward_amount = burn_block
1143911439
.reward_recipients
1144011440
.iter()
11441-
.map(|r| r.get("amt").unwrap().as_u64().unwrap())
11441+
.map(|r| r.amt)
1144211442
.sum::<u64>();
1144311443

1144411444
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
@@ -280,9 +280,8 @@ pub mod test_observer {
280280
use std::sync::Mutex;
281281
use std::thread;
282282

283-
use libsigner::BurnBlockEvent;
284283
use stacks::chainstate::stacks::boot::RewardSet;
285-
use stacks::chainstate::stacks::events::StackerDBChunksEvent;
284+
use stacks::chainstate::stacks::events::{BurnBlockEvent, StackerDBChunksEvent};
286285
use stacks::chainstate::stacks::StacksTransaction;
287286
use stacks::codec::StacksMessageCodec;
288287
use stacks::config::{EventKeyType, EventObserverConfig};
@@ -5611,10 +5610,10 @@ fn pox_integration_test() {
56115610

56125611
for block in burn_blocks.iter() {
56135612
for holder in block.reward_slot_holders.iter() {
5614-
if let Some(current) = recipient_slots.get_mut(holder) {
5613+
if let Some(current) = recipient_slots.get_mut(&holder.clone().to_b58()) {
56155614
*current += 1;
56165615
} else {
5617-
recipient_slots.insert(holder.clone(), 1);
5616+
recipient_slots.insert(holder.clone().to_b58(), 1);
56185617
}
56195618
}
56205619
}

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

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

0 commit comments

Comments
 (0)