github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/verification/combined_verifier_v2.go (about) 1 //go:build relic 2 // +build relic 3 4 package verification 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/koko1123/flow-go-1/consensus/hotstuff" 11 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 12 "github.com/koko1123/flow-go-1/model/flow" 13 "github.com/koko1123/flow-go-1/module/signature" 14 "github.com/koko1123/flow-go-1/state/protocol" 15 "github.com/onflow/flow-go/crypto" 16 "github.com/onflow/flow-go/crypto/hash" 17 ) 18 19 // CombinedVerifier is a verifier capable of verifying two signatures, one for each 20 // scheme. The first type is a signature from a staking signer, 21 // which verifies either a single or an aggregated signature. The second type is 22 // a signature from a random beacon signer, which verifies either the signature share or 23 // the reconstructed threshold signature. 24 type CombinedVerifier struct { 25 committee hotstuff.Committee 26 stakingHasher hash.Hasher 27 beaconHasher hash.Hasher 28 packer hotstuff.Packer 29 } 30 31 var _ hotstuff.Verifier = (*CombinedVerifier)(nil) 32 33 // NewCombinedVerifier creates a new combined verifier with the given dependencies. 34 // - the hotstuff committee's state is used to retrieve the public keys for the staking signature; 35 // - the merger is used to combine and split staking and random beacon signatures; 36 // - the packer is used to unpack QC for verification; 37 func NewCombinedVerifier(committee hotstuff.Committee, packer hotstuff.Packer) *CombinedVerifier { 38 return &CombinedVerifier{ 39 committee: committee, 40 stakingHasher: signature.NewBLSHasher(signature.ConsensusVoteTag), 41 beaconHasher: signature.NewBLSHasher(signature.RandomBeaconTag), 42 packer: packer, 43 } 44 } 45 46 // VerifyVote verifies the validity of a combined signature from a vote. 47 // Usually this method is only used to verify the proposer's vote, which is 48 // the vote included in a block proposal. 49 // - model.InvalidFormatError if the signature has an incompatible format. 50 // - model.ErrInvalidSignature is the signature is invalid 51 // - model.InvalidSignerError if signer is _not_ part of the random beacon committee 52 // - unexpected errors should be treated as symptoms of bugs or uncovered 53 // edge cases in the logic (i.e. as fatal) 54 func (c *CombinedVerifier) VerifyVote(signer *flow.Identity, sigData []byte, block *model.Block) error { 55 56 // create the to-be-signed message 57 msg := MakeVoteMessage(block.View, block.BlockID) 58 59 // split the two signatures from the vote 60 stakingSig, beaconShare, err := signature.DecodeDoubleSig(sigData) 61 if err != nil { 62 if errors.Is(err, signature.ErrInvalidSignatureFormat) { 63 return model.NewInvalidFormatErrorf("could not split signature for block %v: %w", block.BlockID, err) 64 } 65 return fmt.Errorf("unexpected internal error while splitting signature for block %v: %w", block.BlockID, err) 66 } 67 68 dkg, err := c.committee.DKG(block.BlockID) 69 if err != nil { 70 return fmt.Errorf("could not get dkg: %w", err) 71 } 72 73 // verify each signature against the message 74 // TODO: check if using batch verification is faster (should be yes) 75 stakingValid, err := signer.StakingPubKey.Verify(stakingSig, msg, c.stakingHasher) 76 if err != nil { 77 return fmt.Errorf("internal error while verifying staking signature of node %x at block %v: %w", 78 signer.NodeID, block.BlockID, err) 79 } 80 if !stakingValid { 81 return fmt.Errorf("invalid staking sig for block %v: %w", block.BlockID, model.ErrInvalidSignature) 82 } 83 84 // there is no beacon share, no need to verify it 85 if beaconShare == nil { 86 return nil 87 } 88 89 // if there is beacon share, there should be beacon public key 90 beaconPubKey, err := dkg.KeyShare(signer.NodeID) 91 if err != nil { 92 if protocol.IsIdentityNotFound(err) { 93 return model.NewInvalidSignerErrorf("%v is not a random beacon participant: %w", signer.NodeID, err) 94 } 95 return fmt.Errorf("unexpected error retrieving random beacon key share for node %x at block %v: %w", 96 signer.NodeID, block.BlockID, err) 97 } 98 99 beaconValid, err := beaconPubKey.Verify(beaconShare, msg, c.beaconHasher) 100 if err != nil { 101 return fmt.Errorf("internal error while verifying beacon signature at block %v: %w", 102 block.BlockID, err) 103 } 104 if !beaconValid { 105 return fmt.Errorf("invalid beacon sig for block %v: %w", block.BlockID, model.ErrInvalidSignature) 106 } 107 return nil 108 } 109 110 // VerifyQC checks the cryptographic validity of the QC's `sigData` for the 111 // given block. It is the responsibility of the calling code to ensure 112 // that all `signers` are authorized, without duplicates. Return values: 113 // - nil if `sigData` is cryptographically valid 114 // - model.InsufficientSignaturesError if `signers` is empty. 115 // Depending on the order of checks in the higher-level logic this error might 116 // be an indicator of a external byzantine input or an internal bug. 117 // - model.InvalidFormatError if `sigData` has an incompatible format 118 // - model.ErrInvalidSignature if a signature is invalid 119 // - error if running into any unexpected exception (i.e. fatal error) 120 func (c *CombinedVerifier) VerifyQC(signers flow.IdentityList, sigData []byte, block *model.Block) error { 121 if len(signers) == 0 { 122 return model.NewInsufficientSignaturesErrorf("empty list of signers") 123 } 124 dkg, err := c.committee.DKG(block.BlockID) 125 if err != nil { 126 return fmt.Errorf("could not get dkg data: %w", err) 127 } 128 129 // unpack sig data using packer 130 blockSigData, err := c.packer.Unpack(signers, sigData) 131 if err != nil { 132 return fmt.Errorf("could not split signature: %w", err) 133 } 134 135 msg := MakeVoteMessage(block.View, block.BlockID) 136 137 // verify the beacon signature first since it is faster to verify (no public key aggregation needed) 138 beaconValid, err := dkg.GroupKey().Verify(blockSigData.ReconstructedRandomBeaconSig, msg, c.beaconHasher) 139 if err != nil { 140 return fmt.Errorf("internal error while verifying beacon signature: %w", err) 141 } 142 if !beaconValid { 143 return fmt.Errorf("invalid reconstructed random beacon sig for block (%x): %w", block.BlockID, model.ErrInvalidSignature) 144 } 145 146 // aggregate public staking keys of all signers (more costly) 147 // TODO: update to use module/signature.PublicKeyAggregator 148 aggregatedKey, err := crypto.AggregateBLSPublicKeys(signers.PublicStakingKeys()) // caution: requires non-empty slice of keys! 149 if err != nil { 150 // `AggregateBLSPublicKeys` returns a `crypto.invalidInputsError` in two distinct cases: 151 // (i) In case no keys are provided, i.e. `len(signers) == 0`. 152 // This scenario _is expected_ during normal operations, because a byzantine 153 // proposer might construct an (invalid) QC with an empty list of signers. 154 // (ii) In case some provided public keys type is not BLS. 155 // This scenario is _not expected_ during normal operations, because all keys are 156 // guaranteed by the protocol to be BLS keys. 157 // 158 // By checking `len(signers) == 0` upfront , we can rule out case (i) as a source of error. 159 // Hence, if we encounter an error here, we know it is case (ii). Thereby, we can clearly 160 // distinguish a faulty _external_ input from an _internal_ uncovered edge-case. 161 return fmt.Errorf("could not compute aggregated key for block %x: %w", block.BlockID, err) 162 } 163 164 // verify aggregated signature with aggregated keys from last step 165 stakingValid, err := aggregatedKey.Verify(blockSigData.AggregatedStakingSig, msg, c.stakingHasher) 166 if err != nil { 167 return fmt.Errorf("internal error while verifying staking signature for block %x: %w", block.BlockID, err) 168 } 169 if !stakingValid { 170 return fmt.Errorf("invalid aggregated staking sig for block %v: %w", block.BlockID, model.ErrInvalidSignature) 171 } 172 173 return nil 174 }