github.com/onflow/flow-go@v0.33.17/fvm/fvm_signature_test.go (about)

     1  package fvm_test
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/onflow/cadence"
     9  	jsoncdc "github.com/onflow/cadence/encoding/json"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/onflow/flow-go/crypto"
    14  	"github.com/onflow/flow-go/crypto/hash"
    15  
    16  	"github.com/onflow/flow-go/engine/execution/testutil"
    17  	"github.com/onflow/flow-go/fvm"
    18  	fvmCrypto "github.com/onflow/flow-go/fvm/crypto"
    19  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    20  	"github.com/onflow/flow-go/model/flow"
    21  	msig "github.com/onflow/flow-go/module/signature"
    22  )
    23  
    24  var createMessage = func(m string) (signableMessage []byte, message cadence.Array) {
    25  	signableMessage = []byte(m)
    26  	message = testutil.BytesToCadenceArray(signableMessage)
    27  	return signableMessage, message
    28  }
    29  
    30  func TestKeyListSignature(t *testing.T) {
    31  
    32  	t.Parallel()
    33  
    34  	type signatureAlgorithm struct {
    35  		name       string
    36  		seedLength int
    37  		algorithm  crypto.SigningAlgorithm
    38  	}
    39  
    40  	signatureAlgorithms := []signatureAlgorithm{
    41  		{"ECDSA_P256", crypto.KeyGenSeedMinLen, crypto.ECDSAP256},
    42  		{"ECDSA_secp256k1", crypto.KeyGenSeedMinLen, crypto.ECDSASecp256k1},
    43  	}
    44  
    45  	type hashAlgorithm struct {
    46  		name   string
    47  		hasher func(string) hash.Hasher
    48  	}
    49  
    50  	// Hardcoded tag as required by the crypto.keyList Cadence contract
    51  	// TODO: update to a random tag once the Cadence contract is updated
    52  	// to accept custom tags
    53  	tag := "FLOW-V0.0-user"
    54  
    55  	hashAlgorithms := []hashAlgorithm{
    56  		{
    57  			"SHA3_256",
    58  			func(tag string) hash.Hasher {
    59  				hasher, err := fvmCrypto.NewPrefixedHashing(hash.SHA3_256, tag)
    60  				require.Nil(t, err)
    61  				return hasher
    62  			},
    63  		},
    64  		{
    65  			"SHA2_256",
    66  			func(tag string) hash.Hasher {
    67  				hasher, err := fvmCrypto.NewPrefixedHashing(hash.SHA2_256, tag)
    68  				require.Nil(t, err)
    69  				return hasher
    70  			},
    71  		},
    72  		{
    73  			"KECCAK_256",
    74  			func(tag string) hash.Hasher {
    75  				hasher, err := fvmCrypto.NewPrefixedHashing(hash.Keccak_256, tag)
    76  				require.Nil(t, err)
    77  				return hasher
    78  			},
    79  		},
    80  	}
    81  
    82  	testForHash := func(signatureAlgorithm signatureAlgorithm, hashAlgorithm hashAlgorithm) {
    83  
    84  		code := []byte(
    85  			fmt.Sprintf(
    86  				`
    87                        import Crypto
    88  
    89                        pub fun main(
    90                            rawPublicKeys: [[UInt8]],
    91                            message: [UInt8],
    92                            signatures: [[UInt8]],
    93                            weight: UFix64,
    94                        ): Bool {
    95                            let keyList = Crypto.KeyList()
    96  
    97                            for rawPublicKey in rawPublicKeys {
    98                                keyList.add(
    99                                    PublicKey(
   100                                        publicKey: rawPublicKey,
   101                                        signatureAlgorithm: SignatureAlgorithm.%s
   102                                    ),
   103                                    hashAlgorithm: HashAlgorithm.%s,
   104                                    weight: weight,
   105                                )
   106                            }
   107  
   108                            let signatureSet: [Crypto.KeyListSignature] = []
   109  
   110                            var i = 0
   111                            for signature in signatures {
   112                                signatureSet.append(
   113                                    Crypto.KeyListSignature(
   114                                        keyIndex: i,
   115                                        signature: signature
   116                                    )
   117                                )
   118                                i = i + 1
   119                            }
   120  
   121                            return keyList.verify(
   122                                signatureSet: signatureSet,
   123                                signedData: message,
   124                            )
   125                        }
   126                      `,
   127  				signatureAlgorithm.name,
   128  				hashAlgorithm.name,
   129  			),
   130  		)
   131  
   132  		t.Run(fmt.Sprintf("%s %s", signatureAlgorithm.name, hashAlgorithm.name), func(t *testing.T) {
   133  
   134  			createKey := func() (privateKey crypto.PrivateKey, publicKey cadence.Array) {
   135  				seed := make([]byte, signatureAlgorithm.seedLength)
   136  
   137  				var err error
   138  
   139  				_, err = rand.Read(seed)
   140  				require.NoError(t, err)
   141  
   142  				privateKey, err = crypto.GeneratePrivateKey(signatureAlgorithm.algorithm, seed)
   143  				require.NoError(t, err)
   144  
   145  				publicKey = testutil.BytesToCadenceArray(
   146  					privateKey.PublicKey().Encode(),
   147  				)
   148  
   149  				return privateKey, publicKey
   150  			}
   151  
   152  			signMessage := func(privateKey crypto.PrivateKey, message []byte) cadence.Array {
   153  				signature, err := privateKey.Sign(message, hashAlgorithm.hasher(tag))
   154  				require.NoError(t, err)
   155  
   156  				return testutil.BytesToCadenceArray(signature)
   157  			}
   158  
   159  			t.Run("Single key", newVMTest().run(
   160  				func(
   161  					t *testing.T,
   162  					vm fvm.VM,
   163  					chain flow.Chain,
   164  					ctx fvm.Context,
   165  					snapshotTree snapshot.SnapshotTree,
   166  				) {
   167  					privateKey, publicKey := createKey()
   168  					signableMessage, message := createMessage("foo")
   169  					signature := signMessage(privateKey, signableMessage)
   170  					weight, _ := cadence.NewUFix64("1.0")
   171  
   172  					publicKeys := cadence.NewArray([]cadence.Value{
   173  						publicKey,
   174  					})
   175  
   176  					signatures := cadence.NewArray([]cadence.Value{
   177  						signature,
   178  					})
   179  
   180  					t.Run("Valid", func(t *testing.T) {
   181  						script := fvm.Script(code).WithArguments(
   182  							jsoncdc.MustEncode(publicKeys),
   183  							jsoncdc.MustEncode(message),
   184  							jsoncdc.MustEncode(signatures),
   185  							jsoncdc.MustEncode(weight),
   186  						)
   187  
   188  						_, output, err := vm.Run(ctx, script, snapshotTree)
   189  						assert.NoError(t, err)
   190  						assert.NoError(t, output.Err)
   191  
   192  						assert.Equal(t, cadence.NewBool(true), output.Value)
   193  					})
   194  
   195  					t.Run("Invalid message", func(t *testing.T) {
   196  						_, invalidRawMessage := createMessage("bar")
   197  
   198  						script := fvm.Script(code).WithArguments(
   199  							jsoncdc.MustEncode(publicKeys),
   200  							jsoncdc.MustEncode(invalidRawMessage),
   201  							jsoncdc.MustEncode(signatures),
   202  							jsoncdc.MustEncode(weight),
   203  						)
   204  
   205  						_, output, err := vm.Run(ctx, script, snapshotTree)
   206  						assert.NoError(t, err)
   207  						assert.NoError(t, output.Err)
   208  
   209  						assert.Equal(t, cadence.NewBool(false), output.Value)
   210  					})
   211  
   212  					t.Run("Invalid signature", func(t *testing.T) {
   213  						invalidPrivateKey, _ := createKey()
   214  						invalidRawSignature := signMessage(invalidPrivateKey, signableMessage)
   215  
   216  						invalidRawSignatures := cadence.NewArray([]cadence.Value{
   217  							invalidRawSignature,
   218  						})
   219  
   220  						script := fvm.Script(code).WithArguments(
   221  							jsoncdc.MustEncode(publicKeys),
   222  							jsoncdc.MustEncode(message),
   223  							jsoncdc.MustEncode(invalidRawSignatures),
   224  							jsoncdc.MustEncode(weight),
   225  						)
   226  
   227  						_, output, err := vm.Run(ctx, script, snapshotTree)
   228  						assert.NoError(t, err)
   229  						assert.NoError(t, output.Err)
   230  
   231  						assert.Equal(t, cadence.NewBool(false), output.Value)
   232  					})
   233  
   234  					t.Run("Malformed public key", func(t *testing.T) {
   235  						invalidPublicKey := testutil.BytesToCadenceArray([]byte{1, 2, 3})
   236  
   237  						invalidPublicKeys := cadence.NewArray([]cadence.Value{
   238  							invalidPublicKey,
   239  						})
   240  
   241  						script := fvm.Script(code).WithArguments(
   242  							jsoncdc.MustEncode(invalidPublicKeys),
   243  							jsoncdc.MustEncode(message),
   244  							jsoncdc.MustEncode(signatures),
   245  							jsoncdc.MustEncode(weight),
   246  						)
   247  
   248  						_, output, err := vm.Run(ctx, script, snapshotTree)
   249  						require.NoError(t, err)
   250  						require.Error(t, output.Err)
   251  					})
   252  				},
   253  			))
   254  
   255  			t.Run("Multiple keys", newVMTest().run(
   256  				func(
   257  					t *testing.T,
   258  					vm fvm.VM,
   259  					chain flow.Chain,
   260  					ctx fvm.Context,
   261  					snapshotTree snapshot.SnapshotTree,
   262  				) {
   263  					privateKeyA, publicKeyA := createKey()
   264  					privateKeyB, publicKeyB := createKey()
   265  					privateKeyC, publicKeyC := createKey()
   266  
   267  					publicKeys := cadence.NewArray([]cadence.Value{
   268  						publicKeyA,
   269  						publicKeyB,
   270  						publicKeyC,
   271  					})
   272  
   273  					signableMessage, message := createMessage("foo")
   274  
   275  					signatureA := signMessage(privateKeyA, signableMessage)
   276  					signatureB := signMessage(privateKeyB, signableMessage)
   277  					signatureC := signMessage(privateKeyC, signableMessage)
   278  
   279  					weight, _ := cadence.NewUFix64("0.5")
   280  
   281  					t.Run("3 of 3", func(t *testing.T) {
   282  						signatures := cadence.NewArray([]cadence.Value{
   283  							signatureA,
   284  							signatureB,
   285  							signatureC,
   286  						})
   287  
   288  						script := fvm.Script(code).WithArguments(
   289  							jsoncdc.MustEncode(publicKeys),
   290  							jsoncdc.MustEncode(message),
   291  							jsoncdc.MustEncode(signatures),
   292  							jsoncdc.MustEncode(weight),
   293  						)
   294  
   295  						_, output, err := vm.Run(ctx, script, snapshotTree)
   296  						assert.NoError(t, err)
   297  						assert.NoError(t, output.Err)
   298  
   299  						assert.Equal(t, cadence.NewBool(true), output.Value)
   300  					})
   301  
   302  					t.Run("2 of 3", func(t *testing.T) {
   303  						signatures := cadence.NewArray([]cadence.Value{
   304  							signatureA,
   305  							signatureB,
   306  						})
   307  
   308  						script := fvm.Script(code).WithArguments(
   309  							jsoncdc.MustEncode(publicKeys),
   310  							jsoncdc.MustEncode(message),
   311  							jsoncdc.MustEncode(signatures),
   312  							jsoncdc.MustEncode(weight),
   313  						)
   314  
   315  						_, output, err := vm.Run(ctx, script, snapshotTree)
   316  						assert.NoError(t, err)
   317  						assert.NoError(t, output.Err)
   318  
   319  						assert.Equal(t, cadence.NewBool(true), output.Value)
   320  					})
   321  
   322  					t.Run("1 of 3", func(t *testing.T) {
   323  						signatures := cadence.NewArray([]cadence.Value{
   324  							signatureA,
   325  						})
   326  
   327  						script := fvm.Script(code).WithArguments(
   328  							jsoncdc.MustEncode(publicKeys),
   329  							jsoncdc.MustEncode(message),
   330  							jsoncdc.MustEncode(signatures),
   331  							jsoncdc.MustEncode(weight),
   332  						)
   333  
   334  						_, output, err := vm.Run(ctx, script, snapshotTree)
   335  						assert.NoError(t, err)
   336  						assert.NoError(t, output.Err)
   337  
   338  						assert.Equal(t, cadence.NewBool(false), output.Value)
   339  					})
   340  				},
   341  			))
   342  		})
   343  	}
   344  
   345  	for _, signatureAlgorithm := range signatureAlgorithms {
   346  		for _, hashAlgorithm := range hashAlgorithms {
   347  			testForHash(signatureAlgorithm, hashAlgorithm)
   348  		}
   349  	}
   350  
   351  	testForHash(signatureAlgorithm{
   352  		"BLS_BLS12_381",
   353  		crypto.KeyGenSeedMinLen,
   354  		crypto.BLSBLS12381,
   355  	}, hashAlgorithm{
   356  		"KMAC128_BLS_BLS12_381",
   357  		func(tag string) hash.Hasher {
   358  			return msig.NewBLSHasher(tag)
   359  		},
   360  	})
   361  }
   362  
   363  func TestBLSMultiSignature(t *testing.T) {
   364  
   365  	t.Parallel()
   366  
   367  	type signatureAlgorithm struct {
   368  		name       string
   369  		seedLength int
   370  		algorithm  crypto.SigningAlgorithm
   371  	}
   372  
   373  	signatureAlgorithms := []signatureAlgorithm{
   374  		{"BLS_BLS12_381", crypto.KeyGenSeedMinLen, crypto.BLSBLS12381},
   375  		{"ECDSA_P256", crypto.KeyGenSeedMinLen, crypto.ECDSAP256},
   376  		{"ECDSA_secp256k1", crypto.KeyGenSeedMinLen, crypto.ECDSASecp256k1},
   377  	}
   378  	BLSSignatureAlgorithm := signatureAlgorithms[0]
   379  
   380  	randomSK := func(t *testing.T, signatureAlgorithm signatureAlgorithm) crypto.PrivateKey {
   381  		seed := make([]byte, signatureAlgorithm.seedLength)
   382  		n, err := rand.Read(seed)
   383  		require.Equal(t, n, signatureAlgorithm.seedLength)
   384  		require.NoError(t, err)
   385  		sk, err := crypto.GeneratePrivateKey(signatureAlgorithm.algorithm, seed)
   386  		require.NoError(t, err)
   387  		return sk
   388  	}
   389  
   390  	testVerifyPoP := func() {
   391  		t.Run("verifyBLSPoP", newVMTest().run(
   392  			func(
   393  				t *testing.T,
   394  				vm fvm.VM,
   395  				chain flow.Chain,
   396  				ctx fvm.Context,
   397  				snapshotTree snapshot.SnapshotTree,
   398  			) {
   399  
   400  				code := func(signatureAlgorithm signatureAlgorithm) []byte {
   401  					return []byte(
   402  						fmt.Sprintf(
   403  							`
   404  								import Crypto
   405  		
   406  								pub fun main(
   407  									publicKey: [UInt8],
   408  									proof: [UInt8]
   409  								): Bool {
   410  									let p = PublicKey(
   411  										publicKey: publicKey, 
   412  										signatureAlgorithm: SignatureAlgorithm.%s
   413  									)
   414  									return p.verifyPoP(proof)
   415  								}
   416  								`,
   417  							signatureAlgorithm.name,
   418  						),
   419  					)
   420  				}
   421  
   422  				t.Run("valid and correct BLS key", func(t *testing.T) {
   423  
   424  					sk := randomSK(t, BLSSignatureAlgorithm)
   425  					publicKey := testutil.BytesToCadenceArray(
   426  						sk.PublicKey().Encode(),
   427  					)
   428  
   429  					proof, err := crypto.BLSGeneratePOP(sk)
   430  					require.NoError(t, err)
   431  					pop := testutil.BytesToCadenceArray(
   432  						proof,
   433  					)
   434  
   435  					script := fvm.Script(code(BLSSignatureAlgorithm)).WithArguments(
   436  						jsoncdc.MustEncode(publicKey),
   437  						jsoncdc.MustEncode(pop),
   438  					)
   439  
   440  					_, output, err := vm.Run(ctx, script, snapshotTree)
   441  					assert.NoError(t, err)
   442  					assert.NoError(t, output.Err)
   443  					assert.Equal(t, cadence.NewBool(true), output.Value)
   444  
   445  				})
   446  
   447  				t.Run("valid but incorrect BLS key", func(t *testing.T) {
   448  
   449  					sk := randomSK(t, BLSSignatureAlgorithm)
   450  					publicKey := testutil.BytesToCadenceArray(
   451  						sk.PublicKey().Encode(),
   452  					)
   453  
   454  					otherSk := randomSK(t, BLSSignatureAlgorithm)
   455  					proof, err := crypto.BLSGeneratePOP(otherSk)
   456  					require.NoError(t, err)
   457  
   458  					pop := testutil.BytesToCadenceArray(
   459  						proof,
   460  					)
   461  					script := fvm.Script(code(BLSSignatureAlgorithm)).WithArguments(
   462  						jsoncdc.MustEncode(publicKey),
   463  						jsoncdc.MustEncode(pop),
   464  					)
   465  
   466  					_, output, err := vm.Run(ctx, script, snapshotTree)
   467  					assert.NoError(t, err)
   468  					assert.NoError(t, output.Err)
   469  					assert.Equal(t, cadence.NewBool(false), output.Value)
   470  
   471  				})
   472  
   473  				for _, signatureAlgorithm := range signatureAlgorithms[1:] {
   474  					t.Run("valid non BLS key/"+signatureAlgorithm.name, func(t *testing.T) {
   475  						sk := randomSK(t, signatureAlgorithm)
   476  						publicKey := testutil.BytesToCadenceArray(
   477  							sk.PublicKey().Encode(),
   478  						)
   479  
   480  						random := make([]byte, crypto.SignatureLenBLSBLS12381)
   481  						_, err := rand.Read(random)
   482  						require.NoError(t, err)
   483  						pop := testutil.BytesToCadenceArray(
   484  							random,
   485  						)
   486  
   487  						script := fvm.Script(code(signatureAlgorithm)).WithArguments(
   488  							jsoncdc.MustEncode(publicKey),
   489  							jsoncdc.MustEncode(pop),
   490  						)
   491  
   492  						_, output, err := vm.Run(ctx, script, snapshotTree)
   493  						assert.NoError(t, err)
   494  						assert.Error(t, output.Err)
   495  					})
   496  				}
   497  			},
   498  		))
   499  	}
   500  
   501  	testBLSSignatureAggregation := func() {
   502  		t.Run("aggregateBLSSignatures", newVMTest().run(
   503  			func(
   504  				t *testing.T,
   505  				vm fvm.VM,
   506  				chain flow.Chain,
   507  				ctx fvm.Context,
   508  				snapshotTree snapshot.SnapshotTree,
   509  			) {
   510  
   511  				code := []byte(
   512  					`
   513  							import Crypto
   514  	
   515  							pub fun main(
   516  							signatures: [[UInt8]],
   517  							): [UInt8]? {
   518  								return BLS.aggregateSignatures(signatures)!
   519  							}
   520  						`,
   521  				)
   522  
   523  				// random message
   524  				input := make([]byte, 100)
   525  				_, err := rand.Read(input)
   526  				require.NoError(t, err)
   527  
   528  				// generate keys and signatures
   529  				numSigs := 50
   530  				sigs := make([]crypto.Signature, 0, numSigs)
   531  
   532  				kmac := msig.NewBLSHasher("test tag")
   533  				for i := 0; i < numSigs; i++ {
   534  					sk := randomSK(t, BLSSignatureAlgorithm)
   535  					// a valid BLS signature
   536  					s, err := sk.Sign(input, kmac)
   537  					require.NoError(t, err)
   538  					sigs = append(sigs, s)
   539  				}
   540  
   541  				t.Run("valid BLS signatures", func(t *testing.T) {
   542  
   543  					signatures := make([]cadence.Value, 0, numSigs)
   544  					for _, sig := range sigs {
   545  						s := testutil.BytesToCadenceArray(sig)
   546  						signatures = append(signatures, s)
   547  					}
   548  
   549  					script := fvm.Script(code).WithArguments(
   550  						jsoncdc.MustEncode(cadence.Array{
   551  							Values: signatures,
   552  							ArrayType: &cadence.VariableSizedArrayType{
   553  								ElementType: &cadence.VariableSizedArrayType{
   554  									ElementType: cadence.UInt8Type{},
   555  								},
   556  							},
   557  						}),
   558  					)
   559  
   560  					_, output, err := vm.Run(ctx, script, snapshotTree)
   561  					assert.NoError(t, err)
   562  					assert.NoError(t, output.Err)
   563  
   564  					expectedSig, err := crypto.AggregateBLSSignatures(sigs)
   565  					require.NoError(t, err)
   566  					assert.Equal(t, cadence.Optional{Value: testutil.BytesToCadenceArray(expectedSig)}, output.Value)
   567  				})
   568  
   569  				t.Run("at least one invalid BLS signature", func(t *testing.T) {
   570  
   571  					signatures := make([]cadence.Value, 0, numSigs)
   572  					// alter one random signature
   573  					tmp := sigs[numSigs/2]
   574  					sigs[numSigs/2] = crypto.BLSInvalidSignature()
   575  
   576  					for _, sig := range sigs {
   577  						s := testutil.BytesToCadenceArray(sig)
   578  						signatures = append(signatures, s)
   579  					}
   580  
   581  					script := fvm.Script(code).WithArguments(
   582  						jsoncdc.MustEncode(cadence.Array{
   583  							Values: signatures,
   584  							ArrayType: &cadence.VariableSizedArrayType{
   585  								ElementType: &cadence.VariableSizedArrayType{
   586  									ElementType: cadence.UInt8Type{},
   587  								},
   588  							},
   589  						}),
   590  					)
   591  
   592  					// revert the change
   593  					sigs[numSigs/2] = tmp
   594  
   595  					_, output, err := vm.Run(ctx, script, snapshotTree)
   596  					assert.NoError(t, err)
   597  					assert.Error(t, output.Err)
   598  					assert.Equal(t, nil, output.Value)
   599  				})
   600  
   601  				t.Run("empty signature list", func(t *testing.T) {
   602  
   603  					signatures := []cadence.Value{}
   604  					script := fvm.Script(code).WithArguments(
   605  						jsoncdc.MustEncode(cadence.Array{
   606  							Values: signatures,
   607  							ArrayType: &cadence.VariableSizedArrayType{
   608  								ElementType: &cadence.VariableSizedArrayType{
   609  									ElementType: cadence.UInt8Type{},
   610  								},
   611  							},
   612  						}),
   613  					)
   614  
   615  					_, output, err := vm.Run(ctx, script, snapshotTree)
   616  					assert.NoError(t, err)
   617  					assert.Error(t, output.Err)
   618  					assert.Equal(t, nil, output.Value)
   619  				})
   620  			},
   621  		))
   622  	}
   623  
   624  	testKeyAggregation := func() {
   625  		t.Run("aggregateBLSPublicKeys", newVMTest().run(
   626  			func(
   627  				t *testing.T,
   628  				vm fvm.VM,
   629  				chain flow.Chain,
   630  				ctx fvm.Context,
   631  				snapshotTree snapshot.SnapshotTree,
   632  			) {
   633  
   634  				code := func(signatureAlgorithm signatureAlgorithm) []byte {
   635  					return []byte(
   636  						fmt.Sprintf(
   637  							`
   638  								import Crypto
   639  		
   640  								pub fun main(
   641  									publicKeys: [[UInt8]]
   642  								): [UInt8]? {
   643  									let pks: [PublicKey] = []
   644  									for pk in publicKeys {
   645  										pks.append(PublicKey(
   646  											publicKey: pk, 
   647  											signatureAlgorithm: SignatureAlgorithm.%s
   648  										))
   649  									}
   650  									return BLS.aggregatePublicKeys(pks)!.publicKey
   651  								}
   652  								`,
   653  							signatureAlgorithm.name,
   654  						),
   655  					)
   656  				}
   657  
   658  				pkNum := 100
   659  				pks := make([]crypto.PublicKey, 0, pkNum)
   660  
   661  				t.Run("valid BLS keys", func(t *testing.T) {
   662  
   663  					publicKeys := make([]cadence.Value, 0, pkNum)
   664  					for i := 0; i < pkNum; i++ {
   665  						sk := randomSK(t, BLSSignatureAlgorithm)
   666  						pk := sk.PublicKey()
   667  						pks = append(pks, pk)
   668  						publicKeys = append(
   669  							publicKeys,
   670  							testutil.BytesToCadenceArray(pk.Encode()),
   671  						)
   672  					}
   673  
   674  					script := fvm.Script(code(BLSSignatureAlgorithm)).WithArguments(
   675  						jsoncdc.MustEncode(cadence.Array{
   676  							Values: publicKeys,
   677  							ArrayType: &cadence.VariableSizedArrayType{
   678  								ElementType: &cadence.VariableSizedArrayType{
   679  									ElementType: cadence.UInt8Type{},
   680  								},
   681  							},
   682  						}),
   683  					)
   684  
   685  					_, output, err := vm.Run(ctx, script, snapshotTree)
   686  					assert.NoError(t, err)
   687  					assert.NoError(t, output.Err)
   688  					expectedPk, err := crypto.AggregateBLSPublicKeys(pks)
   689  					require.NoError(t, err)
   690  
   691  					assert.Equal(t, cadence.Optional{Value: testutil.BytesToCadenceArray(expectedPk.Encode())}, output.Value)
   692  				})
   693  
   694  				for _, signatureAlgorithm := range signatureAlgorithms[1:] {
   695  					t.Run("non BLS keys/"+signatureAlgorithm.name, func(t *testing.T) {
   696  
   697  						publicKeys := make([]cadence.Value, 0, pkNum)
   698  						for i := 0; i < pkNum; i++ {
   699  							sk := randomSK(t, signatureAlgorithm)
   700  							pk := sk.PublicKey()
   701  							pks = append(pks, pk)
   702  							publicKeys = append(
   703  								publicKeys,
   704  								testutil.BytesToCadenceArray(sk.PublicKey().Encode()),
   705  							)
   706  						}
   707  
   708  						script := fvm.Script(code(signatureAlgorithm)).WithArguments(
   709  							jsoncdc.MustEncode(cadence.Array{
   710  								Values: publicKeys,
   711  								ArrayType: &cadence.VariableSizedArrayType{
   712  									ElementType: &cadence.VariableSizedArrayType{
   713  										ElementType: cadence.UInt8Type{},
   714  									},
   715  								},
   716  							}),
   717  						)
   718  
   719  						_, output, err := vm.Run(ctx, script, snapshotTree)
   720  						assert.NoError(t, err)
   721  						assert.Error(t, output.Err)
   722  					})
   723  				}
   724  
   725  				t.Run("empty list", func(t *testing.T) {
   726  
   727  					var publicKeys []cadence.Value
   728  					script := fvm.Script(code(BLSSignatureAlgorithm)).WithArguments(
   729  						jsoncdc.MustEncode(cadence.Array{
   730  							Values: publicKeys,
   731  							ArrayType: &cadence.VariableSizedArrayType{
   732  								ElementType: &cadence.VariableSizedArrayType{
   733  									ElementType: cadence.UInt8Type{},
   734  								},
   735  							},
   736  						}),
   737  					)
   738  
   739  					_, output, err := vm.Run(ctx, script, snapshotTree)
   740  					assert.NoError(t, err)
   741  					assert.Error(t, output.Err)
   742  					assert.Equal(t, nil, output.Value)
   743  				})
   744  			},
   745  		))
   746  	}
   747  
   748  	testBLSCombinedAggregations := func() {
   749  		t.Run("Combined Aggregations", newVMTest().run(
   750  			func(
   751  				t *testing.T,
   752  				vm fvm.VM,
   753  				chain flow.Chain,
   754  				ctx fvm.Context,
   755  				snapshotTree snapshot.SnapshotTree,
   756  			) {
   757  
   758  				message, cadenceMessage := createMessage("random_message")
   759  				tag := "random_tag"
   760  
   761  				code := []byte(`
   762  							import Crypto
   763  
   764  							pub fun main(
   765  								publicKeys: [[UInt8]],
   766  								signatures: [[UInt8]],
   767  								message:  [UInt8],
   768  								tag: String,
   769  							): Bool {
   770  								let pks: [PublicKey] = []
   771  								for pk in publicKeys {
   772  									pks.append(PublicKey(
   773  										publicKey: pk,
   774  										signatureAlgorithm: SignatureAlgorithm.BLS_BLS12_381
   775  									))
   776  								}
   777  								let aggPk = BLS.aggregatePublicKeys(pks)!
   778  								let aggSignature = BLS.aggregateSignatures(signatures)!
   779  								let boo = aggPk.verify(
   780  									signature: aggSignature, 
   781  									signedData: message, 
   782  									domainSeparationTag: tag, 
   783  									hashAlgorithm: HashAlgorithm.KMAC128_BLS_BLS12_381)
   784  								return boo
   785  							}
   786  							`)
   787  
   788  				num := 50
   789  				publicKeys := make([]cadence.Value, 0, num)
   790  				signatures := make([]cadence.Value, 0, num)
   791  
   792  				kmac := msig.NewBLSHasher(string(tag))
   793  				for i := 0; i < num; i++ {
   794  					sk := randomSK(t, BLSSignatureAlgorithm)
   795  					pk := sk.PublicKey()
   796  					publicKeys = append(
   797  						publicKeys,
   798  						testutil.BytesToCadenceArray(pk.Encode()),
   799  					)
   800  					sig, err := sk.Sign(message, kmac)
   801  					require.NoError(t, err)
   802  					signatures = append(
   803  						signatures,
   804  						testutil.BytesToCadenceArray(sig),
   805  					)
   806  				}
   807  
   808  				script := fvm.Script(code).WithArguments(
   809  					jsoncdc.MustEncode(cadence.Array{ // keys
   810  						Values: publicKeys,
   811  						ArrayType: &cadence.VariableSizedArrayType{
   812  							ElementType: &cadence.VariableSizedArrayType{
   813  								ElementType: cadence.UInt8Type{},
   814  							},
   815  						},
   816  					}),
   817  					jsoncdc.MustEncode(cadence.Array{ // signatures
   818  						Values: signatures,
   819  						ArrayType: &cadence.VariableSizedArrayType{
   820  							ElementType: &cadence.VariableSizedArrayType{
   821  								ElementType: cadence.UInt8Type{},
   822  							},
   823  						},
   824  					}),
   825  					jsoncdc.MustEncode(cadenceMessage),
   826  					jsoncdc.MustEncode(cadence.String(tag)),
   827  				)
   828  
   829  				_, output, err := vm.Run(ctx, script, snapshotTree)
   830  				assert.NoError(t, err)
   831  				assert.NoError(t, output.Err)
   832  				assert.Equal(t, cadence.NewBool(true), output.Value)
   833  			},
   834  		))
   835  	}
   836  
   837  	testVerifyPoP()
   838  	testKeyAggregation()
   839  	testBLSSignatureAggregation()
   840  	testBLSCombinedAggregations()
   841  }