github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/signature/signer_indices.go (about)

     1  package signature
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/onflow/flow-go/ledger/common/bitutils"
     8  	"github.com/onflow/flow-go/model/flow"
     9  )
    10  
    11  // EncodeSignerToIndicesAndSigType encodes the given stakingSigners and beaconSigners into bit vectors for
    12  // signer indices and sig types.
    13  // PREREQUISITES:
    14  //   - The input `canonicalIdentifiers` must exhaustively list the set of authorized signers in their canonical order.
    15  //   - The inputs `stakingSigners` and `beaconSigners` are treated as sets, i.e. they
    16  //     should not contain any duplicates.
    17  //   - A node can be listed in either `stakingSigners` or `beaconSigners`. A node appearing in both lists
    18  //     constitutes an illegal input.
    19  //   - `stakingSigners` must be a subset of `canonicalIdentifiers`
    20  //   - `beaconSigners` must be a subset of `canonicalIdentifiers`
    21  //
    22  // RETURN VALUES:
    23  //
    24  //   - `signerIndices` is a bit vector. Let signerIndices[i] denote the ith bit of `signerIndices`.
    25  //
    26  //     .                            ┌ 1 if and only if canonicalIdentifiers[i] is in `stakingSigners` or `beaconSigners`
    27  //     .         signerIndices[i] = └ 0 otherwise
    28  //
    29  //     Let `n` be the length of `canonicalIdentifiers`. `signerIndices` contains at least `n` bits, though, we
    30  //     right-pad it with tailing zeros to full bytes.
    31  //
    32  //   - `sigTypes` is a bit vector. Let sigTypes[i] denote the ith bit of `sigTypes`
    33  //     .                       ┌ 1 if and only if the ith signer is in `beaconSigners`
    34  //     .         sigTypes[i] = └ 0 if and only if the ith signer is in `stakingSigners`
    35  //     (Per prerequisite, we require that no signer is listed in both `beaconSigners` and `stakingSigners`)
    36  //
    37  // Example:
    38  // As an example consider the case where we have a committee C of 10 nodes in canonical oder
    39  //
    40  //	C = [A,B,C,D,E,F,G,H,I,J]
    41  //
    42  // where nodes [B,F] are stakingSigners and beaconSigners are [C,E,G,I,J].
    43  //  1. First return parameter: `signerIndices`
    44  //     - We start with a bit vector v that has |C| number of bits
    45  //     - If a node contributed either as staking signer or beacon signer,
    46  //     we set the respective bit to 1:
    47  //     .         [A,B,C,D,E,F,G,H,I,J]
    48  //     .            ↓ ↓   ↓ ↓ ↓   ↓ ↓
    49  //     .          0,1,1,0,1,1,1,0,1,1
    50  //     - Lastly, right-pad the resulting bit vector with 0 to full bytes. We have 10 committee members,
    51  //     so we pad to 2 bytes:
    52  //     .          01101110 11000000
    53  //  2. second return parameter: `sigTypes`
    54  //     - Here, we restrict our focus on the signers, which we encoded in the previous step.
    55  //     In our example, nodes [B,C,E,F,G,I,J] signed in canonical order. This is exactly the same order,
    56  //     as we have represented the signer in the last step.
    57  //     - For these 5 nodes in their canonical order, we encode each node's signature type as
    58  //     .        bit-value 1: node was in beaconSigners
    59  //     .        bit-value 0: node was in stakingSigners
    60  //     This results in the bit vector
    61  //     .            [B,C,E,F,G,I,J]
    62  //     .             ↓ ↓ ↓ ↓ ↓ ↓ ↓
    63  //     .             0,1,0,1,1,1,1
    64  //     - Again, we right-pad with zeros to full bytes, As we only had 7 signers, the sigType slice is 1byte long
    65  //     .            01011110
    66  //
    67  // the signer indices is prefixed with a checksum of the canonicalIdentifiers, which can be used by the decoder
    68  // to verify if the decoder is using the same canonicalIdentifiers as the encoder to decode the signer indices.
    69  //
    70  // ERROR RETURNS
    71  // During normal operations, no error returns are expected. This is because encoding signer sets is generally
    72  // part of the node's internal work to generate messages. Hence, the inputs to this method come from other
    73  // trusted components within the node. Therefore, any illegal input is treated as a symptom of an internal bug.
    74  func EncodeSignerToIndicesAndSigType(
    75  	canonicalIdentifiers flow.IdentifierList,
    76  	stakingSigners flow.IdentifierList,
    77  	beaconSigners flow.IdentifierList,
    78  ) (signerIndices []byte, sigTypes []byte, err error) {
    79  	stakingSignersLookup := stakingSigners.Lookup()
    80  	if len(stakingSignersLookup) != len(stakingSigners) {
    81  		return nil, nil, fmt.Errorf("duplicated entries in staking signers %v", stakingSignersLookup)
    82  	}
    83  	beaconSignersLookup := beaconSigners.Lookup()
    84  	if len(beaconSignersLookup) != len(beaconSigners) {
    85  		return nil, nil, fmt.Errorf("duplicated entries in beacon signers %v", stakingSignersLookup)
    86  	}
    87  
    88  	// encode Identifiers to `signerIndices`; and for each signer, encode the signature type in `sigTypes`
    89  	signerIndices = bitutils.MakeBitVector(len(canonicalIdentifiers))
    90  	sigTypes = bitutils.MakeBitVector(len(stakingSigners) + len(beaconSigners))
    91  	signerCounter := 0
    92  	for canonicalIdx, member := range canonicalIdentifiers {
    93  		if _, ok := stakingSignersLookup[member]; ok {
    94  			bitutils.SetBit(signerIndices, canonicalIdx)
    95  			// The default value for sigTypes is bit zero, which corresponds to a staking sig.
    96  			// Hence, we don't have to change anything here.
    97  			delete(stakingSignersLookup, member)
    98  			signerCounter++
    99  			continue
   100  		}
   101  		if _, ok := beaconSignersLookup[member]; ok {
   102  			bitutils.SetBit(signerIndices, canonicalIdx)
   103  			bitutils.SetBit(sigTypes, signerCounter)
   104  			delete(beaconSignersLookup, member)
   105  			signerCounter++
   106  			continue
   107  		}
   108  	}
   109  
   110  	if len(stakingSignersLookup) > 0 {
   111  		return nil, nil, fmt.Errorf("unknown staking signers %v", stakingSignersLookup)
   112  	}
   113  	if len(beaconSignersLookup) > 0 {
   114  		return nil, nil, fmt.Errorf("unknown or duplicated beacon signers %v", beaconSignersLookup)
   115  	}
   116  
   117  	prefixed := PrefixCheckSum(canonicalIdentifiers, signerIndices)
   118  
   119  	return prefixed, sigTypes, nil
   120  }
   121  
   122  // DecodeSigTypeToStakingAndBeaconSigners decodes the bit-vector `sigType` to the set of
   123  // staking signer identities (`stakingSigners`) and the set of beacon signer identities (`beaconSigners`).
   124  // Prerequisite:
   125  //   - The input `signers` must be the set of signers in their canonical order.
   126  //
   127  // Expected Error returns during normal operations:
   128  //   - signature.IsInvalidSigTypesError if the given `sigType` does not encode a valid sequence of signature types
   129  func DecodeSigTypeToStakingAndBeaconSigners(
   130  	signers flow.IdentitySkeletonList,
   131  	sigType []byte,
   132  ) (flow.IdentitySkeletonList, flow.IdentitySkeletonList, error) {
   133  	numberSigners := len(signers)
   134  	if err := validPadding(sigType, numberSigners); err != nil {
   135  		if errors.Is(err, ErrIncompatibleBitVectorLength) || errors.Is(err, ErrIllegallyPaddedBitVector) {
   136  			return nil, nil, NewInvalidSigTypesErrorf("invalid padding of sigTypes: %w", err)
   137  		}
   138  		return nil, nil, fmt.Errorf("unexpected exception while checking padding of sigTypes: %w", err)
   139  	}
   140  
   141  	// decode bits to IdentitySkeletonList
   142  	stakingSigners := make(flow.IdentitySkeletonList, 0, numberSigners)
   143  	beaconSigners := make(flow.IdentitySkeletonList, 0, numberSigners)
   144  	for i, signer := range signers {
   145  		if bitutils.ReadBit(sigType, i) == 0 {
   146  			stakingSigners = append(stakingSigners, signer)
   147  		} else {
   148  			beaconSigners = append(beaconSigners, signer)
   149  		}
   150  	}
   151  	return stakingSigners, beaconSigners, nil
   152  }
   153  
   154  // EncodeSignersToIndices encodes the given signerIDs into compacted bit vector.
   155  // PREREQUISITES:
   156  //   - The input `canonicalIdentifiers` must exhaustively list the set of authorized signers in their canonical order.
   157  //   - The input `signerIDs` represents a set, i.e. it should not contain any duplicates.
   158  //   - `signerIDs` must be a subset of `canonicalIdentifiers`
   159  //   - `signerIDs` can be in arbitrary order (canonical order _not required_)
   160  //
   161  // RETURN VALUE:
   162  //   - `signerIndices` is a bit vector. Let signerIndices[i] denote the ith bit of `signerIndices`.
   163  //     .                             ┌ 1 if and only if canonicalIdentifiers[i] is in `signerIDs`
   164  //     .          signerIndices[i] = └ 0 otherwise
   165  //     Let `n` be the length of `canonicalIdentifiers`. `signerIndices` contains at least `n` bits, though, we
   166  //     right-pad it with tailing zeros to full bytes.
   167  //
   168  // Example:
   169  // As an example consider the case where we have a committee C of 10 nodes in canonical oder
   170  //
   171  //	C = [A,B,C,D,E,F,G,H,I,J]
   172  //
   173  // where nodes [B,F] are stakingSigners, and beaconSigners are [C,E,G,I,J].
   174  //  1. First return parameter: QC.signerIndices
   175  //     - We start with a bit vector v that has |C| number of bits
   176  //     - If a node contributed either as staking signer or beacon signer,
   177  //     we set the respective bit to 1:
   178  //     .          [A,B,C,D,E,F,G,H,I,J]
   179  //     .             ↓ ↓   ↓ ↓ ↓   ↓ ↓
   180  //     .           0,1,1,0,1,1,1,0,1,1
   181  //     - Lastly, right-pad the resulting bit vector with 0 to full bytes. We have 10 committee members,
   182  //     so we pad to 2 bytes:
   183  //     .           01101110 11000000
   184  //
   185  // ERROR RETURNS
   186  // During normal operations, no error returns are expected. This is because encoding signer sets is generally
   187  // part of the node's internal work to generate messages. Hence, the inputs to this method come from other
   188  // trusted components within the node. Therefore, any illegal input is treated as a symptom of an internal bug.
   189  // canonicalIdentifiers represents all identities who are eligible to sign the given resource. It excludes
   190  // identities who are ineligible to sign the given resource. For example, canonicalIdentifiers in the context
   191  // of a cluster consensus quorum certificate would include authorized members of the cluster and
   192  // exclude ejected members of the cluster, or unejected collection nodes from a different cluster.
   193  // the signer indices is prefixed with a checksum of the canonicalIdentifiers, which can be used by the decoder
   194  // to verify if the decoder is using the same canonicalIdentifiers as the encoder to decode the signer indices.
   195  func EncodeSignersToIndices(
   196  	canonicalIdentifiers flow.IdentifierList,
   197  	signerIDs flow.IdentifierList,
   198  ) (signerIndices []byte, err error) {
   199  	signersLookup := signerIDs.Lookup()
   200  	if len(signersLookup) != len(signerIDs) {
   201  		return nil, fmt.Errorf("duplicated entries in signerIDs %v", signerIDs)
   202  	}
   203  
   204  	// encode Identifiers to bits
   205  	signerIndices = bitutils.MakeBitVector(len(canonicalIdentifiers))
   206  	for canonicalIdx, member := range canonicalIdentifiers {
   207  		if _, ok := signersLookup[member]; ok {
   208  			bitutils.SetBit(signerIndices, canonicalIdx)
   209  			delete(signersLookup, member)
   210  		}
   211  	}
   212  	if len(signersLookup) > 0 {
   213  		return nil, fmt.Errorf("unknown signers IDs in the keys of %v", signersLookup)
   214  	}
   215  
   216  	prefixed := PrefixCheckSum(canonicalIdentifiers, signerIndices)
   217  
   218  	return prefixed, nil
   219  }
   220  
   221  // DecodeSignerIndicesToIdentifiers decodes the given compacted bit vector into signerIDs
   222  // Prerequisite:
   223  //   - The input `canonicalIdentifiers` must exhaustively list the set of authorized signers in their canonical order.
   224  //
   225  // Expected Error returns during normal operations:
   226  // * signature.InvalidSignerIndicesError if the given index vector `prefixed` does not encode a valid set of signers
   227  func DecodeSignerIndicesToIdentifiers(
   228  	canonicalIdentifiers flow.IdentifierList,
   229  	prefixed []byte,
   230  ) (flow.IdentifierList, error) {
   231  	indices, err := decodeSignerIndices(canonicalIdentifiers, prefixed)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	signerIDs := make(flow.IdentifierList, 0, len(indices))
   237  	for _, index := range indices {
   238  		signerIDs = append(signerIDs, canonicalIdentifiers[index])
   239  	}
   240  	return signerIDs, nil
   241  }
   242  
   243  func decodeSignerIndices(
   244  	canonicalIdentifiers flow.IdentifierList,
   245  	prefixed []byte,
   246  ) ([]int, error) {
   247  	// the prefixed contains the checksum of the canonicalIdentifiers that the signerIndices
   248  	// creator saw.
   249  	// extract the checksum and compare with the canonicalIdentifiers to see if both
   250  	// the signerIndices creator and validator see the same list.
   251  	signerIndices, err := CompareAndExtract(canonicalIdentifiers, prefixed)
   252  	if err != nil {
   253  		if errors.Is(err, ErrInvalidChecksum) {
   254  			return nil, NewInvalidSignerIndicesErrorf("signer indices' checkum is invalid: %w", err)
   255  		}
   256  		return nil, fmt.Errorf("unexpected exception while checking signer indices: %w", err)
   257  	}
   258  
   259  	numberCanonicalNodes := len(canonicalIdentifiers)
   260  	err = validPadding(signerIndices, numberCanonicalNodes)
   261  	if err != nil {
   262  		if errors.Is(err, ErrIncompatibleBitVectorLength) || errors.Is(err, ErrIllegallyPaddedBitVector) {
   263  			return nil, NewInvalidSignerIndicesErrorf("invalid padding of signerIndices: %w", err)
   264  		}
   265  		return nil, fmt.Errorf("unexpected exception while checking padding of signer indices: %w", err)
   266  	}
   267  
   268  	// decode bits to Identifiers
   269  	indices := make([]int, 0, numberCanonicalNodes)
   270  	for i := 0; i < numberCanonicalNodes; i++ {
   271  		if bitutils.ReadBit(signerIndices, i) == 1 {
   272  			indices = append(indices, i)
   273  		}
   274  	}
   275  	return indices, nil
   276  }
   277  
   278  // DecodeSignerIndicesToIdentities decodes the given compacted bit vector into node Identities.
   279  // Prerequisite:
   280  //   - The input `canonicalIdentifiers` must exhaustively list the set of authorized signers in their canonical order.
   281  //
   282  // The returned list of decoded identities is in canonical order.
   283  //
   284  // Expected Error returns during normal operations:
   285  // * signature.InvalidSignerIndicesError if the given index vector `prefixed` does not encode a valid set of signers
   286  func DecodeSignerIndicesToIdentities(
   287  	canonicalIdentities flow.IdentitySkeletonList,
   288  	prefixed []byte,
   289  ) (flow.IdentitySkeletonList, error) {
   290  	indices, err := decodeSignerIndices(canonicalIdentities.NodeIDs(), prefixed)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	signers := make(flow.IdentitySkeletonList, 0, len(indices))
   296  	for _, index := range indices {
   297  		signers = append(signers, canonicalIdentities[index])
   298  	}
   299  	return signers, nil
   300  }
   301  
   302  // validPadding verifies that `bitVector` satisfies the following criteria
   303  //  1. The `bitVector`'s length [in bytes], must be the _minimal_ possible length such that it can hold
   304  //     `numUsedBits` number of bits. Otherwise, we return an `ErrIncompatibleBitVectorLength`.
   305  //  2. If `numUsedBits` is _not_ an integer-multiple of 8, `bitVector` is padded with tailing bits. Per
   306  //     convention, these bits must be zero. Otherwise, we return an `ErrIllegallyPaddedBitVector`.
   307  //
   308  // Expected Error returns during normal operations:
   309  //   - ErrIncompatibleBitVectorLength if the vector has the wrong length
   310  //   - ErrIllegallyPaddedBitVector if the vector is padded with bits other than 0
   311  func validPadding(bitVector []byte, numUsedBits int) error {
   312  	// Verify condition 1:
   313  	l := len(bitVector)
   314  	if l != bitutils.MinimalByteSliceLength(numUsedBits) {
   315  		return fmt.Errorf("the bit vector contains a payload of %d used bits, so it should have %d bytes but has %d bytes: %w",
   316  			numUsedBits, bitutils.MinimalByteSliceLength(numUsedBits), l, ErrIncompatibleBitVectorLength)
   317  	}
   318  	// Condition 1 implies that the number of padded bits must be strictly smaller than 8. Otherwise, the vector
   319  	// could have fewer bytes and still have enough room to store `numUsedBits`.
   320  
   321  	// Verify condition 2, i.e. that all padded bits are all 0:
   322  	// * As `bitVector` passed check 1, all padded bits are located in `bitVector`s _last byte_.
   323  	// * Let `lastByte` be the last byte of `bitVector`. The leading bits, specifically `numUsedBits & 7`,
   324  	//   belong to the used payload, which could have non-zero values. We remove these using left-bit-shifts.
   325  	//   The result contains exactly all padded bits (plus some auxiliary 0-bits included by the bit-shift
   326  	//   operator). Hence, condition 2 is satisfied if and only if the result is identical to zero.
   327  	// Note that this implementation is much more efficient than individually checking the padded bits, as we check all
   328  	// padded bits at once; furthermore, we only use multiplication, subtraction, shift, which are fast.
   329  	if numUsedBits&7 == 0 { // if numUsedBits is multiple of 8, then there are no padding bits to check
   330  		return nil
   331  	}
   332  	// the above check has ensured that lastByte does exist (l==0 is excluded)
   333  	lastByte := bitVector[l-1]
   334  	if (lastByte << (numUsedBits & 7)) != 0 { // shift by numUsedBits % 8
   335  		return fmt.Errorf("some padded bits are not zero with %d used bits (bitVector: %x): %w", numUsedBits, bitVector, ErrIllegallyPaddedBitVector)
   336  	}
   337  
   338  	return nil
   339  }