diff --git a/proposals/0387-bls-pubkey-management-in-vote-account.md b/proposals/0387-bls-pubkey-management-in-vote-account.md new file mode 100644 index 000000000..4c44cb99f --- /dev/null +++ b/proposals/0387-bls-pubkey-management-in-vote-account.md @@ -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. + +## 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 + +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. \ No newline at end of file