-
Notifications
You must be signed in to change notification settings - Fork 298
SIMD-0387: BLS Pubkey Management in Vote Account. #387
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
Changes from all commits
6f8f199
29a6d38
9c9ead5
229b601
eed2c38
e8e24d3
39033d5
d0dde55
5de2954
a8e44f9
b71247b
3197a17
629daad
561edb2
34a576b
109feee
4129dd0
2166bb6
c6c617c
d61ea12
d6ffd1d
fa25fbb
6c46ae4
e7f6719
89d114e
b5330ae
aee4d34
c423fa8
03cff65
eb96fd6
8b00dae
01e8865
a6a8ef0
02cc97c
c3813cb
9382ffa
e81fa9e
5296d19
cba1c98
3ec7407
b88c205
3a55e37
20b5657
e1f1a07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,251 @@ | ||
| --- | ||
| simd: '0387' | ||
| title: BLS Pubkey management in vote account | ||
| authors: | ||
| - Sam Kim (Anza) | ||
| - Quentin Kniep (Anza) | ||
| - Wen Xu (Anza) | ||
| category: Standard | ||
| type: Core | ||
| status: Review | ||
| created: 2025-10-27 | ||
| feature: (fill in with feature key and github tracking issues once accepted) | ||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| This proposal specifies in detail how a BLS public key can be generated by | ||
| users via updated existing tools and how they can put the generated BLS public | ||
| keys into their vote accounts for voting in Alpenglow. | ||
|
|
||
| It also describes in detail the data structure changes needed. | ||
|
|
||
| ## Motivation | ||
|
|
||
| The Alpenglow SIMD (326) described the new consensus protocol which will be | ||
| launched on Solana. The protocol requires efficient and safe aggregation of | ||
| validator votes to succinctly prove certain state transitions can safely happen | ||
| (for example, 60% of the validators voted to skip a slot). The ed25519 | ||
| signatures we currently use are not the best fit for this purpose, so instead | ||
| we will be using the Boneh–Lynn–Shacham (BLS) aggregate signature scheme to | ||
| sign Alpenglow votes. | ||
|
|
||
| However, the BLS public key is entirely different from an ed25519 public key | ||
| (as BLS operates over a different elliptic curve), so we can’t naively reuse | ||
| the current ed25519 public keys in vote accounts either. Each validator must | ||
| add a BLS public key into their vote account before the network enables Alpenglow | ||
| in order to vote. | ||
|
|
||
| ## New Terminology | ||
|
|
||
| N/A | ||
|
|
||
| ## Dependencies | ||
|
|
||
| - Alpenglow is specified in [SIMD 326](https://github.com/solana-foundation/solana-improvement-documents/pull/326) | ||
|
|
||
| - VoteStateV4 is specified in [SIMD 185](https://github.com/solana-foundation/solana-improvement-documents/pull/185) | ||
| and it adds an optional BLS public key field | ||
|
|
||
| - Requiring BLS public key for Alpenglow is specified in [SIMD 357](https://github.com/solana-foundation/solana-improvement-documents/pull/357) | ||
|
|
||
| ## Detailed Design | ||
|
|
||
| BLS keypairs can be generated randomly like ed25519 keypairs. But to save the | ||
| users some trouble on keypair management, the current plan is to initially | ||
| derive their BLS keypair used in Alpenglow votes based on their ed25519 vote | ||
| keypair. In other words, with an existing ed25519 vote keypair, the operators | ||
| can safely regenerate the associated BLS keypair on demand. Also during | ||
| validator operations, the users still only need to supply the vote keypair as | ||
| before. | ||
|
|
||
| The association of BLS keypair with vote authority ed25519 keypair is the | ||
| default client behavior to simplify Alpenglow launch. After Alpenglow launches | ||
| we may get rid of ed25519 vote keypair and allow users to randomly generate BLS | ||
| keypairs. | ||
|
|
||
| When users create vote accounts, they must register their BLS public key by | ||
| storing it in the newly created vote account. When they modify their vote | ||
| authority, they must re-register the new corresponding BLS key. | ||
|
|
||
| ### Changes to vote program | ||
|
|
||
| Whenever a new BLS public key is being updated in the vote account, we need | ||
| to perform BLS verification on its validity, see "Security Considerations" | ||
| for details. We plan to implement this by calling BLS library from the vote | ||
| program, see "Alternatives Considered" for comparison with other solutions. | ||
|
|
||
| Since BLS verification is expensive (around 1.15ms), each verification will cost | ||
| 34,500 CUs. Any Vote program instruction that performs a BLS verification will | ||
| therefore add 34,500 CUs per verification on top of its baseline cost. As a result, | ||
| Vote program instructions - which currently all cost 2,100 CUs - will have | ||
| differentiated CU costs depending on whether they include BLS verification. The | ||
| updated CU values are detailed in later sections. | ||
|
|
||
| Note the 34,500 CUs for BLS verification will be consumed immediately before | ||
| the verification is performed. | ||
|
|
||
| Currently the vote program is allocated a budget of 3,000 CUs in the validator's | ||
| builtin program cost modeling mechanism. Simple vote transactions (containing a | ||
| `Vote` instruction) already bypass this mechanism, and other Vote program | ||
| instructions that may use BLS verification are fairly infrequent. As a result, | ||
| the Vote program will be removed from builtin program cost modeling. | ||
|
|
||
| #### Disallow change of vote authority by old instructions | ||
|
|
||
| After the feature gate associated with this SIMD is activated, the previous | ||
| instructions will be disallowed to change vote authority after off-chain tools | ||
| are upgraded, they will result in transaction errors. These include: | ||
|
|
||
| - Authorize(Pubkey, VoteAuthorize): when VoteAuthorize is VoteAuthorize::Voter | ||
| and the account has BLS public key it will fail | ||
|
|
||
| - AuthorizeWithSeed(VoteAuthorizeWithSeedArgs): when authorization_type is | ||
| VoteAuthorize::Voter and the account has BLS public key it will fail | ||
|
|
||
| - AuthorizeChecked(VoteAuthorize): when VoteAuthorize is VoteAuthorize::Voter | ||
| and the account has BLS public key it will fail | ||
|
|
||
| - AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs): when | ||
| authorization_type is VoteAuthorize::Voter and the account has BLS public key | ||
| it will fail | ||
|
|
||
| #### Proof of Possession calculation and verification | ||
|
|
||
| While a standard Proof of Possession (PoP) is simply a signature over the | ||
| public key itself (`σ=Signsk(pk)`), this can leave room for "binding theft" | ||
| where a valid PoP is intercepted and registered to an attacker's vote account. | ||
| To prevent this and cross-chain replay, the PoP must sign a domain-separated | ||
| message binding the key to its specific context. | ||
|
|
||
| The PoP calculation and verification must use the following message structure: | ||
|
|
||
| ```rust | ||
| message=label ∣∣ chain_id ∣∣ authorized_voter_pubkey ∣∣ bls_pubkey_bytes | ||
| ``` | ||
|
|
||
| Where: (|| above is concatenation) | ||
|
|
||
| - `label` is a constant string, we will make it "ALPENGLOW" here (all upper | ||
| case). | ||
|
|
||
| - `chain_id` is the genesis hash of the chain. | ||
|
|
||
| - `authorized_voter_pubkey` is the authorized_voter Ed25519 public key. | ||
|
|
||
| - `bls_pubkey_bytes` is the compressed new BLS public key (48 bytes). | ||
|
|
||
| See "Security Considerations" for why the fields are needed. | ||
|
|
||
| During PoP calculation, the CLI will generate the BLS keypair, then use the BLS | ||
| private key to sign this message to generate the signature, compress it, and | ||
| save it in `authorized_voter_bls_proof_of_possession`. | ||
|
|
||
| During PoP verification, the validators will construct the same message, then | ||
| check that the `authorized_voter_bls_proof_of_possession` is the correct | ||
| signature. | ||
|
|
||
| #### Add InitializeAccountV2 | ||
|
|
||
| ```rust | ||
| InitializeAccountV2(VoteInitV2), | ||
| ``` | ||
|
|
||
| ```rust | ||
| pub const BLS_PUBLIC_KEY_COMPRESSED_SIZE: usize = 48; | ||
| pub const BLS_SIGNATURE_COMPRESSED_SIZE: usize = 96; | ||
|
|
||
| pub struct VoteInitV2 { | ||
| pub node_pubkey: Pubkey, | ||
| pub authorized_voter: Pubkey, | ||
| pub authorized_voter_bls_pubkey: [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE], | ||
| pub authorized_voter_bls_proof_of_possession: [u8; BLS_SIGNATURE_COMPRESSED_SIZE], | ||
| pub authorized_withdrawer: Pubkey, | ||
| pub inflation_rewards_commission_bps: u16, | ||
| pub inflation_rewards_collector: Pubkey, | ||
| pub block_revenue_commission_bps: u16, | ||
| pub block_revenue_collector: Pubkey, | ||
| } | ||
| ``` | ||
|
|
||
| Upon receiving the transaction, the vote program will perform a BLS | ||
| verification on submitted BLS public key and associated proof of possession. | ||
| The transaction will fail if the verification failed. Otherwise the new vote | ||
| account is created with given parameters. | ||
|
|
||
| #### Add new variant of VoteAuthorize | ||
|
|
||
| ```rust | ||
| pub struct VoterWithBLSArgs { | ||
| bls_pub_key: [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE], | ||
| bls_proof_of_possession: [u8; BLS_SIGNATURE_COMPRESSED_SIZE], | ||
| } | ||
|
|
||
| pub enum VoteAuthorize { | ||
| Voter, | ||
| Withdrawer, | ||
| VoterWithBLS(VoterWithBLSArgs), | ||
| } | ||
| ``` | ||
|
|
||
| Upon receiving the transaction, if the parameter is of the new variant, the | ||
| vote program will perform a BLS verification on submitted BLS public key and | ||
| associated proof of possession. The transaction will fail if the verification | ||
| failed. Otherwise the vote authority change will be recorded in vote account. | ||
|
|
||
| ## Impact | ||
|
|
||
| ### Before feature gate in this SIMD is activated | ||
|
|
||
| There is no change, users cannot update their BLS public key in vote account. | ||
|
|
||
| ### After the feature gate in this SIMD is activated but before Alpenglow launch | ||
|
|
||
| Users can update their BLS public key in the vote account. | ||
|
|
||
| ### After Alpenglow launch | ||
|
|
||
| Per SIMD 357, only vote accounts with updated BLS public key can participate | ||
| in the voting process. | ||
|
|
||
| When starting a validator, the operators are supposed to provide all ed25519 | ||
| keypairs like before. The BLS keypair will automatically be derived from the | ||
| vote authority keypair (if that’s missing, then the identity keypair is used | ||
| like now). The operations needed to switch the keypair and the operations | ||
| needed to switch to a standby node are the same as today. | ||
|
bw-solana marked this conversation as resolved.
|
||
|
|
||
| ## Security Considerations | ||
|
|
||
| The safety of BLS votes in Alpenglow is still guarded by the ed25519 vote | ||
| authority keypair, so users are supposed to safe guard it like before. | ||
|
|
||
| We need to have the proof of possession in the instruction inputs so we can | ||
| guard against BLS rogue-key attack. If anyone is allowed to randomly choose a | ||
| public key, then an attacker can select a particular public key which interacts | ||
| with other participants' keys so a forged aggregate signature verifies even | ||
| though not all honest parties actually signed. | ||
|
|
||
| We need to put `authorized_voter_pubkey` in Proof of Possession calculation | ||
| because a replay attack exists: | ||
|
|
||
| - User A wants to update vote authority, calculates PoP signature | ||
|
|
||
| - The attacker sees this PoP signature in the transaction and sends in another | ||
| transaction grabbing the BLS public key before user A | ||
|
|
||
| - Now user A cannot use the BLS public key generated for his own vote account | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we say that we check whether the public key is already in use? I don't see it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't enforce it anywhere, but we can emit a warning in CLI if the public key is already in use. The reasons being:
|
||
|
|
||
| We also add a `label` so in the future we can update the version, and add a | ||
| `chain_id` so attackers can't do cross-chain replay attack. | ||
|
|
||
| ## Alternatives Considered | ||
|
|
||
| ### Moving BLS verification to a syscall and/or a different program | ||
|
|
||
| We can put BLS verification into a separate program or into a syscall, this is | ||
| conceptually cleaner. | ||
|
|
||
| We choose not to do this now because currently vote program needs to handle | ||
| a lot of vote transactions so it's a native program. We may explore this | ||
| option later if the vote program is migrated to an on-chain BPF program after | ||
| Alpenglow launches. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should mention a critical piece of the proposal here, which is how the network verifies PoP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proof of Possession Calculation actually is the verification method as well, reworked that section.