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  }