Merge pull request #3042 from nspcc-dev/allow-gt-equality-comparison

This commit is contained in:
Roman Khimov 2023-06-15 22:38:20 +03:00 committed by GitHub
commit 9185820289
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 468 additions and 254 deletions

View file

@ -7,7 +7,6 @@ import (
"fmt"
"math/big"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
@ -178,32 +177,11 @@ func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) {
}
func (c *Crypto) bls12381Serialize(_ *interop.Context, args []stackitem.Item) stackitem.Item {
val := args[0].(*stackitem.Interop).Value()
var res []byte
switch p := val.(type) {
case *bls12381.G1Affine:
compressed := p.Bytes()
res = compressed[:]
case *bls12381.G1Jac:
g1Affine := new(bls12381.G1Affine)
g1Affine.FromJacobian(p)
compressed := g1Affine.Bytes()
res = compressed[:]
case *bls12381.G2Affine:
compressed := p.Bytes()
res = compressed[:]
case *bls12381.G2Jac:
g2Affine := new(bls12381.G2Affine)
g2Affine.FromJacobian(p)
compressed := g2Affine.Bytes()
res = compressed[:]
case *bls12381.GT:
compressed := p.Bytes()
res = compressed[:]
default:
panic(errors.New("unknown bls12381 point type"))
val, ok := args[0].(*stackitem.Interop).Value().(blsPoint)
if !ok {
panic(errors.New("not a bls12381 point"))
}
return stackitem.NewByteArray(res)
return stackitem.NewByteArray(val.Bytes())
}
func (c *Crypto) bls12381Deserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item {
@ -211,142 +189,39 @@ func (c *Crypto) bls12381Deserialize(_ *interop.Context, args []stackitem.Item)
if err != nil {
panic(fmt.Errorf("invalid serialized bls12381 point: %w", err))
}
var res interface{}
switch l := len(buf); l {
case bls12381.SizeOfG1AffineCompressed:
g1Affine := new(bls12381.G1Affine)
_, err = g1Affine.SetBytes(buf)
p := new(blsPoint)
err = p.FromBytes(buf)
if err != nil {
panic(fmt.Errorf("failed to decode bls12381 G1Affine point: %w", err))
panic(err)
}
res = g1Affine
case bls12381.SizeOfG2AffineCompressed:
g2Affine := new(bls12381.G2Affine)
_, err = g2Affine.SetBytes(buf)
if err != nil {
panic(fmt.Errorf("failed to decode bls12381 G2Affine point: %w", err))
}
res = g2Affine
case bls12381.SizeOfGT:
gt := new(bls12381.GT)
err := gt.SetBytes(buf)
if err != nil {
panic(fmt.Errorf("failed to decode GT point: %w", err))
}
res = gt
}
return stackitem.NewInterop(res)
return stackitem.NewInterop(*p)
}
func (c *Crypto) bls12381Equal(_ *interop.Context, args []stackitem.Item) stackitem.Item {
a := args[0].(*stackitem.Interop).Value()
b := args[1].(*stackitem.Interop).Value()
var res bool
switch x := a.(type) {
case *bls12381.G1Affine:
y, ok := b.(*bls12381.G1Affine)
if !ok {
panic(errors.New("y is not bls12381 G1Affine point"))
a, okA := args[0].(*stackitem.Interop).Value().(blsPoint)
b, okB := args[1].(*stackitem.Interop).Value().(blsPoint)
if !(okA && okB) {
panic("some of the arguments are not a bls12381 point")
}
res = x.Equal(y)
case *bls12381.G1Jac:
y, ok := b.(*bls12381.G1Jac)
if !ok {
panic(errors.New("y is not bls12381 G1Jac point"))
}
res = x.Equal(y)
case *bls12381.G2Affine:
y, ok := b.(*bls12381.G2Affine)
if !ok {
panic(errors.New("y is not bls12381 G2Affine point"))
}
res = x.Equal(y)
case *bls12381.G2Jac:
y, ok := b.(*bls12381.G2Jac)
if !ok {
panic(errors.New("y is not bls12381 G2Jac point"))
}
res = x.Equal(y)
default:
panic(fmt.Errorf("unexpected x bls12381 point type: %T", x))
res, err := a.EqualsCheckType(b)
if err != nil {
panic(err)
}
return stackitem.NewBool(res)
}
func (c *Crypto) bls12381Add(_ *interop.Context, args []stackitem.Item) stackitem.Item {
a := args[0].(*stackitem.Interop).Value()
b := args[1].(*stackitem.Interop).Value()
var res interface{}
switch x := a.(type) {
case *bls12381.G1Affine:
switch y := b.(type) {
case *bls12381.G1Affine:
xJac := new(bls12381.G1Jac)
xJac.FromAffine(x)
xJac.AddMixed(y)
res = xJac
case *bls12381.G1Jac:
yJac := new(bls12381.G1Jac)
yJac.Set(y)
yJac.AddMixed(x)
res = yJac
default:
panic("inconsistent point types")
a, okA := args[0].(*stackitem.Interop).Value().(blsPoint)
b, okB := args[1].(*stackitem.Interop).Value().(blsPoint)
if !(okA && okB) {
panic("some of the arguments are not a bls12381 point")
}
case *bls12381.G1Jac:
resJac := new(bls12381.G1Jac)
resJac.Set(x)
switch y := b.(type) {
case *bls12381.G1Affine:
resJac.AddMixed(y)
case *bls12381.G1Jac:
resJac.AddAssign(y)
default:
panic("inconsistent")
p, err := blsPointAdd(a, b)
if err != nil {
panic(err)
}
res = resJac
case *bls12381.G2Affine:
switch y := b.(type) {
case *bls12381.G2Affine:
xJac := new(bls12381.G2Jac)
xJac.FromAffine(x)
xJac.AddMixed(y)
res = xJac
case *bls12381.G2Jac:
yJac := new(bls12381.G2Jac)
yJac.Set(y)
yJac.AddMixed(x)
res = yJac
default:
panic("inconsistent")
}
case *bls12381.G2Jac:
resJac := new(bls12381.G2Jac)
resJac.Set(x)
switch y := b.(type) {
case *bls12381.G2Affine:
resJac.AddMixed(y)
case *bls12381.G2Jac:
resJac.AddAssign(y)
default:
panic("invalid")
}
res = resJac
case *bls12381.GT:
resGT := new(bls12381.GT)
resGT.Set(x)
switch y := b.(type) {
case *bls12381.GT:
// It's multiplication, see https://github.com/neo-project/Neo.Cryptography.BLS12_381/issues/4.
resGT.Mul(x, y)
default:
panic("invalid")
}
res = resGT
default:
panic(fmt.Errorf("unexpected bls12381 point type: %T", x))
}
return stackitem.NewInterop(res)
return stackitem.NewInterop(p)
}
func scalarFromBytes(bytes []byte, neg bool) (*fr.Element, error) {
@ -368,7 +243,10 @@ func scalarFromBytes(bytes []byte, neg bool) (*fr.Element, error) {
}
func (c *Crypto) bls12381Mul(_ *interop.Context, args []stackitem.Item) stackitem.Item {
a := args[0].(*stackitem.Interop).Value()
a, okA := args[0].(*stackitem.Interop).Value().(blsPoint)
if !okA {
panic("multiplier is not a bls12381 point")
}
mulBytes, err := args[1].TryBytes()
if err != nil {
panic(fmt.Errorf("invalid multiplier: %w", err))
@ -384,78 +262,25 @@ func (c *Crypto) bls12381Mul(_ *interop.Context, args []stackitem.Item) stackite
alphaBi := new(big.Int)
alpha.BigInt(alphaBi)
var res interface{}
switch x := a.(type) {
case *bls12381.G1Affine:
// The result is in Jacobian form in the reference implementation.
g1Jac := new(bls12381.G1Jac)
g1Jac.FromAffine(x)
g1Jac.ScalarMultiplication(g1Jac, alphaBi)
res = g1Jac
case *bls12381.G1Jac:
g1Jac := new(bls12381.G1Jac)
g1Jac.ScalarMultiplication(x, alphaBi)
res = g1Jac
case *bls12381.G2Affine:
// The result is in Jacobian form in the reference implementation.
g2Jac := new(bls12381.G2Jac)
g2Jac.FromAffine(x)
g2Jac.ScalarMultiplication(g2Jac, alphaBi)
res = g2Jac
case *bls12381.G2Jac:
g2Jac := new(bls12381.G2Jac)
g2Jac.ScalarMultiplication(x, alphaBi)
res = g2Jac
case *bls12381.GT:
gt := new(bls12381.GT)
// C# implementation differs a bit from go's. They use double-and-add algorithm, see
// https://github.com/neo-project/Neo.Cryptography.BLS12_381/blob/844bc3a4f7d8ba2c545ace90ca124f8ada4c8d29/src/Neo.Cryptography.BLS12_381/Gt.cs#L102
// and https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add,
// Pay attention that C#'s Gt.Double() squares (not doubles!) the initial GT point.
// Thus.C#'s scalar multiplication operation over Gt and Scalar is effectively an exponent.
// Go's exponent algorithm differs a bit from the C#'s double-and-add in that go's one
// uses 2-bits windowed method for multiplication. However, the resulting GT point is
// absolutely the same between two implementations.
gt.Exp(*x, alphaBi)
res = gt
default:
panic(fmt.Errorf("unexpected bls12381 point type: %T", x))
p, err := blsPointMul(a, alphaBi)
if err != nil {
panic(err)
}
return stackitem.NewInterop(res)
return stackitem.NewInterop(p)
}
func (c *Crypto) bls12381Pairing(_ *interop.Context, args []stackitem.Item) stackitem.Item {
a := args[0].(*stackitem.Interop).Value()
b := args[1].(*stackitem.Interop).Value()
var (
x *bls12381.G1Affine
y *bls12381.G2Affine
)
switch p := a.(type) {
case *bls12381.G1Affine:
x = p
case *bls12381.G1Jac:
x = new(bls12381.G1Affine)
x.FromJacobian(p)
default:
panic(fmt.Errorf("unexpected bls12381 point type (g1): %T", x))
a, okA := args[0].(*stackitem.Interop).Value().(blsPoint)
b, okB := args[1].(*stackitem.Interop).Value().(blsPoint)
if !(okA && okB) {
panic("some of the arguments are not a bls12381 point")
}
switch p := b.(type) {
case *bls12381.G2Affine:
y = p
case *bls12381.G2Jac:
y = new(bls12381.G2Affine)
y.FromJacobian(p)
default:
panic(fmt.Errorf("unexpected bls12381 point type (g2): %T", x))
}
gt, err := bls12381.Pair([]bls12381.G1Affine{*x}, []bls12381.G2Affine{*y})
p, err := blsPointPairing(a, b)
if err != nil {
panic(fmt.Errorf("failed to perform pairing operation"))
panic(err)
}
return stackitem.NewInterop(interface{}(&gt))
return stackitem.NewInterop(p)
}
// Metadata implements the Contract interface.

View file

@ -0,0 +1,293 @@
package native
import (
"errors"
"fmt"
"math/big"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// blsPoint is a wrapper around bls12381 point types that must be used as
// stackitem.Interop values and implement stackitem.Equatable interface.
type blsPoint struct {
point any
}
var _ = stackitem.Equatable(blsPoint{})
// Equals implements stackitem.Equatable interface.
func (p blsPoint) Equals(other stackitem.Equatable) bool {
res, err := p.EqualsCheckType(other)
return err == nil && res
}
// EqualsCheckType checks whether other is of the same type as p and returns an error if not.
// It also returns whether other and p are equal.
func (p blsPoint) EqualsCheckType(other stackitem.Equatable) (bool, error) {
b, ok := other.(blsPoint)
if !ok {
return false, errors.New("not a bls12-381 point")
}
var (
res bool
err error
)
switch x := p.point.(type) {
case *bls12381.G1Affine:
y, ok := b.point.(*bls12381.G1Affine)
if !ok {
err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G1Affine", y)
break
}
res = x.Equal(y)
case *bls12381.G1Jac:
y, ok := b.point.(*bls12381.G1Jac)
if !ok {
err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G1Jac", y)
break
}
res = x.Equal(y)
case *bls12381.G2Affine:
y, ok := b.point.(*bls12381.G2Affine)
if !ok {
err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G2Affine", y)
break
}
res = x.Equal(y)
case *bls12381.G2Jac:
y, ok := b.point.(*bls12381.G2Jac)
if !ok {
err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs G2Jac", y)
break
}
res = x.Equal(y)
case *bls12381.GT:
y, ok := b.point.(*bls12381.GT)
if !ok {
err = fmt.Errorf("equal: unexpected y bls12381 point type: %T vs GT", y)
break
}
res = x.Equal(y)
default:
err = fmt.Errorf("equal: unexpected x bls12381 point type: %T", x)
}
return res, err
}
// Bytes returns serialized representation of the provided point in compressed form.
func (p blsPoint) Bytes() []byte {
switch p := p.point.(type) {
case *bls12381.G1Affine:
compressed := p.Bytes()
return compressed[:]
case *bls12381.G1Jac:
g1Affine := new(bls12381.G1Affine)
g1Affine.FromJacobian(p)
compressed := g1Affine.Bytes()
return compressed[:]
case *bls12381.G2Affine:
compressed := p.Bytes()
return compressed[:]
case *bls12381.G2Jac:
g2Affine := new(bls12381.G2Affine)
g2Affine.FromJacobian(p)
compressed := g2Affine.Bytes()
return compressed[:]
case *bls12381.GT:
compressed := p.Bytes()
return compressed[:]
default:
panic(errors.New("unknown bls12381 point type"))
}
}
// FromBytes deserializes BLS12-381 point from the given byte slice in compressed form.
func (p *blsPoint) FromBytes(buf []byte) error {
switch l := len(buf); l {
case bls12381.SizeOfG1AffineCompressed:
g1Affine := new(bls12381.G1Affine)
_, err := g1Affine.SetBytes(buf)
if err != nil {
return fmt.Errorf("failed to decode bls12381 G1Affine point: %w", err)
}
p.point = g1Affine
case bls12381.SizeOfG2AffineCompressed:
g2Affine := new(bls12381.G2Affine)
_, err := g2Affine.SetBytes(buf)
if err != nil {
return fmt.Errorf("failed to decode bls12381 G2Affine point: %w", err)
}
p.point = g2Affine
case bls12381.SizeOfGT:
gt := new(bls12381.GT)
err := gt.SetBytes(buf)
if err != nil {
return fmt.Errorf("failed to decode GT point: %w", err)
}
p.point = gt
}
return nil
}
// blsPointAdd performs addition of two BLS12-381 points.
func blsPointAdd(a, b blsPoint) (blsPoint, error) {
var (
res any
err error
)
switch x := a.point.(type) {
case *bls12381.G1Affine:
switch y := b.point.(type) {
case *bls12381.G1Affine:
xJac := new(bls12381.G1Jac)
xJac.FromAffine(x)
xJac.AddMixed(y)
res = xJac
case *bls12381.G1Jac:
yJac := new(bls12381.G1Jac)
yJac.Set(y)
yJac.AddMixed(x)
res = yJac
default:
err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y)
}
case *bls12381.G1Jac:
resJac := new(bls12381.G1Jac)
resJac.Set(x)
switch y := b.point.(type) {
case *bls12381.G1Affine:
resJac.AddMixed(y)
case *bls12381.G1Jac:
resJac.AddAssign(y)
default:
err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y)
}
res = resJac
case *bls12381.G2Affine:
switch y := b.point.(type) {
case *bls12381.G2Affine:
xJac := new(bls12381.G2Jac)
xJac.FromAffine(x)
xJac.AddMixed(y)
res = xJac
case *bls12381.G2Jac:
yJac := new(bls12381.G2Jac)
yJac.Set(y)
yJac.AddMixed(x)
res = yJac
default:
err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y)
}
case *bls12381.G2Jac:
resJac := new(bls12381.G2Jac)
resJac.Set(x)
switch y := b.point.(type) {
case *bls12381.G2Affine:
resJac.AddMixed(y)
case *bls12381.G2Jac:
resJac.AddAssign(y)
default:
err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y)
}
res = resJac
case *bls12381.GT:
resGT := new(bls12381.GT)
resGT.Set(x)
switch y := b.point.(type) {
case *bls12381.GT:
// It's multiplication, see https://github.com/neo-project/Neo.Cryptography.BLS12_381/issues/4.
resGT.Mul(x, y)
default:
err = fmt.Errorf("add: inconsistent bls12381 point types: %T and %T", x, y)
}
res = resGT
default:
err = fmt.Errorf("add: unexpected bls12381 point type: %T", x)
}
return blsPoint{point: res}, err
}
// blsPointAdd performs scalar multiplication of two BLS12-381 points.
func blsPointMul(a blsPoint, alphaBi *big.Int) (blsPoint, error) {
var (
res any
err error
)
switch x := a.point.(type) {
case *bls12381.G1Affine:
// The result is in Jacobian form in the reference implementation.
g1Jac := new(bls12381.G1Jac)
g1Jac.FromAffine(x)
g1Jac.ScalarMultiplication(g1Jac, alphaBi)
res = g1Jac
case *bls12381.G1Jac:
g1Jac := new(bls12381.G1Jac)
g1Jac.ScalarMultiplication(x, alphaBi)
res = g1Jac
case *bls12381.G2Affine:
// The result is in Jacobian form in the reference implementation.
g2Jac := new(bls12381.G2Jac)
g2Jac.FromAffine(x)
g2Jac.ScalarMultiplication(g2Jac, alphaBi)
res = g2Jac
case *bls12381.G2Jac:
g2Jac := new(bls12381.G2Jac)
g2Jac.ScalarMultiplication(x, alphaBi)
res = g2Jac
case *bls12381.GT:
gt := new(bls12381.GT)
// C# implementation differs a bit from go's. They use double-and-add algorithm, see
// https://github.com/neo-project/Neo.Cryptography.BLS12_381/blob/844bc3a4f7d8ba2c545ace90ca124f8ada4c8d29/src/Neo.Cryptography.BLS12_381/Gt.cs#L102
// and https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add,
// Pay attention that C#'s Gt.Double() squares (not doubles!) the initial GT point.
// Thus.C#'s scalar multiplication operation over Gt and Scalar is effectively an exponent.
// Go's exponent algorithm differs a bit from the C#'s double-and-add in that go's one
// uses 2-bits windowed method for multiplication. However, the resulting GT point is
// absolutely the same between two implementations.
gt.Exp(*x, alphaBi)
res = gt
default:
err = fmt.Errorf("mul: unexpected bls12381 point type: %T", x)
}
return blsPoint{point: res}, err
}
func blsPointPairing(a, b blsPoint) (blsPoint, error) {
var (
x *bls12381.G1Affine
y *bls12381.G2Affine
)
switch p := a.point.(type) {
case *bls12381.G1Affine:
x = p
case *bls12381.G1Jac:
x = new(bls12381.G1Affine)
x.FromJacobian(p)
default:
return blsPoint{}, fmt.Errorf("pairing: unexpected bls12381 point type (g1): %T", x)
}
switch p := b.point.(type) {
case *bls12381.G2Affine:
y = p
case *bls12381.G2Jac:
y = new(bls12381.G2Affine)
y.FromJacobian(p)
default:
return blsPoint{}, fmt.Errorf("pairing: unexpected bls12381 point type (g2): %T", x)
}
gt, err := bls12381.Pair([]bls12381.G1Affine{*x}, []bls12381.G2Affine{*y})
if err != nil {
return blsPoint{}, fmt.Errorf("failed to perform pairing operation: %w", err)
}
return blsPoint{&gt}, nil
}

View file

@ -6,7 +6,6 @@ import (
"strings"
"testing"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/io"
@ -38,6 +37,10 @@ func newCryptolibClient(t *testing.T) *neotest.ContractInvoker {
return newNativeClient(t, nativenames.CryptoLib)
}
type serializable interface {
Bytes() []byte
}
func TestCryptolib_TestG1_Compat(t *testing.T) {
c := newCryptolibClient(t)
cryptoInvoker := c.WithSigners(c.Committee)
@ -48,9 +51,9 @@ func TestCryptolib_TestG1_Compat(t *testing.T) {
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.InteropT, itm.Type())
actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.G1Affine)
actual, ok := itm.(*stackitem.Interop).Value().(serializable)
require.True(t, ok)
arr := actual.Bytes() // the result in compressed form.
arr := actual.Bytes() // the G1Affine result in compressed form.
// Expected value is taken from the reference test.
require.Equal(t, "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb",
hex.EncodeToString(arr[:]))
@ -66,9 +69,9 @@ func TestCryptolib_TestG2_Compat(t *testing.T) {
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.InteropT, itm.Type())
actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.G2Affine)
actual, ok := itm.(*stackitem.Interop).Value().(serializable)
require.True(t, ok)
arr := actual.Bytes() // the result in compressed form.
arr := actual.Bytes() // the result G2Affine in compressed form.
// Expected value is taken from the reference test.
require.Equal(t, "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8",
hex.EncodeToString(arr[:]))
@ -102,10 +105,10 @@ func TestCryptolib_TestBls12381Add_Compat(t *testing.T) {
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.InteropT, itm.Type())
actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT)
actual, ok := itm.(*stackitem.Interop).Value().(serializable)
require.True(t, ok)
arr := actual.Bytes()
// Expected value is taken from the reference test.
// Expected GT value is taken from the reference test.
require.Equal(t, strings.ToLower("079AB7B345EB23C944C957A36A6B74C37537163D4CBF73BAD9751DE1DD9C68EF72CB21447E259880F72A871C3EDA1B0C017F1C95CF79B22B459599EA57E613E00CB75E35DE1F837814A93B443C54241015AC9761F8FB20A44512FF5CFC04AC7F0F6B8B52B2B5D0661CBF232820A257B8C5594309C01C2A45E64C6A7142301E4FB36E6E16B5A85BD2E437599D103C3ACE06D8046C6B3424C4CD2D72CE98D279F2290A28A87E8664CB0040580D0C485F34DF45267F8C215DCBCD862787AB555C7E113286DEE21C9C63A458898BEB35914DC8DAAAC453441E7114B21AF7B5F47D559879D477CF2A9CBD5B40C86BECD071280900410BB2751D0A6AF0FE175DCF9D864ECAAC463C6218745B543F9E06289922434EE446030923A3E4C4473B4E3B1914081ABD33A78D31EB8D4C1BB3BAAB0529BB7BAF1103D848B4CEAD1A8E0AA7A7B260FBE79C67DBE41CA4D65BA8A54A72B61692A61CE5F4D7A093B2C46AA4BCA6C4A66CF873D405EBC9C35D8AA639763720177B23BEFFAF522D5E41D3C5310EA3331409CEBEF9EF393AA00F2AC64673675521E8FC8FDDAF90976E607E62A740AC59C3DDDF95A6DE4FBA15BEB30C43D4E3F803A3734DBEB064BF4BC4A03F945A4921E49D04AB8D45FD753A28B8FA082616B4B17BBCB685E455FF3BF8F60C3BD32A0C185EF728CF41A1B7B700B7E445F0B372BC29E370BC227D443C70AE9DBCF73FEE8ACEDBD317A286A53266562D817269C004FB0F149DD925D2C590A960936763E519C2B62E14C7759F96672CD852194325904197B0B19C6B528AB33566946AF39B"),
hex.EncodeToString(arr[:]))
}
@ -132,10 +135,10 @@ func TestCryptolib_TestBls12381Mul_Compat(t *testing.T) {
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.InteropT, itm.Type())
actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT)
actual, ok := itm.(*stackitem.Interop).Value().(serializable)
require.True(t, ok)
arr := actual.Bytes()
// Expected value is taken from the reference test.
// Expected GT value is taken from the reference test.
require.Equal(t, strings.ToLower(expected), hex.EncodeToString(arr[:]))
}
check(t, false, "18B2DB6B3286BAEA116CCAD8F5554D170A69B329A6DE5B24C50B8834965242001A1C58089FD872B211ACD3263897FA660B117248D69D8AC745283A3E6A4CCEC607F6CF7CEDEE919575D4B7C8AE14C36001F76BE5FCA50ADC296EF8DF4926FA7F0B55A75F255FE61FC2DA7CFFE56ADC8775AAAB54C50D0C4952AD919D90FB0EB221C41ABB9F2352A11BE2D7F176ABE41E0E30AFB34FC2CE16136DE66900D92068F30011E9882C0A56E7E7B30F08442BE9E58D093E1888151136259D059FB539210D635BC491D5244A16CA28FDCF10546EC0F7104D3A419DDC081BA30ECB0CD2289010C2D385946229B7A9735ADC82736914FE61AD26C6C38B787775DE3B939105DE055F8D7004358272A0823F6F1787A7ABB6C3C59C8C9CBD1674AC900512632818CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6")
@ -156,10 +159,10 @@ func TestCryptolib_TestBls12381Pairing_Compat(t *testing.T) {
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.InteropT, itm.Type())
actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT)
actual, ok := itm.(*stackitem.Interop).Value().(serializable)
require.True(t, ok)
arr := actual.Bytes()
// Expected value is taken from the reference test.
// Expected GT value is taken from the reference test.
require.Equal(t, strings.ToLower("0F41E58663BF08CF068672CBD01A7EC73BACA4D72CA93544DEFF686BFD6DF543D48EAA24AFE47E1EFDE449383B67663104C581234D086A9902249B64728FFD21A189E87935A954051C7CDBA7B3872629A4FAFC05066245CB9108F0242D0FE3EF03350F55A7AEFCD3C31B4FCB6CE5771CC6A0E9786AB5973320C806AD360829107BA810C5A09FFDD9BE2291A0C25A99A211B8B424CD48BF38FCEF68083B0B0EC5C81A93B330EE1A677D0D15FF7B984E8978EF48881E32FAC91B93B47333E2BA5706FBA23EB7C5AF0D9F80940CA771B6FFD5857BAAF222EB95A7D2809D61BFE02E1BFD1B68FF02F0B8102AE1C2D5D5AB1A19F26337D205FB469CD6BD15C3D5A04DC88784FBB3D0B2DBDEA54D43B2B73F2CBB12D58386A8703E0F948226E47EE89D018107154F25A764BD3C79937A45B84546DA634B8F6BE14A8061E55CCEBA478B23F7DACAA35C8CA78BEAE9624045B4B601B2F522473D171391125BA84DC4007CFBF2F8DA752F7C74185203FCCA589AC719C34DFFBBAAD8431DAD1C1FB597AAA5193502B86EDB8857C273FA075A50512937E0794E1E65A7617C90D8BD66065B1FFFE51D7A579973B1315021EC3C19934F1368BB445C7C2D209703F239689CE34C0378A68E72A6B3B216DA0E22A5031B54DDFF57309396B38C881C4C849EC23E87089A1C5B46E5110B86750EC6A532348868A84045483C92B7AF5AF689452EAFABF1A8943E50439F1D59882A98EAA0170F1250EBD871FC0A92A7B2D83168D0D727272D441BEFA15C503DD8E90CE98DB3E7B6D194F60839C508A84305AACA1789B6"),
hex.EncodeToString(arr[:]))
}
@ -313,30 +316,10 @@ func TestCryptolib_TestBls12381Mul_CompatCustom(t *testing.T) {
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.InteropT, itm.Type())
var actual []byte
switch resTyp {
case gtP:
gt, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT)
p, ok := itm.(*stackitem.Interop).Value().(serializable)
require.True(t, ok)
arr := gt.Bytes()
actual = arr[:]
case g1JacP:
g1Jac, ok := itm.(*stackitem.Interop).Value().(*bls12381.G1Jac)
require.True(t, ok)
g1Affine := new(bls12381.G1Affine)
g1Affine.FromJacobian(g1Jac)
arr := g1Affine.Bytes()
actual = arr[:]
case g2JacP:
g2Jac, ok := itm.(*stackitem.Interop).Value().(*bls12381.G2Jac)
require.True(t, ok)
g2Affine := new(bls12381.G2Affine)
g2Affine.FromJacobian(g2Jac)
arr := g2Affine.Bytes()
actual = arr[:]
default:
t.Fatal("unexpected result type", resTyp)
}
arr := p.Bytes()
actual := arr[:]
require.Equal(t, strings.ToLower(expected), hex.EncodeToString(actual))
}
@ -350,3 +333,90 @@ func TestCryptolib_TestBls12381Mul_CompatCustom(t *testing.T) {
})
}
}
func TestCryptolib_Bls12381PointsEQUAL_DUP(t *testing.T) {
c := newCryptolibClient(t)
check := func(t *testing.T, point string) {
bytes, err := hex.DecodeString(point)
require.NoError(t, err)
script := io.NewBufBinWriter()
emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, bytes)
emit.Opcodes(script.BinWriter, opcode.DUP, opcode.EQUAL)
c.InvokeScriptCheckHALT(t, script.Bytes(), c.Signers, stackitem.NewBool(true))
}
t.Run("GT", func(t *testing.T) {
check(t, "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62")
})
t.Run("G1", func(t *testing.T) {
check(t, "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8")
})
t.Run("G2", func(t *testing.T) {
check(t, "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365")
})
}
func TestCryptolib_Bls12381PointsEQUAL(t *testing.T) {
c := newCryptolibClient(t)
check := func(t *testing.T, point1, point2 string, expected bool) {
bytes1, err := hex.DecodeString(point1)
require.NoError(t, err)
bytes2, err := hex.DecodeString(point2)
require.NoError(t, err)
script := io.NewBufBinWriter()
emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, bytes1)
emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, bytes2)
emit.Opcodes(script.BinWriter, opcode.EQUAL)
c.InvokeScriptCheckHALT(t, script.Bytes(), c.Signers, stackitem.NewBool(expected))
}
t.Run("GT true", func(t *testing.T) {
check(t, "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62",
"14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62",
true)
})
t.Run("GT false", func(t *testing.T) {
check(t, "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62",
hex.EncodeToString(gt),
false)
})
t.Run("G1 true", func(t *testing.T) {
check(t, "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8",
"a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8",
true)
})
t.Run("G1 false", func(t *testing.T) {
check(t, "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8",
hex.EncodeToString(g1),
false)
})
t.Run("G2 true", func(t *testing.T) {
check(t, "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365",
"a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365",
true)
})
t.Run("G2 false", func(t *testing.T) {
check(t, "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365",
hex.EncodeToString(g2),
false)
})
}
func TestCryptolib_Bls12381Equal_GT(t *testing.T) {
c := newCryptolibClient(t)
script := io.NewBufBinWriter()
emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt)
emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt)
emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PACK)
emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Equal", callflag.All)
stack, err := c.TestInvokeScript(t, script.Bytes(), c.Signers)
require.NoError(t, err)
require.Equal(t, 1, stack.Len())
itm := stack.Pop().Item()
require.Equal(t, stackitem.BooleanT, itm.Type())
require.True(t, itm.Value().(bool))
}

View file

@ -60,6 +60,13 @@ type Convertible interface {
FromStackItem(Item) error
}
// Equatable describes a special value of Interop that can be compared with
// value of some other Interop that implements Equatable.
type Equatable interface {
// Equals checks if two objects are equal.
Equals(other Equatable) bool
}
var (
// ErrInvalidConversion is returned upon an attempt to make an incorrect
// conversion between item types.
@ -994,7 +1001,12 @@ func (i *Interop) Equals(s Item) bool {
return false
}
val, ok := s.(*Interop)
return ok && i.value == val.value
if !ok {
return false
}
a, okA := i.value.(Equatable)
b, okB := val.value.(Equatable)
return (okA && okB && a.Equals(b)) || (!okA && !okB && i.value == val.value)
}
// Type implements the Item interface.

View file

@ -820,11 +820,23 @@ func TestDepth(t *testing.T) {
assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64())
}
type simpleEquatable struct {
ok bool
}
var _ = stackitem.Equatable(simpleEquatable{})
func (e simpleEquatable) Equals(other stackitem.Equatable) bool {
_, ok := other.(simpleEquatable)
return ok && e.ok
}
func TestEQUALTrue(t *testing.T) {
prog := makeProgram(opcode.DUP, opcode.EQUAL)
t.Run("Array", getTestFuncForVM(prog, true, []stackitem.Item{}))
t.Run("Map", getTestFuncForVM(prog, true, stackitem.NewMap()))
t.Run("Buffer", getTestFuncForVM(prog, true, stackitem.NewBuffer([]byte{1, 2})))
t.Run("Equatable", getTestFuncForVM(prog, true, stackitem.NewInterop(simpleEquatable{ok: true})))
}
func TestEQUAL(t *testing.T) {
@ -837,6 +849,8 @@ func TestEQUAL(t *testing.T) {
t.Run("Map", getTestFuncForVM(prog, false, stackitem.NewMap(), stackitem.NewMap()))
t.Run("Array", getTestFuncForVM(prog, false, []stackitem.Item{}, []stackitem.Item{}))
t.Run("Buffer", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{42}), stackitem.NewBuffer([]byte{42})))
t.Run("EquatableFalse", getTestFuncForVM(prog, false, stackitem.NewInterop(simpleEquatable{false}), stackitem.NewInterop(simpleEquatable{})))
t.Run("EquatableTrue", getTestFuncForVM(prog, true, stackitem.NewInterop(simpleEquatable{true}), stackitem.NewInterop(simpleEquatable{})))
}
func TestEQUALByteArrayWithLimit(t *testing.T) {