Skip to content

Commit 2c55318

Browse files
author
Aaron Blankstein
authored
Merge pull request #2021 from blockstack/feat/events-1930
Event dispatcher updates
2 parents c5bb746 + e0e2f91 commit 2c55318

File tree

13 files changed

+322
-82
lines changed

13 files changed

+322
-82
lines changed

.github/workflows/stacks-blockchain.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ jobs:
4848
# Run net-tests
4949
nettest:
5050
runs-on: ubuntu-latest
51+
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
5152
steps:
5253
- uses: actions/checkout@v2
5354
- name: Run network relay tests

docs/event-dispatcher.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,41 @@ Example:
6767
"tx_index": 1,
6868
"txid": "0x738e4d44636023efa08374033428e44eca490582bd39a6e61f3b6cf749b4214c"
6969
}
70-
]
70+
],
71+
"matured_miner_rewards": [
72+
{
73+
"recipient": "ST31DA6FTSJX2WGTZ69SFY11BH51NZMB0ZZ239N96",
74+
"coinbase_amount": "1000",
75+
"tx_fees_anchored_shared": "800",
76+
"tx_fees_anchored_exclusive": "0",
77+
"tx_fees_streamed_confirmed": "0",
78+
"from_stacks_block_hash": "0xf5d4ce0efe1d42c963d615ce57f0d014f263a985175e4ece766eceff10e0a358",
79+
"from_index_block_hash": "0x329efcbcc6daf5ac3f264522e0df50eddb5be85df6ee8a9fc2384c54274d7afc",
80+
}
81+
]
7182
}
7283
```
7384

85+
### `POST /new_burn_block`
86+
87+
This payload includes information about burn blocks as their sortitions are processed.
88+
In the event of PoX forks, a `new_burn_block` event may be triggered for a burn block
89+
previously processed.
90+
91+
Example:
92+
93+
```json
94+
{
95+
"burn_block_hash": "0x4eaabcd105865e471f697eff5dd5bd85d47ecb5a26a3379d74fae0ae87c40904",
96+
"reward_recipients": [
97+
{
98+
"recipient": "1C56LYirKa3PFXFsvhSESgDy2acEHVAEt6",
99+
"amount": 5000
100+
}
101+
],
102+
"burn_amount": 12000
103+
}
104+
```
74105

75106
### `POST /new_mempool_tx`
76107

src/chainstate/coordinator/mod.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,23 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17-
use std::collections::VecDeque;
17+
use std::collections::{HashMap, VecDeque};
1818
use std::convert::{TryFrom, TryInto};
1919
use std::time::Duration;
2020

2121
use burnchains::{
2222
db::{BurnchainBlockData, BurnchainDB},
23-
Burnchain, BurnchainBlockHeader, BurnchainHeaderHash, Error as BurnchainError, Txid,
23+
Address, Burnchain, BurnchainBlockHeader, BurnchainHeaderHash, Error as BurnchainError, Txid,
2424
};
2525
use chainstate::burn::{
2626
db::sortdb::{PoxId, SortitionDB, SortitionId},
2727
operations::leader_block_commit::RewardSetInfo,
28+
operations::BlockstackOperationType,
2829
BlockHeaderHash, BlockSnapshot, ConsensusHash,
2930
};
3031
use chainstate::stacks::{
3132
boot::STACKS_BOOT_CODE_CONTRACT_ADDRESS,
32-
db::{ClarityTx, StacksChainState, StacksHeaderInfo},
33+
db::{accounts::MinerReward, ClarityTx, MinerRewardInfo, StacksChainState, StacksHeaderInfo},
3334
events::StacksTransactionReceipt,
3435
Error as ChainstateError, StacksAddress, StacksBlock, StacksBlockHeader, StacksBlockId,
3536
};
@@ -108,6 +109,19 @@ pub trait BlockEventDispatcher {
108109
receipts: Vec<StacksTransactionReceipt>,
109110
parent: &StacksBlockId,
110111
winner_txid: Txid,
112+
matured_rewards: Vec<MinerReward>,
113+
matured_rewards_info: Option<MinerRewardInfo>,
114+
);
115+
116+
/// called whenever a burn block is about to be
117+
/// processed for sortition. note, in the event
118+
/// of PoX forks, this will be called _multiple_
119+
/// times for the same burnchain header hash.
120+
fn announce_burn_block(
121+
&self,
122+
burn_block: &BurnchainHeaderHash,
123+
rewards: Vec<(StacksAddress, u64)>,
124+
burns: u64,
111125
);
112126

113127
fn dispatch_boot_receipts(&mut self, receipts: Vec<StacksTransactionReceipt>);
@@ -426,6 +440,33 @@ pub fn get_reward_cycle_info<U: RewardSetProvider>(
426440
}
427441
}
428442

443+
fn dispatcher_announce_burn_ops<T: BlockEventDispatcher>(
444+
dispatcher: &T,
445+
burn_header: &BurnchainBlockHeader,
446+
ops: &[BlockstackOperationType],
447+
) {
448+
let mut reward_recipients: HashMap<_, u64> = HashMap::new();
449+
let mut burn_amt = 0;
450+
for op in ops.iter() {
451+
if let BlockstackOperationType::LeaderBlockCommit(commit) = op {
452+
let amt_per_address = commit.burn_fee / (commit.commit_outs.len() as u64);
453+
for addr in commit.commit_outs.iter() {
454+
if addr.is_burn() {
455+
burn_amt += amt_per_address;
456+
} else {
457+
if let Some(prior_amt) = reward_recipients.get_mut(addr) {
458+
*prior_amt += amt_per_address;
459+
} else {
460+
reward_recipients.insert(addr.clone(), amt_per_address);
461+
}
462+
}
463+
}
464+
}
465+
}
466+
let reward_recipients_vec = reward_recipients.into_iter().collect();
467+
dispatcher.announce_burn_block(&burn_header.block_hash, reward_recipients_vec, burn_amt);
468+
}
469+
429470
impl<'a, T: BlockEventDispatcher, N: CoordinatorNotices, U: RewardSetProvider>
430471
ChainsCoordinator<'a, T, N, U>
431472
{
@@ -475,6 +516,10 @@ impl<'a, T: BlockEventDispatcher, N: CoordinatorNotices, U: RewardSetProvider>
475516
for unprocessed_block in sortitions_to_process.drain(..) {
476517
let BurnchainBlockData { header, ops } = unprocessed_block;
477518

519+
if let Some(dispatcher) = self.dispatcher {
520+
dispatcher_announce_burn_ops(dispatcher, &header, &ops);
521+
}
522+
478523
let sortition_tip_snapshot = SortitionDB::get_block_snapshot(
479524
self.sortition_db.conn(),
480525
&canonical_sortition_tip,
@@ -621,6 +666,8 @@ impl<'a, T: BlockEventDispatcher, N: CoordinatorNotices, U: RewardSetProvider>
621666
block_receipt.tx_receipts,
622667
&parent,
623668
winner_txid,
669+
block_receipt.matured_rewards,
670+
block_receipt.matured_rewards_info,
624671
);
625672
}
626673

src/chainstate/coordinator/tests.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use util::hash::Hash160;
2424
use burnchains::{db::*, *};
2525
use chainstate::burn::db::sortdb::{PoxId, SortitionDB, SortitionId};
2626
use chainstate::burn::*;
27-
use chainstate::stacks::db::{ClarityTx, StacksChainState, StacksHeaderInfo};
27+
use chainstate::stacks::db::{
28+
accounts::MinerReward, ClarityTx, StacksChainState, StacksHeaderInfo,
29+
};
2830
use chainstate::stacks::index::TrieHash;
2931
use core;
3032
use monitoring::increment_stx_blocks_processed_counter;
@@ -229,14 +231,24 @@ impl BlockEventDispatcher for NullEventDispatcher {
229231
_receipts: Vec<StacksTransactionReceipt>,
230232
_parent: &StacksBlockId,
231233
_winner_txid: Txid,
234+
_rewards: Vec<MinerReward>,
235+
_rewards_info: Option<MinerRewardInfo>,
232236
) {
233237
assert!(
234238
false,
235239
"We should never try to announce to the null dispatcher"
236240
);
237241
}
238242

239-
fn dispatch_boot_receipts(&mut self, receipts: Vec<StacksTransactionReceipt>) {}
243+
fn announce_burn_block(
244+
&self,
245+
_burn_block: &BurnchainHeaderHash,
246+
_rewards: Vec<(StacksAddress, u64)>,
247+
_burns: u64,
248+
) {
249+
}
250+
251+
fn dispatch_boot_receipts(&mut self, _receipts: Vec<StacksTransactionReceipt>) {}
240252
}
241253

242254
pub fn make_coordinator<'a>(

src/chainstate/stacks/address.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use util::hash::HASH160_ENCODED_SIZE;
3636
use burnchains::Address;
3737
use burnchains::PublicKey;
3838

39+
use address::b58;
3940
use address::c32::c32_address_decode;
4041

4142
use deps::bitcoin::blockdata::opcodes::All as BtcOp;
@@ -167,6 +168,16 @@ impl StacksAddress {
167168
}
168169
}
169170

171+
pub fn to_b58(self) -> String {
172+
let StacksAddress { version, bytes } = self;
173+
let btc_version = to_b52_version_byte(version)
174+
// fallback to version
175+
.unwrap_or(version);
176+
let mut all_bytes = vec![btc_version];
177+
all_bytes.extend(bytes.0.iter());
178+
b58::check_encode_slice(&all_bytes)
179+
}
180+
170181
/// Convert to PrincipalData::Standard(StandardPrincipalData)
171182
pub fn to_account_principal(&self) -> PrincipalData {
172183
PrincipalData::Standard(StandardPrincipalData(

src/chainstate/stacks/db/accounts.rs

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -116,30 +116,6 @@ impl FromRow<MinerPaymentSchedule> for MinerPaymentSchedule {
116116
}
117117

118118
impl MinerReward {
119-
pub fn empty_miner(address: &StacksAddress) -> MinerReward {
120-
MinerReward {
121-
address: address.clone(),
122-
coinbase: 0,
123-
tx_fees_anchored_shared: 0,
124-
tx_fees_anchored_exclusive: 0,
125-
tx_fees_streamed_produced: 0,
126-
tx_fees_streamed_confirmed: 0,
127-
vtxindex: 0,
128-
}
129-
}
130-
131-
pub fn empty_user(address: &StacksAddress, vtxindex: u32) -> MinerReward {
132-
MinerReward {
133-
address: address.clone(),
134-
coinbase: 0,
135-
tx_fees_anchored_shared: 0,
136-
tx_fees_anchored_exclusive: 0,
137-
tx_fees_streamed_produced: 0,
138-
tx_fees_streamed_confirmed: 0,
139-
vtxindex: vtxindex,
140-
}
141-
}
142-
143119
pub fn total(&self) -> u128 {
144120
self.coinbase
145121
+ self.tx_fees_anchored_shared
@@ -624,11 +600,11 @@ impl StacksChainState {
624600
/// scheduled miner payments to the cache. Miner payments are immutable once written, and are
625601
/// keyed to the index block hash (which is globally unique), so once cached, no invalidation
626602
/// should be necessary.
627-
pub fn find_mature_miner_rewards<'a>(
628-
tx: &mut StacksDBTx<'a>,
603+
pub fn find_mature_miner_rewards(
604+
tx: &mut StacksDBTx,
629605
tip: &StacksHeaderInfo,
630606
mut cache: Option<&mut MinerPaymentCache>,
631-
) -> Result<Option<Vec<MinerReward>>, Error> {
607+
) -> Result<Option<(Vec<MinerReward>, MinerRewardInfo)>, Error> {
632608
if tip.block_height <= MINER_REWARD_MATURITY + MINER_REWARD_WINDOW {
633609
// no mature rewards exist
634610
return Ok(None);
@@ -736,6 +712,11 @@ impl StacksChainState {
736712
assert_eq!(*scheduled_payments.last().unwrap(), rs_miners_after);
737713
}
738714

715+
let matured_rewards_info = MinerRewardInfo {
716+
from_stacks_block_hash: matured_miners.0.block_hash.clone(),
717+
from_block_consensus_hash: matured_miners.0.consensus_hash.clone(),
718+
};
719+
739720
let mut rewards = vec![];
740721
let miner_reward =
741722
StacksChainState::calculate_miner_reward(&matured_miners.0, &scheduled_payments);
@@ -745,7 +726,7 @@ impl StacksChainState {
745726
let reward = StacksChainState::calculate_miner_reward(user_reward, &scheduled_payments);
746727
rewards.push(reward);
747728
}
748-
Ok(Some(rewards))
729+
Ok(Some((rewards, matured_rewards_info)))
749730
}
750731
}
751732

@@ -1162,31 +1143,33 @@ mod test {
11621143
let legacy_rewards = old_find_mature_miner_rewards(&mut tx, &parent_tip)
11631144
.unwrap()
11641145
.unwrap();
1146+
11651147
assert_eq!(legacy_rewards, expected_rewards);
11661148

1167-
let rewards_opt =
1168-
StacksChainState::find_mature_miner_rewards(&mut tx, &parent_tip, None).unwrap();
1169-
assert!(rewards_opt.is_some());
1149+
let (rewards, _) = StacksChainState::find_mature_miner_rewards(&mut tx, &parent_tip, None)
1150+
.unwrap()
1151+
.unwrap();
11701152

1171-
let rewards = rewards_opt.unwrap();
11721153
assert_eq!(rewards.len(), 2);
11731154
assert_eq!(rewards, expected_rewards);
11741155

1175-
let rewards_cached =
1156+
let (rewards_cached, _) =
11761157
StacksChainState::find_mature_miner_rewards(&mut tx, &parent_tip, Some(&mut cache))
11771158
.unwrap()
11781159
.unwrap();
1160+
11791161
assert_eq!(rewards_cached, rewards);
11801162
assert_eq!(rewards_cached, expected_rewards);
11811163

11821164
let mut empty_cache = MinerPaymentCache::new();
1183-
let rewards_cached = StacksChainState::find_mature_miner_rewards(
1165+
let (rewards_cached, _) = StacksChainState::find_mature_miner_rewards(
11841166
&mut tx,
11851167
&parent_tip,
11861168
Some(&mut empty_cache),
11871169
)
11881170
.unwrap()
11891171
.unwrap();
1172+
11901173
assert_eq!(rewards_cached, rewards);
11911174
assert_eq!(rewards_cached, expected_rewards);
11921175
}

src/chainstate/stacks/db/blocks.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3753,9 +3753,9 @@ impl StacksChainState {
37533753
/// block's transactions. Finally, it returns the execution costs for the microblock stream
37543754
/// and for the anchored block (separately).
37553755
/// Returns None if we're out of blocks to process.
3756-
fn append_block<'a>(
3757-
chainstate_tx: &mut ChainstateTx<'a>,
3758-
clarity_instance: &'a mut ClarityInstance,
3756+
fn append_block(
3757+
chainstate_tx: &mut ChainstateTx,
3758+
clarity_instance: &mut ClarityInstance,
37593759
burn_dbconn: &dyn BurnStateDB,
37603760
parent_chain_tip: &StacksHeaderInfo,
37613761
chain_tip_consensus_hash: &ConsensusHash,
@@ -3778,17 +3778,19 @@ impl StacksChainState {
37783778
let next_block_height = block.header.total_work.work;
37793779

37803780
// find matured miner rewards, so we can grant them within the Clarity DB tx.
3781-
let matured_miner_rewards_opt = {
3782-
StacksChainState::find_mature_miner_rewards(
3781+
let (matured_rewards, matured_rewards_info) =
3782+
match StacksChainState::find_mature_miner_rewards(
37833783
&mut chainstate_tx.headers_tx,
37843784
parent_chain_tip,
37853785
Some(chainstate_tx.miner_payment_cache),
3786-
)?
3787-
};
3786+
)? {
3787+
Some((rewards, rewards_info)) => (rewards, Some(rewards_info)),
3788+
None => (vec![], None),
3789+
};
37883790

37893791
let (
37903792
scheduled_miner_reward,
3791-
txs_receipts,
3793+
tx_receipts,
37923794
microblock_execution_cost,
37933795
block_execution_cost,
37943796
total_liquid_ustx,
@@ -3896,16 +3898,12 @@ impl StacksChainState {
38963898
.expect("BUG: microblock cost + block cost < block cost");
38973899

38983900
// grant matured miner rewards
3899-
let new_liquid_miner_ustx =
3900-
if let Some(mature_miner_rewards) = matured_miner_rewards_opt {
3901-
// grant in order by miner, then users
3902-
StacksChainState::process_matured_miner_rewards(
3903-
&mut clarity_tx,
3904-
&mature_miner_rewards,
3905-
)?
3906-
} else {
3907-
0
3908-
};
3901+
let new_liquid_miner_ustx = if matured_rewards.len() > 0 {
3902+
// grant in order by miner, then users
3903+
StacksChainState::process_matured_miner_rewards(&mut clarity_tx, &matured_rewards)?
3904+
} else {
3905+
0
3906+
};
39093907

39103908
// total burns
39113909
let total_burnt = block_burns
@@ -3994,11 +3992,13 @@ impl StacksChainState {
39943992
)
39953993
.expect("FATAL: failed to advance chain tip");
39963994

3997-
chainstate_tx.log_transactions_processed(&new_tip.index_block_hash(), &txs_receipts);
3995+
chainstate_tx.log_transactions_processed(&new_tip.index_block_hash(), &tx_receipts);
39983996

39993997
let epoch_receipt = StacksEpochReceipt {
40003998
header: new_tip,
4001-
tx_receipts: txs_receipts,
3999+
tx_receipts,
4000+
matured_rewards,
4001+
matured_rewards_info,
40024002
parent_microblocks_cost: microblock_execution_cost,
40034003
anchored_block_cost: block_execution_cost,
40044004
};

0 commit comments

Comments
 (0)