Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nakamoto: Add stacker/signer bit vector and event #4269

Merged
merged 20 commits into from
Jan 29, 2024
Merged

Conversation

kantai
Copy link
Member

@kantai kantai commented Jan 21, 2024

Description

This PR implements a handful of things:

  1. It adds a BitVec class to stacks-common. This PR is at least the third implementation of a bit vector in the codebase, so it seemed like adding a simple data struct would be useful here.
  2. Adds a BitVec field to the NakamotoBlockHeader, this is included in the consensus serialization and the stacker/signer signature hash. It is not included in the miner's signature hash.
  3. Adds a new_pox_set endpoint for the event dispatcher. This endpoint announces when a reward set is calculated in a PoX anchor block. It relays information about the reward set:
    • The cycle number of the set
    • The StacksBlockId of the anchor block
    • The canonically ordered list of reward addresses (with repetitions for addresses with multiple slots)
    • The canonically ordered list of signers, each entry containing a signing address (C32-encoded), the number of signing slots, and the amount stacked. This canonical ordering is what will be used for entries in the bit vector.
  4. It implements make_signer_set, which is invoked by make_reward_set to translate the list of RawRewardEntrys into signing set entries. Currently, it does this by summing all entries with the same public key and assigning slots based on the slot thresholds.
  5. It mocks data for the signing key in raw reward entries in nakamoto_get_reward_set()
  6. It alters the logic deciding between using nakamoto reward set fetching and 2.x: any reward set that is used by Nakamoto should be fetched via the nakamoto reward set endpoint. The first nakamoto reward set will occur in epoch 2.5.
  7. It fixes a broken integration test correct_burn_outs -- this integration test wasn't asserting that the sortition db advanced, so it missed the fact that the recent update in next to require signing keys (and unique ones) broke the test. The test now asserts the sortition db advances and it works.
  8. It update the correct_burn_outs test to check the results of the new_pox_set event observer API are correct.

Still needs test coverage

  • make_signer_set needs unit testing. This is easy enough to add and I will add it before this merges, but before I did that, I wanted to get feedback on this PR because if make_signer_set needs to change, it seems like a waste to write the unit tests now.

Relevant issues

#4241 - this is the stacks-node side of tracking. this PR doesn't implement the punishment for non-participation, though.
#4256 - this PR emits this list
#4255 - summing over the signer set entries in this PR gives the amount of active lockup. the amount of total lockup (which could include people who miss the threshold) would need to be added as another field in the return. This should be simple to add and I can do it if its useful (@zone117x).

Copy link

codecov bot commented Jan 21, 2024

Codecov Report

Attention: 634 lines in your changes are missing coverage. Please review.

Comparison is base (d8eac43) 17.66% compared to head (d9c1aa1) 52.88%.

Files Patch % Lines
...ckslib/src/chainstate/stacks/boot/signers_tests.rs 6.31% 267 Missing ⚠️
...tackslib/src/chainstate/stacks/boot/pox_4_tests.rs 60.00% 138 Missing ⚠️
testnet/stacks-node/src/tests/neon_integrations.rs 14.28% 36 Missing ⚠️
...net/stacks-node/src/tests/nakamoto_integrations.rs 2.85% 34 Missing ⚠️
stacks-common/src/bitvec.rs 83.66% 33 Missing ⚠️
stackslib/src/chainstate/nakamoto/mod.rs 90.31% 25 Missing ⚠️
stackslib/src/chainstate/nakamoto/tests/mod.rs 37.50% 15 Missing ⚠️
stackslib/src/chainstate/nakamoto/tests/node.rs 17.64% 14 Missing ⚠️
testnet/stacks-node/src/event_dispatcher.rs 78.78% 14 Missing ⚠️
...ackslib/src/chainstate/nakamoto/coordinator/mod.rs 84.00% 12 Missing ⚠️
... and 9 more
Additional details and impacted files
@@             Coverage Diff             @@
##             next    #4269       +/-   ##
===========================================
+ Coverage   17.66%   52.88%   +35.21%     
===========================================
  Files         432      434        +2     
  Lines      307454   308750     +1296     
===========================================
+ Hits        54321   163277   +108956     
+ Misses     253133   145473   -107660     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

chainstate.get_reward_addresses_in_cycle(burnchain, sortdb, cycle, block_id)?;

// TODO (pox-4-workstream): the pox-4 contract must be able to return signing keys
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue for this? Because this seems to be pretty high-priority!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of ongoing discussion of this in the open .signers PR, I'm not sure that it has its own issue yet, but its definitely top of mind there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened an issue: #4290

let reward_set =
StacksChainState::make_reward_set(threshold, registered_addrs, cur_epoch.epoch_id);
if reward_set.signers.is_none() {
error!("FATAL: PoX reward set did not specify signer set in Nakamoto");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this truly fatal? If so, then should it panic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its as fatal as non-participation, so it should be treated the same.

@@ -313,6 +316,8 @@ pub struct NakamotoBlockHeader {
pub miner_signature: MessageSignature,
/// Schnorr signature over the block header from the signer set active during the tenure.
pub signer_signature: ThresholdSignature,
/// A bitvec which represents the signers that participated in this block signature.
pub signer_bitvec: BitVec,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little skeptical that we should be using the generic BitVec here. Yes, we need a bitmap of some kind, but I think we also need a hard bound on how big this is allowed to be (e.g. there can be no more than 4000 bits). And, I think that bound needs to be expressed in the data type itself, so it's impossible to represent a block header that would exceed this bound (and thus it's impossible to both serialize and deserialize such a block).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a generic const to the BitVec struct for MAX_SIZE. The field in the block header sets this to 4000.

let signing_key = if let Some(PrincipalData::Standard(s)) = entry.signing_key.clone() {
StacksAddress::from(s)
} else {
// TODO: should figure out if in mainnet?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue this is purely a code-aesthetic choice, really. This branch is only reached if the signing_principal set in the pox-4 contract is a contract principal, and the important thing is just that the address is unusable, which the burn address will be regardless of version bit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually moot now that we know from @jferrant that we need the keys themselves.

Copy link
Member

@jcnelson jcnelson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is on the right track. Just a few comments.

MarvinJanssen added a commit that referenced this pull request Jan 23, 2024
serialize_with = "addr_serialize",
deserialize_with = "addr_deserialize"
)]
pub signing_address: StacksAddress,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per @jferrant, it turns out that this actually needs to be a public key -- DKG needs the public keys before it can begin.

Copy link
Member

@jcnelson jcnelson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My subsequent review is blocked on the need for the signer set to track public keys instead of principals.

@kantai
Copy link
Member Author

kantai commented Jan 25, 2024

Okay -- this PR has now been updated with the work from #4295: the reward set is written to .signers and the reward set entries contain the signer keys.

The signer keys are stored in the reward set (not the stacking-state map) as (buff 33)s, and checked for length. The signer keys are sent over the event dispatcher interface, in the consensus order used for the bit vec.

These signer keys have a many-to-many relationship with PoX addresses. Each reward set entry points to one signing key, but a PoX address can appear multiple times in the reward set entries, and different entries can point to the same signing key.

The make_signer_set sums over the signing keys, and this PR includes unit tests for its behavior.

@@ -2408,6 +2409,10 @@ impl EventKeyType {
return Some(EventKeyType::BlockProposal);
}

if raw_key == "stacker_set" {
Copy link
Collaborator

@jferrant jferrant Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we have the means to subscribe to stacker sets, but this doesn't help a signer that spins up after stackers have registered. Orthogonal to this PR...Should I create a ticket to add this as an rpc endpoint as we will need to configure the signers with it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please open a ticket -- it should be simple enough to add in a follow-up PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done #4298

@jferrant
Copy link
Collaborator

Aside from the failed build due to a missing bitvec in the one file, LGTM

@jferrant jferrant self-requested a review January 25, 2024 22:59
@@ -103,6 +105,15 @@ pub struct MinedNakamotoBlockEvent {
pub tx_events: Vec<TransactionEvent>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PoxAnchorBlockEvent {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kantai did you intend to include this in the event payload? It looks to be unused right now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah -- no, at one point I was going to, but it would have required some cloning of sizable structs (the reward set). Removed in 99730e0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That data looked really helpful though! Right now the stacker list data in the event is just this this right?

pub struct RewardSet {
    pub rewarded_addresses: Vec<PoxAddress>,
    pub start_cycle_state: PoxStartCycleInfo,
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll also contain:

   // only generated for nakamoto reward sets
   pub signers: Option<Vec<NakamotoSignerEntry>>,

Which will contain signing keys, stacked amount for each signing key, and the number of signing slots.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants