Skip to content

Commit ecaaf5c

Browse files
Merge pull request #6842 from aaronb-stacks/chore/release-0.5-prep
Chore: Release 0.5 prep
2 parents 0bbd05a + f98d042 commit ecaaf5c

File tree

30 files changed

+1538
-279
lines changed

30 files changed

+1538
-279
lines changed

CHANGELOG.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to the versioning scheme outlined in the [README.md](README.md).
77

8+
## [3.3.0.0.5]
9+
10+
### Added
11+
12+
- New endpoint `/v3/blocks/simulate/{block_id}` allows to simulate the execution of a specific block with a brand new set of transactions
13+
- Improved block validation in `stacks-inspect`.
14+
15+
### Changed
16+
17+
- Removed `validate-naka-block` option in `stacks-inspect`, merging it with `validate-block` so that users do not need to differentiate between the two.
18+
819
## [3.3.0.0.4]
920

1021
### Added
@@ -17,11 +28,6 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1728
### Added
1829

1930
- In the `/v3/transaction/{txid}` RPC endpoint, added `block_height` and `is_canonical` to the response.
20-
- Improved block validation in `stacks-inspect`.
21-
22-
### Changed
23-
24-
- Removed `validate-naka-block` option in `stacks-inspect`, merging it with `validate-block` so that users do not need to differentiate between the two.
2531

2632
### Fixed
2733

clarity/src/vm/functions/post_conditions.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,8 @@ fn check_allowances(
477477
}
478478

479479
// Check STX movements
480-
if let Some(stx_moved) = assets.get_stx(owner) {
480+
let amount_moved = assets.get_stx(owner);
481+
if let Some(stx_moved) = amount_moved {
481482
if stx_allowances.is_empty() {
482483
// If there are no allowances for STX, any movement is a violation
483484
record_violation(MAX_ALLOWANCES as u128);
@@ -492,7 +493,8 @@ fn check_allowances(
492493
}
493494

494495
// Check STX burns
495-
if let Some(stx_burned) = assets.get_stx_burned(owner) {
496+
let amount_burned = assets.get_stx_burned(owner);
497+
if let Some(stx_burned) = amount_burned {
496498
if stx_allowances.is_empty() {
497499
// If there are no allowances for STX, any burn is a violation
498500
record_violation(MAX_ALLOWANCES as u128);
@@ -581,6 +583,26 @@ fn check_allowances(
581583
}
582584
}
583585

586+
if earliest_violation.is_none() {
587+
// Check combined STX burns and movements. If the total exceeds any allowance,
588+
// emit an error that makes this transaction invalid.
589+
let total_stx_change = amount_moved
590+
.unwrap_or(0)
591+
.checked_add(amount_burned.unwrap_or(0))
592+
.ok_or(VmInternalError::Expect(
593+
"STX movement and burn overflowed u128".into(),
594+
))?;
595+
if total_stx_change > 0 {
596+
for (_, allowance) in &stx_allowances {
597+
if total_stx_change > *allowance {
598+
return Err(VmExecutionError::Internal(VmInternalError::Expect(
599+
"Total STX movement and burn exceeds allowance".into(),
600+
)));
601+
}
602+
}
603+
}
604+
}
605+
584606
Ok(earliest_violation)
585607
}
586608

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: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ use stacks::core::{
7979
PEER_VERSION_EPOCH_1_0, PEER_VERSION_EPOCH_2_0, PEER_VERSION_EPOCH_2_05,
8080
PEER_VERSION_EPOCH_2_1, PEER_VERSION_EPOCH_2_2, PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_2_4,
8181
PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0, PEER_VERSION_EPOCH_3_1, PEER_VERSION_EPOCH_3_2,
82-
PEER_VERSION_TESTNET,
82+
PEER_VERSION_EPOCH_3_3, PEER_VERSION_TESTNET,
8383
};
8484
use stacks::libstackerdb::{SlotMetadata, StackerDBChunkData};
8585
use stacks::net::api::callreadonly::CallReadOnlyRequestBody;
@@ -230,7 +230,7 @@ lazy_static! {
230230
start_height: 252,
231231
end_height: STACKS_EPOCH_MAX,
232232
block_limit: HELIUM_BLOCK_LIMIT_20,
233-
network_epoch: PEER_VERSION_EPOCH_3_2
233+
network_epoch: PEER_VERSION_EPOCH_3_3
234234
},
235235
];
236236
}
@@ -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
@@ -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
}

0 commit comments

Comments
 (0)