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

modify nakamoto block header to use Vec<MessageSignature> #4781

Open
wants to merge 45 commits into
base: develop
Choose a base branch
from

Conversation

hstove
Copy link
Contributor

@hstove hstove commented May 13, 2024

This builds on top of #4778, to try and reduce duplicate work and conflicts.

The verify_signer_signatures function has a few rules:

  • The Vec<Signature> can be smaller than Vec<Signer>, but it's still order dependent.
    • What this means is that if you have signers A,B,C, you can have signatures sA,sC, but not sC,sA.
  • This depends on the signers being unique by public key (which I think is true, but if not it requires refactoring)
  • I'm summing the total weight of the RewardSet and using 70% as the threshold
  • No duplicates, no signatures from non-signers, etc

Tracking the work included in this PR:

  • Updates the signer_signature field in NakamotoBlockHeader to be Vec<MessageSignature>
  • Adds a new verify_signer_signatures method to NakamotoBlockHeader with tests
  • Verify the block header in accept_block
  • Update TestSigners and TestSigningChannel to sign the block using the new format
  • Update blind_signer related code for Nakamoto integrations

What I think (?) is out of scope:

@hstove hstove requested a review from kantai May 13, 2024 03:04
@hstove hstove force-pushed the feat/header-signer-signatures branch from 6abb574 to 8c92e2d Compare May 13, 2024 22:33
@jcnelson
Copy link
Member

Hi @hstove, I suspect that you have discovered that in order to ship this PR, a lot of unit test infrastructure needs to be changed as well (see #4796). I wonder if it makes sense to create a feature branch in the Stacks blockchain for switching over from WSTS to multisig?

@hstove hstove changed the base branch from chore/signer-traits to feat/add-v0-signer May 15, 2024 20:10
@hstove hstove force-pushed the feat/header-signer-signatures branch from fe3ca6f to d14e5b4 Compare May 15, 2024 20:11
@hstove
Copy link
Contributor Author

hstove commented May 15, 2024

@jcnelson I think that's possibly the right idea, but I'm finding it to be actually pretty reasonable to swap the testing infra without really messing it up for when we go back to threshold sigs. There are a few places (ie TestSigners, SignCoordinator) where I'm keeping essentially two implementations, and only one of them is actively used.

I have a number of TODO comments in this PR, which are mainly related to where you'd need to do epoch-gated behavior. I'd love to see what you think about how to proceed given my first attempt at this.

@hstove
Copy link
Contributor Author

hstove commented May 15, 2024

@jferrant @kantai @obycode tagging you in as I think this PR is in a good place to get a review. I'll want to see a full CI run to see exactly what's still TBD, but a lot of the initial pieces are in place.

I've also rebased this on top of #4788, as that includes some type changes that are utilized.

@saralab saralab marked this pull request as ready for review May 16, 2024 13:51
Copy link
Member

@kantai kantai left a comment

Choose a reason for hiding this comment

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

This LGTM -- just a few comments. Exciting stuff!

stackslib/src/chainstate/nakamoto/mod.rs Outdated Show resolved Hide resolved
stackslib/src/chainstate/nakamoto/mod.rs Outdated Show resolved Hide resolved
stackslib/src/chainstate/nakamoto/mod.rs Outdated Show resolved Hide resolved
stackslib/src/chainstate/nakamoto/mod.rs Outdated Show resolved Hide resolved
stackslib/src/chainstate/nakamoto/mod.rs Outdated Show resolved Hide resolved
stackslib/src/chainstate/nakamoto/mod.rs Outdated Show resolved Hide resolved
testnet/stacks-node/src/nakamoto_node/sign_coordinator.rs Outdated Show resolved Hide resolved
@@ -1696,7 +1784,8 @@ impl NakamotoChainState {
db_handle: &mut SortitionHandleConn,
staging_db_tx: &NakamotoStagingBlocksTx,
headers_conn: &Connection,
aggregate_public_key: &Point,
_aggregate_public_key: &Point,
Copy link
Member

Choose a reason for hiding this comment

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

You can remove this argument, and just note in the comment below that it will be required in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made this an Option (still unused), as I'm not sure whether this should or shouldn't be a place where we want to refactor later (given how it's used downstream).

);
block.header.signer_signature = ThresholdSignature(signature);
/// Reorder a list of signatures to match the order of the reward set.
pub fn reorder_signatures(
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 rather confused as to why this method and its caller (sign_block_with_reward_set) need to exist? If a block is signed by iterating through the reward set's private keys, then it would always be the case that the signatures are in order. No one should be trying to check a block's signatures against a different reward set than the one that signed it, so sign_block_with_reward_set as written seems buggy. If the test wants to sign a block with a different reward set than the one configured in this TestSigners instance, then perhaps the test should create a TestSigners for that reward set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I understand the confusion. For one, this function isn't signing the block using the reward set's keys (the RewardSet also doesn't have private keys), it's signing the block using TestSigners's keys and using the reward set to separately:

  1. (most importantly) not sign the block with any TestSigner keys that aren't in the reward set
  2. reorder signatures to match the reward set

For a use case, imagine a testing scenario like this:

  • Set up your test with TestSigners(alice, bob)
  • Next, stack with just bob
  • Sign some blocks
  • Later stack with alice
  • Sign some blocks

In that cash, TestSigners would appropriately only sign blocks using the correct signers at that time. You could do this by manually changing test_signers.signer_keys, too, but TestSigners is generally just used for one-time test setup and then passed around.

}

// Sort [`Self::signer_keys`] by their compressed public key
pub fn sorted_signer_keys(&self) -> Vec<Secp256k1PrivateKey> {
Copy link
Member

Choose a reason for hiding this comment

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

Why does this method need to exist? I thought blocks were signed by their respective signers in reward-set order?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You could do reward-set order (for each in reward set, lookup if test signer has that key, sign), but TestSigners is kind of doing the opposite (for each key in signer_keys, sign, then reorder/remove based on reward set). I can see how that's messy though, I'll do it the other way. Also, this specific function isn't even used. I probably used it at some point but I'll remove it

Copy link
Contributor Author

@hstove hstove May 16, 2024

Choose a reason for hiding this comment

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

I removed this and refactored the other methods to do the (for each key in reward_set, sign if TestSigner has that key) approach. LMK if you think this needs more work

@jcnelson jcnelson changed the base branch from feat/add-v0-signer to develop May 23, 2024 19:00
true,
self.burnchain.pox_constants.clone(),
)
.expect("FATAL: could not open sortition DB");
Copy link
Member

Choose a reason for hiding this comment

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

Please don't make these fatal. Just return an error; the miner will try again on its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code was moved up into run_miner in aa9baf9, which is in #4807

&new_block.header.consensus_hash,
)
.expect("FATAL: could not retrieve chain tip")
.expect("FATAL: could not retrieve chain tip");
Copy link
Member

Choose a reason for hiding this comment

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

Again, please don't make this fatal.

@jcnelson jcnelson requested a review from a team as a code owner May 28, 2024 18:28
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

4 participants