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