Skip to content
This repository has been archived by the owner on Aug 22, 2024. It is now read-only.

Commit

Permalink
Merge pull request stacks-network#4311 from stacks-network/feat/rewar…
Browse files Browse the repository at this point in the history
…d-set-endpoint

Reward set calculation and RPC endpoint
  • Loading branch information
kantai authored Feb 6, 2024
2 parents 2d1f1ae + c5f4d81 commit 7c7a527
Show file tree
Hide file tree
Showing 23 changed files with 1,041 additions and 440 deletions.
1 change: 1 addition & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ jobs:
- tests::nakamoto_integrations::mine_multiple_per_tenure_integration
- tests::nakamoto_integrations::block_proposal_api_endpoint
- tests::nakamoto_integrations::miner_writes_proposed_block_to_stackerdb
- tests::nakamoto_integrations::correct_burn_outs
- tests::signer::stackerdb_dkg_sign
- tests::signer::stackerdb_block_proposal
steps:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE

### Added

- New RPC endpoint `/v2/stacker_set/{cycle_number}` to fetch stacker sets in PoX-4
- New `/new_pox_anchor` endpoint for broadcasting PoX anchor block processing.
- Stacker bitvec in NakamotoBlock

Expand Down
1 change: 1 addition & 0 deletions clarity/src/vm/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub enum RuntimeErrorType {
UnwrapFailure,
DefunctPoxContract,
PoxAlreadyLocked,
MetadataAlreadySet,
}

#[derive(Debug, PartialEq)]
Expand Down
4 changes: 4 additions & 0 deletions docs/rpc/api/core-node/get_stacker_set.400.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"response": "error",
"err_msg": "Could not read reward set. Prepare phase may not have started for this cycle yet. Cycle = 22, Err = PoXAnchorBlockRequired"
}
25 changes: 25 additions & 0 deletions docs/rpc/api/core-node/get_stacker_set.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"stacker_set": {
"rewarded_addresses": [
{
"Standard": [
{
"bytes": "dc5f18421006ee2b98ab972edfa7268a981e3f00",
"version": 26
},
"SerializeP2PKH"
]
}
],
"signers": [
{
"signing_key": "02d0a27e4f1bf186b4391eecfcc4d4a0d403684ad089b477b8548a69dd6378bf26",
"slots": 1,
"stacked_amt": 2143020000000000
}
],
"start_cycle_state": {
"missed_reward_slots": []
}
}
}
24 changes: 24 additions & 0 deletions docs/rpc/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -583,3 +583,27 @@ paths:
application/json:
example:
$ref: ./api/core-node/post-block-proposal-req.example.json

/v2/stacker_set/{cycle_number}:
get:
summary: Fetch the stacker and signer set information for a given cycle.
tags:
- Mining
operationId: get_stacker_set
description: |
Used to get stacker and signer set information for a given cycle.
This will only return information for cycles started in Epoch-2.5 where PoX-4 was active and subsequent cycles.
responses:
200:
description: Information for the given reward cycle
content:
application/json:
example:
$ref: ./api/core-node/get_stacker_set.example.json
400:
description: Could not fetch the given reward set
content:
application/json:
example:
$ref: ./api/core-node/get_stacker_set.400.example.json
12 changes: 12 additions & 0 deletions stackslib/src/burnchains/burnchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,18 @@ impl Burnchain {
.reward_cycle_to_block_height(self.first_block_height, reward_cycle)
}

pub fn next_reward_cycle(&self, block_height: u64) -> Option<u64> {
let cycle = self.block_height_to_reward_cycle(block_height)?;
let effective_height = block_height.checked_sub(self.first_block_height)?;
let next_bump = if effective_height % u64::from(self.pox_constants.reward_cycle_length) == 0
{
0
} else {
1
};
Some(cycle + next_bump)
}

pub fn block_height_to_reward_cycle(&self, block_height: u64) -> Option<u64> {
self.pox_constants
.block_height_to_reward_cycle(self.first_block_height, block_height)
Expand Down
73 changes: 53 additions & 20 deletions stackslib/src/chainstate/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ pub trait RewardSetProvider {
sortdb: &SortitionDB,
block_id: &StacksBlockId,
) -> Result<RewardSet, Error>;

fn get_reward_set_nakamoto(
&self,
cycle_start_burn_height: u64,
chainstate: &mut StacksChainState,
burnchain: &Burnchain,
sortdb: &SortitionDB,
block_id: &StacksBlockId,
) -> Result<RewardSet, Error>;
}

pub struct OnChainRewardSetProvider<'a, T: BlockEventDispatcher>(pub Option<&'a T>);
Expand All @@ -312,6 +321,16 @@ impl<'a, T: BlockEventDispatcher> RewardSetProvider for OnChainRewardSetProvider
let cycle = burnchain
.block_height_to_reward_cycle(cycle_start_burn_height)
.expect("FATAL: no reward cycle for burn height");
// `self.get_reward_set_nakamoto` reads the reward set from data written during
// updates to .signers
// `self.get_reward_set_epoch2` reads the reward set from the `.pox-*` contract
//
// Data **cannot** be read from `.signers` in epoch 2.5 because the write occurs
// in the first block of the prepare phase, but the PoX anchor block is *before*
// the prepare phase. Therefore, we fetch the reward set in the 2.x style, and then
// apply the necessary nakamoto assertions if the reward set is going to be
// active in Nakamoto (i.e., check for signer set existence).

let is_nakamoto_reward_set = match SortitionDB::get_stacks_epoch_by_epoch_id(
sortdb.conn(),
&StacksEpochId::Epoch30,
Expand All @@ -325,33 +344,47 @@ impl<'a, T: BlockEventDispatcher> RewardSetProvider for OnChainRewardSetProvider
// if epoch-3.0 isn't defined, then never use a nakamoto reward set.
None => false,
};
let reward_set = if !is_nakamoto_reward_set {
// Stacks 2.x epoch
self.get_reward_set_epoch2(
cycle_start_burn_height,
chainstate,
burnchain,
sortdb,
block_id,
cur_epoch,
)?
} else {
// Nakamoto epoch
self.get_reward_set_nakamoto(
cycle_start_burn_height,
chainstate,
burnchain,
sortdb,
block_id,
)?
};

let reward_set = self.get_reward_set_epoch2(
cycle_start_burn_height,
chainstate,
burnchain,
sortdb,
block_id,
cur_epoch,
)?;

if is_nakamoto_reward_set {
if reward_set.signers.is_none() || reward_set.signers == Some(vec![]) {
error!("FATAL: Signer sets are empty in a reward set that will be used in nakamoto"; "reward_set" => ?reward_set);
return Err(Error::PoXAnchorBlockRequired);
}
}

if let Some(dispatcher) = self.0 {
dispatcher.announce_reward_set(&reward_set, block_id, cycle);
}

Ok(reward_set)
}

fn get_reward_set_nakamoto(
&self,
cycle_start_burn_height: u64,
chainstate: &mut StacksChainState,
burnchain: &Burnchain,
sortdb: &SortitionDB,
block_id: &StacksBlockId,
) -> Result<RewardSet, Error> {
self.read_reward_set_nakamoto(
cycle_start_burn_height,
chainstate,
burnchain,
sortdb,
block_id,
false,
)
}
}

impl<'a, T: BlockEventDispatcher> OnChainRewardSetProvider<'a, T> {
Expand Down
11 changes: 11 additions & 0 deletions stackslib/src/chainstate/coordinator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,17 @@ impl RewardSetProvider for StubbedRewardSetProvider {
signers: None,
})
}

fn get_reward_set_nakamoto(
&self,
cycle_start_burn_height: u64,
chainstate: &mut StacksChainState,
burnchain: &Burnchain,
sortdb: &SortitionDB,
block_id: &StacksBlockId,
) -> Result<RewardSet, CoordError> {
panic!("Stubbed reward set provider cannot be invoked in nakamoto")
}
}

fn make_reward_set_coordinator<'a>(
Expand Down
Loading

0 comments on commit 7c7a527

Please sign in to comment.