@@ -6,8 +6,10 @@ import (
66 "encoding/binary"
77 "errors"
88 "fmt"
9+ "github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
910 "math/big"
1011
12+ bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
1113 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
1214 "github.com/decred/dcrd/dcrec/secp256k1/v4"
1315 "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
@@ -46,6 +48,16 @@ const (
4648 Secp256r1Keccak256 NamedCurveHash = 123
4749)
4850
51+ const (
52+ Bls12FieldElementLength = 64
53+ Bls12G1EncodedLength = 2 * Bls12FieldElementLength
54+
55+ Bls12G2EncodedLength = 4 * Bls12FieldElementLength
56+ // Bls12381MultiExpMaxPairs is the maximum number of (point, scalar) pairs
57+ // accepted by the Bls12381MultiExp native contract.
58+ Bls12381MultiExpMaxPairs = 128
59+ )
60+
4961func newCrypto () * Crypto {
5062 c := & Crypto {ContractMD : * interop .NewContractMD (nativenames .CryptoLib , nativeids .CryptoLib )}
5163 defer c .BuildHFSpecificMD (c .ActiveIn ())
@@ -134,6 +146,11 @@ func newCrypto() *Crypto {
134146 manifest .NewParameter ("signature" , smartcontract .ByteArrayType ))
135147 md = NewMethodAndPrice (c .recoverSecp256K1 , 1 << 15 , callflag .NoneFlag , config .HFEchidna )
136148 c .AddMethod (md , desc )
149+
150+ desc = NewDescriptor ("bls12381MultiExp" , smartcontract .InteropInterfaceType ,
151+ manifest .NewParameter ("pairs" , smartcontract .ArrayType ))
152+ md = NewMethodAndPrice (c .bls12381MultiExp , 1 << 23 , callflag .NoneFlag , config .HFFaun )
153+ c .AddMethod (md , desc )
137154 return c
138155}
139156
@@ -335,6 +352,185 @@ func (c *Crypto) bls12381Deserialize(_ *interop.Context, args []stackitem.Item)
335352 return stackitem .NewInterop (* p )
336353}
337354
355+ func (c * Crypto ) bls12381EthereumPointSerialize (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
356+ return serializeEthereumPoint (args [0 ])
357+ }
358+
359+ func (c * Crypto ) bls12381EthereumPointDeserialize (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
360+ return deserializeEthereumPoint (args [0 ])
361+ }
362+
363+ func (c * Crypto ) bls12381EthereumPointsSerialize (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
364+ points , ok := args [0 ].Value ().([]stackitem.Item )
365+ if ! ok {
366+ panic ("points must be with array type" )
367+ }
368+ res := make ([]stackitem.Item , 0 , len (points ))
369+ for _ , p := range points {
370+ res = append (res , serializeEthereumPoint (p ))
371+ }
372+ return stackitem .NewArray (res )
373+ }
374+
375+ func (c * Crypto ) bls12381EthereumPointsDeserialize (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
376+ points , ok := args [0 ].Value ().([]stackitem.Item )
377+ if ! ok {
378+ panic ("points must be with array type" )
379+ }
380+ res := make ([]stackitem.Item , 0 , len (points ))
381+ for _ , p := range points {
382+ res = append (res , deserializeEthereumPoint (p ))
383+ }
384+ return stackitem .NewArray (res )
385+ }
386+
387+ func (c * Crypto ) bls12381EthereumPointsAndScalarsSerialize (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
388+ pairs , ok := args [0 ].Value ().([]stackitem.Item )
389+ if ! ok {
390+ panic ("points must be with array type" )
391+ }
392+ res := make ([]stackitem.Item , 0 , len (pairs ))
393+ for _ , si := range pairs {
394+ if si .Type () != stackitem .ArrayT && si .Type () != stackitem .StructT {
395+ panic ("pair must be Array or Struct" )
396+ }
397+ pair := si .Value ().([]stackitem.Item )
398+ if len (pair ) != 2 {
399+ panic ("pair must contain point and scalar" )
400+ }
401+ scalarLE , err := pair [1 ].TryBytes ()
402+ if err != nil {
403+ panic (fmt .Errorf ("can't get scalar bytes: %w" , err ))
404+ }
405+ scalarBytes := make ([]byte , fr .Bytes )
406+ copy (scalarBytes , scalarLE )
407+ res = append (res , stackitem .NewArray ([]stackitem.Item {
408+ serializeEthereumPoint (pair [0 ]),
409+ stackitem .NewByteArray (scalarBytes ),
410+ }))
411+ }
412+ return stackitem .NewArray (res )
413+ }
414+
415+ func (c * Crypto ) bls12381EthereumPointsAndScalarsDeserialize (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
416+ pairs , ok := args [0 ].Value ().([]stackitem.Item )
417+ if ! ok {
418+ panic ("points must be with array type" )
419+ }
420+ res := make ([]stackitem.Item , 0 , len (pairs ))
421+ for _ , si := range pairs {
422+ if si .Type () != stackitem .ArrayT && si .Type () != stackitem .StructT {
423+ panic ("pair must be Array or Struct" )
424+ }
425+ pair := si .Value ().([]stackitem.Item )
426+ if len (pair ) != 2 {
427+ panic ("pair must contain point and scalar" )
428+ }
429+ scalarBytes , err := pair [1 ].TryBytes ()
430+ if err != nil {
431+ panic (fmt .Errorf ("invalid multiplier: %w" , err ))
432+ }
433+ scalar , err := scalarFromBytes (scalarBytes , false )
434+ if err != nil {
435+ panic (fmt .Errorf ("can't get scalar from bytes: %w" , err ))
436+ }
437+ res = append (res , stackitem .NewArray ([]stackitem.Item {
438+ deserializeEthereumPoint (pair [0 ]),
439+ stackitem .NewBigInteger (scalar .BigInt (new (big.Int ))),
440+ }))
441+ }
442+ return stackitem .NewArray (res )
443+ }
444+
445+ func serializeEthereumPoint (point stackitem.Item ) stackitem.Item {
446+ if point .Type () != stackitem .InteropT {
447+ panic (fmt .Errorf ("point type must be an %s, got %s" , stackitem .InteropT , point .Type ()))
448+ }
449+ p , ok := point .Value ().(blsPoint )
450+ if ! ok {
451+ panic ("serialized item is not a bls12381 point" )
452+ }
453+ var (
454+ g1 * bls12381.G1Affine
455+ g2 * bls12381.G2Affine
456+ )
457+ switch p := p .point .(type ) {
458+ case * bls12381.G1Affine :
459+ g1 = p
460+ case * bls12381.G1Jac :
461+ g1 = new (bls12381.G1Affine ).FromJacobian (p )
462+ case * bls12381.G2Affine :
463+ g2 = p
464+ case * bls12381.G2Jac :
465+ g2 = new (bls12381.G2Affine ).FromJacobian (p )
466+ default :
467+ panic ("invalid point type" )
468+ }
469+ if g1 != nil {
470+ if g1 .IsInfinity () {
471+ return stackitem .NewByteArray (make ([]byte , Bls12G1EncodedLength ))
472+ }
473+ bytes := g1 .RawBytes ()
474+ return stackitem .NewByteArray (toEthereum (bytes [:]))
475+ }
476+ if g2 .IsInfinity () {
477+ return stackitem .NewByteArray (make ([]byte , Bls12G2EncodedLength ))
478+ }
479+ bytes := g2 .RawBytes ()
480+ return stackitem .NewByteArray (toEthereum (bytes [:]))
481+ }
482+
483+ func deserializeEthereumPoint (point stackitem.Item ) stackitem.Item {
484+ buf , err := point .TryBytes ()
485+ if err != nil {
486+ panic (fmt .Errorf ("invalid serialized ethereum point: %w" , err ))
487+ }
488+ if l := len (buf ); l != Bls12G1EncodedLength && l != Bls12G2EncodedLength {
489+ panic (fmt .Errorf ("ethereum point must be with length %d or %d bytes, got %d bytes" , Bls12G1EncodedLength , Bls12G2EncodedLength , l ))
490+ }
491+ var p any
492+ if len (buf ) == Bls12G1EncodedLength {
493+ g1 := & bls12381.G1Affine {}
494+ _ , err = g1 .SetBytes (fromEthereum (buf ))
495+ p = g1
496+ } else {
497+ g2 := & bls12381.G2Affine {}
498+ _ , err = g2 .SetBytes (fromEthereum (buf ))
499+ p = g2
500+ }
501+ if err != nil {
502+ panic (err )
503+ }
504+ return stackitem .NewInterop (blsPoint {point : p })
505+ }
506+
507+ func fromEthereum (data []byte ) []byte {
508+ var (
509+ count = len (data ) / Bls12FieldElementLength
510+ res = make ([]byte , count * fp .Bytes )
511+ )
512+ for i := range count {
513+ for _ , b := range data [i * Bls12FieldElementLength : (i + 1 )* Bls12FieldElementLength - fp .Bytes ] {
514+ if b != 0 {
515+ panic ("bls12-381 field element overflow" )
516+ }
517+ }
518+ copy (res [i * fp .Bytes :(i + 1 )* fp .Bytes ], data [(i + 1 )* Bls12FieldElementLength - fp .Bytes :(i + 1 )* Bls12FieldElementLength ])
519+ }
520+ return res
521+ }
522+
523+ func toEthereum (data []byte ) []byte {
524+ var (
525+ count = len (data ) / fp .Bytes
526+ res = make ([]byte , count * Bls12FieldElementLength )
527+ )
528+ for i := range count {
529+ copy (res [(i + 1 )* Bls12FieldElementLength - fp .Bytes :(i + 1 )* Bls12FieldElementLength ], data [i * fp .Bytes :(i + 1 )* fp .Bytes ])
530+ }
531+ return res
532+ }
533+
338534func (c * Crypto ) bls12381Equal (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
339535 a , okA := args [0 ].(* stackitem.Interop ).Value ().(blsPoint )
340536 b , okB := args [1 ].(* stackitem.Interop ).Value ().(blsPoint )
@@ -362,6 +558,78 @@ func (c *Crypto) bls12381Add(_ *interop.Context, args []stackitem.Item) stackite
362558 return stackitem .NewInterop (p )
363559}
364560
561+ func (c * Crypto ) bls12381MultiExp (_ * interop.Context , args []stackitem.Item ) stackitem.Item {
562+ pairs := args [0 ].Value ().([]stackitem.Item )
563+ if len (pairs ) == 0 {
564+ panic ("BLS12-381 multi exponent requires at least one pair" )
565+ }
566+ if len (pairs ) > Bls12381MultiExpMaxPairs {
567+ panic (fmt .Sprintf ("BLS12-381 multi exponent supports at most %d pairs" , Bls12381MultiExpMaxPairs ))
568+ }
569+ var (
570+ useG2 int // 1 => use G2
571+ accumulator blsPoint
572+ )
573+ for _ , si := range pairs {
574+ if si .Type () != stackitem .ArrayT && si .Type () != stackitem .StructT {
575+ panic ("BLS12-381 multi exponent pair must be Array or Struct" )
576+ }
577+ pair := si .Value ().([]stackitem.Item )
578+ if len (pair ) != 2 {
579+ panic ("BLS12-381 multi exponent pair must contain point and scalar" )
580+ }
581+ if pair [0 ].Type () != stackitem .InteropT {
582+ panic ("BLS12-381 multi exponent requires interop points" )
583+ }
584+ point , ok := pair [0 ].Value ().(blsPoint )
585+ if ! ok {
586+ panic ("BLS12-381 multi exponent interop must contain blsPoint" )
587+ }
588+ switch point .point .(type ) {
589+ case * bls12381.G1Jac , * bls12381.G1Affine :
590+ useG2 = ensureGroupType (useG2 , - 1 )
591+ case * bls12381.G2Jac , * bls12381.G2Affine :
592+ useG2 = ensureGroupType (useG2 , 1 )
593+ default :
594+ panic ("BLS12-381 type mismatch" )
595+ }
596+ mulBytes , err := pair [1 ].TryBytes ()
597+ if err != nil {
598+ panic (fmt .Errorf ("invalid multiplier: %w" , err ))
599+ }
600+ alpha , err := scalarFromBytes (mulBytes , false )
601+ if err != nil {
602+ panic (err )
603+ }
604+ if alpha .BigInt (new (big.Int )).Sign () == 0 {
605+ continue
606+ }
607+ res , err := blsPointMul (point , alpha .BigInt (new (big.Int )))
608+ if err != nil {
609+ panic (err )
610+ }
611+ if accumulator .point == nil {
612+ accumulator .point = res .point
613+ } else if accumulator , err = blsPointAdd (accumulator , res ); err != nil {
614+ panic (err )
615+ }
616+ }
617+ if useG2 == 0 {
618+ panic ("BLS12-381 multi exponent requires at least one valid pair" )
619+ }
620+ return stackitem .NewInterop (accumulator )
621+ }
622+
623+ func ensureGroupType (useG2 , isG2 int ) int {
624+ if useG2 == 0 {
625+ return isG2
626+ }
627+ if useG2 != isG2 {
628+ panic ("BLS12-381 multi exponent cannot mix groups" )
629+ }
630+ return isG2
631+ }
632+
365633func scalarFromBytes (bytes []byte , neg bool ) (* fr.Element , error ) {
366634 alpha := new (fr.Element )
367635 if len (bytes ) != fr .Bytes {
0 commit comments