github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/warp/signature.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package warp 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "math/big" 11 12 "github.com/ava-labs/avalanchego/snow/validators" 13 "github.com/ava-labs/avalanchego/utils/crypto/bls" 14 "github.com/ava-labs/avalanchego/utils/set" 15 ) 16 17 var ( 18 _ Signature = (*BitSetSignature)(nil) 19 20 ErrInvalidBitSet = errors.New("bitset is invalid") 21 ErrInsufficientWeight = errors.New("signature weight is insufficient") 22 ErrInvalidSignature = errors.New("signature is invalid") 23 ErrParseSignature = errors.New("failed to parse signature") 24 ) 25 26 type Signature interface { 27 fmt.Stringer 28 29 // NumSigners is the number of [bls.PublicKeys] that participated in the 30 // [Signature]. This is exposed because users of these signatures typically 31 // impose a verification fee that is a function of the number of 32 // signers. 33 NumSigners() (int, error) 34 35 // Verify that this signature was signed by at least [quorumNum]/[quorumDen] 36 // of the validators of [msg.SourceChainID] at [pChainHeight]. 37 // 38 // Invariant: [msg] is correctly initialized. 39 Verify( 40 ctx context.Context, 41 msg *UnsignedMessage, 42 networkID uint32, 43 pChainState validators.State, 44 pChainHeight uint64, 45 quorumNum uint64, 46 quorumDen uint64, 47 ) error 48 } 49 50 type BitSetSignature struct { 51 // Signers is a big-endian byte slice encoding which validators signed this 52 // message. 53 Signers []byte `serialize:"true"` 54 Signature [bls.SignatureLen]byte `serialize:"true"` 55 } 56 57 func (s *BitSetSignature) NumSigners() (int, error) { 58 // Parse signer bit vector 59 // 60 // We assert that the length of [signerIndices.Bytes()] is equal 61 // to [len(s.Signers)] to ensure that [s.Signers] does not have 62 // any unnecessary zero-padding to represent the [set.Bits]. 63 signerIndices := set.BitsFromBytes(s.Signers) 64 if len(signerIndices.Bytes()) != len(s.Signers) { 65 return 0, ErrInvalidBitSet 66 } 67 return signerIndices.Len(), nil 68 } 69 70 func (s *BitSetSignature) Verify( 71 ctx context.Context, 72 msg *UnsignedMessage, 73 networkID uint32, 74 pChainState validators.State, 75 pChainHeight uint64, 76 quorumNum uint64, 77 quorumDen uint64, 78 ) error { 79 if msg.NetworkID != networkID { 80 return ErrWrongNetworkID 81 } 82 83 subnetID, err := pChainState.GetSubnetID(ctx, msg.SourceChainID) 84 if err != nil { 85 return err 86 } 87 88 vdrs, totalWeight, err := GetCanonicalValidatorSet(ctx, pChainState, pChainHeight, subnetID) 89 if err != nil { 90 return err 91 } 92 93 // Parse signer bit vector 94 // 95 // We assert that the length of [signerIndices.Bytes()] is equal 96 // to [len(s.Signers)] to ensure that [s.Signers] does not have 97 // any unnecessary zero-padding to represent the [set.Bits]. 98 signerIndices := set.BitsFromBytes(s.Signers) 99 if len(signerIndices.Bytes()) != len(s.Signers) { 100 return ErrInvalidBitSet 101 } 102 103 // Get the validators that (allegedly) signed the message. 104 signers, err := FilterValidators(signerIndices, vdrs) 105 if err != nil { 106 return err 107 } 108 109 // Because [signers] is a subset of [vdrs], this can never error. 110 sigWeight, _ := SumWeight(signers) 111 112 // Make sure the signature's weight is sufficient. 113 err = VerifyWeight( 114 sigWeight, 115 totalWeight, 116 quorumNum, 117 quorumDen, 118 ) 119 if err != nil { 120 return err 121 } 122 123 // Parse the aggregate signature 124 aggSig, err := bls.SignatureFromBytes(s.Signature[:]) 125 if err != nil { 126 return fmt.Errorf("%w: %w", ErrParseSignature, err) 127 } 128 129 // Create the aggregate public key 130 aggPubKey, err := AggregatePublicKeys(signers) 131 if err != nil { 132 return err 133 } 134 135 // Verify the signature 136 unsignedBytes := msg.Bytes() 137 if !bls.Verify(aggPubKey, aggSig, unsignedBytes) { 138 return ErrInvalidSignature 139 } 140 return nil 141 } 142 143 func (s *BitSetSignature) String() string { 144 return fmt.Sprintf("BitSetSignature(Signers = %x, Signature = %x)", s.Signers, s.Signature) 145 } 146 147 // VerifyWeight returns [nil] if [sigWeight] is at least [quorumNum]/[quorumDen] 148 // of [totalWeight]. 149 // If [sigWeight >= totalWeight * quorumNum / quorumDen] then return [nil] 150 func VerifyWeight( 151 sigWeight uint64, 152 totalWeight uint64, 153 quorumNum uint64, 154 quorumDen uint64, 155 ) error { 156 // Verifies that quorumNum * totalWeight <= quorumDen * sigWeight 157 scaledTotalWeight := new(big.Int).SetUint64(totalWeight) 158 scaledTotalWeight.Mul(scaledTotalWeight, new(big.Int).SetUint64(quorumNum)) 159 scaledSigWeight := new(big.Int).SetUint64(sigWeight) 160 scaledSigWeight.Mul(scaledSigWeight, new(big.Int).SetUint64(quorumDen)) 161 if scaledTotalWeight.Cmp(scaledSigWeight) == 1 { 162 return fmt.Errorf( 163 "%w: %d*%d > %d*%d", 164 ErrInsufficientWeight, 165 quorumNum, 166 totalWeight, 167 quorumDen, 168 sigWeight, 169 ) 170 } 171 return nil 172 }