From 115ec4d8dd885a5e267357770a118477d4f01c2d Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 17 Mar 2023 18:38:38 +0300 Subject: [PATCH] core: add BLS12_381 interops --- go.mod | 4 + go.sum | 11 + pkg/core/native/crypto.go | 320 ++++++++++++++++ pkg/core/native/crypto_test.go | 61 +++ pkg/core/native/native_test/cryptolib_test.go | 352 ++++++++++++++++++ pkg/neotest/basic.go | 7 +- pkg/neotest/client.go | 23 +- pkg/services/rpcsrv/server_test.go | 2 +- 8 files changed, 777 insertions(+), 3 deletions(-) create mode 100644 pkg/core/native/native_test/cryptolib_test.go diff --git a/go.mod b/go.mod index cfe1c61c5..1f5255334 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,7 @@ module github.com/nspcc-dev/neo-go require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e + github.com/consensys/gnark-crypto v0.9.1 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/google/uuid v1.2.0 @@ -36,10 +37,12 @@ require ( github.com/benbjohnson/clock v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/consensys/bavard v0.1.13 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/nspcc-dev/hrw v1.0.9 // indirect github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 // indirect github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect @@ -58,6 +61,7 @@ require ( google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect google.golang.org/grpc v1.41.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) go 1.18 diff --git a/go.sum b/go.sum index eaad5aa6b..0b24180df 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.9.1 h1:mru55qKdWl3E035hAoh1jj9d7hVnYY5pfb6tmovSmII= +github.com/consensys/gnark-crypto v0.9.1/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjzGFRUN2sg06VuU4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -183,6 +187,7 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -226,6 +231,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -235,6 +241,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -729,3 +738,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/pkg/core/native/crypto.go b/pkg/core/native/crypto.go index 0228bc9cc..323a8ed21 100644 --- a/pkg/core/native/crypto.go +++ b/pkg/core/native/crypto.go @@ -5,7 +5,10 @@ import ( "encoding/binary" "errors" "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/interop" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -61,6 +64,42 @@ func newCrypto() *Crypto { manifest.NewParameter("curve", smartcontract.IntegerType)) md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag) c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Serialize", smartcontract.ByteArrayType, + manifest.NewParameter("g", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Serialize, 1<<19, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Deserialize", smartcontract.InteropInterfaceType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md = newMethodAndPrice(c.bls12381Deserialize, 1<<19, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Equal", smartcontract.BoolType, + manifest.NewParameter("x", smartcontract.InteropInterfaceType), + manifest.NewParameter("y", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Equal, 1<<5, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Add", smartcontract.InteropInterfaceType, + manifest.NewParameter("x", smartcontract.InteropInterfaceType), + manifest.NewParameter("y", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Add, 1<<19, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Mul", smartcontract.InteropInterfaceType, + manifest.NewParameter("x", smartcontract.InteropInterfaceType), + manifest.NewParameter("mul", smartcontract.ByteArrayType), + manifest.NewParameter("neg", smartcontract.BoolType)) + md = newMethodAndPrice(c.bls12381Mul, 1<<21, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("bls12381Pairing", smartcontract.InteropInterfaceType, + manifest.NewParameter("g1", smartcontract.InteropInterfaceType), + manifest.NewParameter("g2", smartcontract.InteropInterfaceType)) + md = newMethodAndPrice(c.bls12381Pairing, 1<<23, callflag.NoneFlag) + c.AddMethod(md, desc) + return c } @@ -137,6 +176,287 @@ 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")) + } + return stackitem.NewByteArray(res) +} + +func (c *Crypto) bls12381Deserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + buf, err := args[0].TryBytes() + 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) + if err != nil { + panic(fmt.Errorf("failed to decode bls12381 G1Affine point: %w", 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) +} + +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")) + } + 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)) + } + 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") + } + 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") + } + 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) +} + +func scalarFromBytes(bytes []byte, neg bool) (*fr.Element, error) { + alpha := new(fr.Element) + if len(bytes) != fr.Bytes { + return nil, fmt.Errorf("invalid multiplier: 32-bytes scalar is expected, got %d", len(bytes)) + } + // The input bytes are in the LE form, so we can't use fr.Element.SetBytesCanonical as far + // as it accepts BE. + v, err := fr.LittleEndian.Element((*[fr.Bytes]byte)(bytes)) + if err != nil { + return nil, fmt.Errorf("invalid multiplier: failed to decode scalar: %w", err) + } + *alpha = v + if neg { + alpha.Neg(alpha) + } + return alpha, nil +} + +func (c *Crypto) bls12381Mul(_ *interop.Context, args []stackitem.Item) stackitem.Item { + a := args[0].(*stackitem.Interop).Value() + mulBytes, err := args[1].TryBytes() + if err != nil { + panic(fmt.Errorf("invalid multiplier: %w", err)) + } + neg, err := args[2].TryBool() + if err != nil { + panic(fmt.Errorf("invalid negative argument: %w", err)) + } + alpha, err := scalarFromBytes(mulBytes, neg) + if err != nil { + panic(err) + } + 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)) + } + return stackitem.NewInterop(res) +} + +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)) + } + 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}) + if err != nil { + panic(fmt.Errorf("failed to perform pairing operation")) + } + return stackitem.NewInterop(interface{}(>)) +} + // Metadata implements the Contract interface. func (c *Crypto) Metadata() *interop.ContractMD { return &c.ContractMD diff --git a/pkg/core/native/crypto_test.go b/pkg/core/native/crypto_test.go index 11cc87db7..47688e2c2 100644 --- a/pkg/core/native/crypto_test.go +++ b/pkg/core/native/crypto_test.go @@ -7,6 +7,7 @@ import ( "math/big" "testing" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -141,3 +142,63 @@ func testECDSAVerify(t *testing.T, curve NamedCurve) { runCase(t, false, true, msg, priv.PublicKey().Bytes(), sign, int64(curve)) }) } + +func TestCryptolib_ScalarFromBytes_Compat(t *testing.T) { + r2Ref := &fr.Element{ + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11, + } // R2 Scalar representation taken from the https://github.com/neo-project/Neo.Cryptography.BLS12_381/blob/844bc3a4f7d8ba2c545ace90ca124f8ada4c8d29/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs#L55 + + tcs := map[string]struct { + bytes []byte + expected *fr.Element + shouldFail bool + }{ + "zero": { + bytes: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expected: new(fr.Element).SetZero(), + }, + "one": { + bytes: []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + expected: new(fr.Element).SetOne(), + }, + "R2": { + bytes: []byte{254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24}, + expected: r2Ref, + }, + "negative": { + bytes: []byte{0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + }, + "modulus": { + bytes: []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + shouldFail: true, + }, + "larger than modulus": { + bytes: []byte{2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + shouldFail: true, + }, + "larger than modulus 2": { + bytes: []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115}, + shouldFail: true, + }, + "larger than modulus 3": { + bytes: []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116}, + shouldFail: true, + }, + } + for name, tc := range tcs { + t.Run(name, func(t *testing.T) { + actual, err := scalarFromBytes(tc.bytes, false) + if tc.shouldFail { + require.Error(t, err) + } else { + require.NoError(t, err) + if tc.expected != nil { + require.Equal(t, tc.expected, actual) + } + } + }) + } +} diff --git a/pkg/core/native/native_test/cryptolib_test.go b/pkg/core/native/native_test/cryptolib_test.go new file mode 100644 index 000000000..bccda21a2 --- /dev/null +++ b/pkg/core/native/native_test/cryptolib_test.go @@ -0,0 +1,352 @@ +package native_test + +import ( + "encoding/hex" + "strconv" + "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" + "github.com/nspcc-dev/neo-go/pkg/neotest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +// A set of points taken from the reference implementation for compatibility tests. +var ( + g1 []byte + g2 []byte + gt []byte + notG1, notG2 []byte +) + +func init() { + g1, _ = hex.DecodeString("97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb") + g2, _ = hex.DecodeString("93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8") + gt, _ = hex.DecodeString("0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b67663104c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a211b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba5706fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b601b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6") + notG1, _ = hex.DecodeString("8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") + notG2, _ = hex.DecodeString("8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") +} + +func newCryptolibClient(t *testing.T) *neotest.ContractInvoker { + return newNativeClient(t, nativenames.CryptoLib) +} + +func TestCryptolib_TestG1_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + // Use testinvoke because the return value of this method is InteropInterface. + stack, err := cryptoInvoker.TestInvoke(t, "bls12381Deserialize", g1) + require.NoError(t, err) + 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) + require.True(t, ok) + arr := actual.Bytes() // the result in compressed form. + // Expected value is taken from the reference test. + require.Equal(t, "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestG2_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + // Use testinvoke because the return value of this method is InteropInterface. + stack, err := cryptoInvoker.TestInvoke(t, "bls12381Deserialize", g2) + require.NoError(t, err) + 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) + require.True(t, ok) + arr := actual.Bytes() // the result in compressed form. + // Expected value is taken from the reference test. + require.Equal(t, "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestNotG1_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + cryptoInvoker.InvokeFail(t, "failed to decode bls12381 G1Affine point: invalid point: subgroup check failed", "bls12381Deserialize", notG1) +} + +func TestCryptolib_TestNotG2_Compat(t *testing.T) { + c := newCryptolibClient(t) + cryptoInvoker := c.WithSigners(c.Committee) + + cryptoInvoker.InvokeFail(t, "failed to decode bls12381 G2Affine point: invalid point: subgroup check failed", "bls12381Deserialize", notG2) +} + +func TestCryptolib_TestBls12381Add_Compat(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, "bls12381Add", 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.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT) + require.True(t, ok) + arr := actual.Bytes() + // Expected value is taken from the reference test. + require.Equal(t, strings.ToLower("079AB7B345EB23C944C957A36A6B74C37537163D4CBF73BAD9751DE1DD9C68EF72CB21447E259880F72A871C3EDA1B0C017F1C95CF79B22B459599EA57E613E00CB75E35DE1F837814A93B443C54241015AC9761F8FB20A44512FF5CFC04AC7F0F6B8B52B2B5D0661CBF232820A257B8C5594309C01C2A45E64C6A7142301E4FB36E6E16B5A85BD2E437599D103C3ACE06D8046C6B3424C4CD2D72CE98D279F2290A28A87E8664CB0040580D0C485F34DF45267F8C215DCBCD862787AB555C7E113286DEE21C9C63A458898BEB35914DC8DAAAC453441E7114B21AF7B5F47D559879D477CF2A9CBD5B40C86BECD071280900410BB2751D0A6AF0FE175DCF9D864ECAAC463C6218745B543F9E06289922434EE446030923A3E4C4473B4E3B1914081ABD33A78D31EB8D4C1BB3BAAB0529BB7BAF1103D848B4CEAD1A8E0AA7A7B260FBE79C67DBE41CA4D65BA8A54A72B61692A61CE5F4D7A093B2C46AA4BCA6C4A66CF873D405EBC9C35D8AA639763720177B23BEFFAF522D5E41D3C5310EA3331409CEBEF9EF393AA00F2AC64673675521E8FC8FDDAF90976E607E62A740AC59C3DDDF95A6DE4FBA15BEB30C43D4E3F803A3734DBEB064BF4BC4A03F945A4921E49D04AB8D45FD753A28B8FA082616B4B17BBCB685E455FF3BF8F60C3BD32A0C185EF728CF41A1B7B700B7E445F0B372BC29E370BC227D443C70AE9DBCF73FEE8ACEDBD317A286A53266562D817269C004FB0F149DD925D2C590A960936763E519C2B62E14C7759F96672CD852194325904197B0B19C6B528AB33566946AF39B"), + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestBls12381Mul_Compat(t *testing.T) { + c := newCryptolibClient(t) + + check := func(t *testing.T, neg bool, expected string) { + data := make([]byte, fr.Bytes) + data[0] = 0x03 + script := io.NewBufBinWriter() + if neg { + emit.Opcodes(script.BinWriter, opcode.PUSHT) + } else { + emit.Opcodes(script.BinWriter, opcode.PUSHF) + } + emit.Bytes(script.BinWriter, data) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gt) + emit.Opcodes(script.BinWriter, opcode.PUSH3, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Mul", 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.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT) + require.True(t, ok) + arr := actual.Bytes() + // Expected value is taken from the reference test. + require.Equal(t, strings.ToLower(expected), hex.EncodeToString(arr[:])) + } + check(t, false, "18B2DB6B3286BAEA116CCAD8F5554D170A69B329A6DE5B24C50B8834965242001A1C58089FD872B211ACD3263897FA660B117248D69D8AC745283A3E6A4CCEC607F6CF7CEDEE919575D4B7C8AE14C36001F76BE5FCA50ADC296EF8DF4926FA7F0B55A75F255FE61FC2DA7CFFE56ADC8775AAAB54C50D0C4952AD919D90FB0EB221C41ABB9F2352A11BE2D7F176ABE41E0E30AFB34FC2CE16136DE66900D92068F30011E9882C0A56E7E7B30F08442BE9E58D093E1888151136259D059FB539210D635BC491D5244A16CA28FDCF10546EC0F7104D3A419DDC081BA30ECB0CD2289010C2D385946229B7A9735ADC82736914FE61AD26C6C38B787775DE3B939105DE055F8D7004358272A0823F6F1787A7ABB6C3C59C8C9CBD1674AC900512632818CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6") + check(t, true, "014E367F06F92BB039AEDCDD4DF65FC05A0D985B4CA6B79AA2254A6C605EB424048FA7F6117B8D4DA8522CD9C767B0450EEF9FA162E25BD305F36D77D8FEDE115C807C0805968129F15C1AD8489C32C41CB49418B4AEF52390900720B6D8B02C0EAB6A8B1420007A88412AB65DE0D04FEECCA0302E7806761483410365B5E771FCE7E5431230AD5E9E1C280E8953C68D0BD06236E9BD188437ADC14D42728C6E7177399B6B5908687F491F91EE6CCA3A391EF6C098CBEAEE83D962FA604A718A0C9DB625A7AAC25034517EB8743B5868A3803B37B94374E35F152F922BA423FB8E9B3D2B2BBF9DD602558CA5237D37420502B03D12B9230ED2A431D807B81BD18671EBF78380DD3CF490506187996E7C72F53C3914C76342A38A536FFAED478318CDD273F0D38833C07467EAF77743B70C924D43975D3821D47110A358757F926FCF970660FBDD74EF15D93B81E3AA290C78F59CBC6ED0C1E0DCBADFD11A73EB7137850D29EFEB6FA321330D0CF70F5C7F6B004BCF86AC99125F8FECF83157930BEC2AF89F8B378C6D7F63B0A07B3651F5207A84F62CEE929D574DA154EBE795D519B661086F069C9F061BA3B53DC4910EA1614C87B114E2F9EF328AC94E93D00440B412D5AE5A3C396D52D26C0CDF2156EBD3D3F60EA500C42120A7CE1F7EF80F15323118956B17C09E80E96ED4E1572461D604CDE2533330C684F86680406B1D3EE830CBAFE6D29C9A0A2F41E03E26095B713EB7E782144DB1EC6B53047FCB606B7B665B3DD1F52E95FCF2AE59C4AB159C3F98468C0A43C36C022B548189B6") +} + +func TestCryptolib_TestBls12381Pairing_Compat(t *testing.T) { + c := newCryptolibClient(t) + + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g2) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g1) + emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Pairing", 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.InteropT, itm.Type()) + actual, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT) + require.True(t, ok) + arr := actual.Bytes() + // Expected value is taken from the reference test. + require.Equal(t, strings.ToLower("0F41E58663BF08CF068672CBD01A7EC73BACA4D72CA93544DEFF686BFD6DF543D48EAA24AFE47E1EFDE449383B67663104C581234D086A9902249B64728FFD21A189E87935A954051C7CDBA7B3872629A4FAFC05066245CB9108F0242D0FE3EF03350F55A7AEFCD3C31B4FCB6CE5771CC6A0E9786AB5973320C806AD360829107BA810C5A09FFDD9BE2291A0C25A99A211B8B424CD48BF38FCEF68083B0B0EC5C81A93B330EE1A677D0D15FF7B984E8978EF48881E32FAC91B93B47333E2BA5706FBA23EB7C5AF0D9F80940CA771B6FFD5857BAAF222EB95A7D2809D61BFE02E1BFD1B68FF02F0B8102AE1C2D5D5AB1A19F26337D205FB469CD6BD15C3D5A04DC88784FBB3D0B2DBDEA54D43B2B73F2CBB12D58386A8703E0F948226E47EE89D018107154F25A764BD3C79937A45B84546DA634B8F6BE14A8061E55CCEBA478B23F7DACAA35C8CA78BEAE9624045B4B601B2F522473D171391125BA84DC4007CFBF2F8DA752F7C74185203FCCA589AC719C34DFFBBAAD8431DAD1C1FB597AAA5193502B86EDB8857C273FA075A50512937E0794E1E65A7617C90D8BD66065B1FFFE51D7A579973B1315021EC3C19934F1368BB445C7C2D209703F239689CE34C0378A68E72A6B3B216DA0E22A5031B54DDFF57309396B38C881C4C849EC23E87089A1C5B46E5110B86750EC6A532348868A84045483C92B7AF5AF689452EAFABF1A8943E50439F1D59882A98EAA0170F1250EBD871FC0A92A7B2D83168D0D727272D441BEFA15C503DD8E90CE98DB3E7B6D194F60839C508A84305AACA1789B6"), + hex.EncodeToString(arr[:])) +} + +func TestCryptolib_TestBls12381Equal_Compat(t *testing.T) { + c := newCryptolibClient(t) + + script := io.NewBufBinWriter() + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g1) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, g1) + 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)) +} + +// pointType represents one of the BLS12-381 point types and is used for tests only. +type pointType byte + +const ( + g1JacP pointType = iota + g2JacP + gtP +) + +// Data used for this test are generated by C# library and taken from the reference +// tests. +func TestCryptolib_TestBls12381Mul_CompatCustom(t *testing.T) { + tcs := []struct { + name string + point string // serialized hex-encoded point representation. + mul string // serialized hex-encoded Scalar multiplier representation. + neg bool // whether Scalar must be negative. + expectedTyp pointType // point type of the expected result. + expected string // serialized hex-encoded result of (point * mul) multiplication taken from the reference implementation. + }{ + { + name: "GT mul by positive scalar", + point: "14fd52fe9bfd08bbe23fcdf1d3bc5390c62e75a8786a72f8a343123a30a7c5f8d18508a21a2bf902f4db2c068913bc1c130e7ce13260d601c89ee717acfd3d4e1d80f409dd2a5c38b176f0b64d3d0a224c502717270dfecf2b825ac24608215c0d7fcfdf3c1552ada42b7e0521bc2e7389436660c352ecbf2eedf30b77b6b501df302399e6240473af47abe56fc974780c214542fcc0cf10e3001fa5e82d398f6ba1ddd1ccdf133bfd75e033eae50aec66bd5e884b8c74d4c1c6ac7c01278ac5164a54600cb2e24fec168f82542fbf98234dbb9ddf06503dc3c497da88b73db584ba19e685b1b398b51f40160e6c8f0917b4a68dedcc04674e5f5739cf0d845ba801263f712ed4ddda59c1d9909148e3f28124ae770682c9b19233bf0bcfa00d05bfe708d381b066b83a883ba8251ce2ea6772cbde51e1322d82b2c8a026a2153f4822e20cb69b8b05003ee74e09cb481728d688caa8a671f90b55488e272f48c7c5ae32526d3635a5343eb02640358d9ac445c76a5d8f52f653bbaee04ba5ce03c68b88c25be6fd3611cc21c9968e4f87e541beeccc5170b8696a439bb666ad8a6608ab30ebc7dfe56eaf0dd9ab8439171a6e4e0d608e6e6c8ac5ddcf8d6d2a950d06051e6b6c4d3feb6dc8dac2acadd345cadfb890454a2101a112f7471f0e001701f60f3d4352c4d388c0f198854908c0e939719709c1b3f82d2a25cc7156a3838bc141e041c259849326fbd0839f15cea6a78b89349dcd1c03695a74e72d3657af4ee2cf267337bc96363ef4a1c5d5d7a673cc3a3c1a1350043f99537d62", + mul: "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + neg: false, + expectedTyp: gtP, + expected: "03dc980ce0c037634816f9fc1edb2e1807e38a51f838e3a684f195d6c52c41d6a8a5b64d57d3fda507bebe3bd4b661af0e4f7c46754b373c955982b4d64a24838cbc010d04b6ceb499bf411d114dab77eaf70f96ab66c2868dcd63706b602b07010c487fc16c90b61e1c2ad33c31c8f3fc86d114a59b127ac584640f149f3597102c55dd1ed8a305a10c052c0a724e570fc079e410123735a6144ccd88d9e4e91d7b889f80b18a1741eacd6f244fce3cf57795e619b6648b9238053b4b8e4ed6115c905fbcb61525370667ff43144e12b700662a7344ac1af97f11d09779ca6865973f95ff318b42ff00df7c6eb958160947a0ab6cb25534af51ce1f0b076907c6eb5ce0760bd7670cab8814cc3308766eb6e52b5427dbf85d6424990fd3354515ab880358bc55075a08f36b855694c02ee0bd63adefe235ba4ee41dc600a1cae950c1dc760bf7b1edd8712e9e90eebb19de705e29f4feb870129441bd4b9e91c3d37e60c12fa79a5b1e4132ba9498044e6fbf2de37e4dd88b4e9095b46f122019e73a561ba3967b32813c3ec74b8e1b6ab619eeab698e6638114cb29ca9c3d353192db3d392fee2b4dfdfd36b13db440534dd754417cffcd470f4d4cfdcb6d7896181c27b8b30622d7a4ca0a05a7ea67ca011cab07738235b115bbd330239691487d2de5d679a8cad2fe5c7fff16b0b0f3f929619c8005289c3d7ffe5bcd5ea19651bfc9366682a2790cab45ee9a98815bb7e58dc666e2209cd9d700546cf181ceb43fe719243930984b696b0d18d4cd1f5d960e149a2b753b1396e4f8f3b16", + }, + { + name: "GT mul by positive scalar", + point: "0e0c651ff4a57adebab1fa41aa8d1e53d1cf6a6cc554282a24bb460ea0dc169d3ede8b5a93a331698f3926d273a729aa18788543413f43ada55a6a7505e3514f0db7e14d58311c3211962a350bcf908b3af90fbae31ff536fe542328ad25cd3e044a796200c8a8ead7edbc3a8a37209c5d37433ca7d8b0e644d7aac9726b524c41fef1cf0d546c252d795dffc445ddee07041f57c4c9a673bd314294e280ab61390731c09ad904bdd7b8c087d0ce857ea86e78f2d98e75d9b5e377e5751d67cf1717cbce31bc7ea6df95132549bf6d284a68005c53228127671afa54ecfd4c5c4debc437c4c6d9b9aeeee8b4159a5691128c6dc68b309fd822b14f3ce8ff390bd6834d30147e8ab2edc59d0d7b14cc13c79e6eed5fd6cae1795ba3760345d59c0c585f79c900902515e3e95938d9929ad8310e71fc7fd54be9c7529f244af40dadaca0b3bd8afd911f24b261079de48b161dd8f340d42bd84e717275193a0375d9e10fbe048bbea30abd64d3fe085c15b9be192f7baaa0b3a9658bcbb4292a0c0149beb30e54b065a75df45e5da77583f4471e3454cea90a00b5a9a224c15e2ebe01f0ab8aa86591c1012c618d41fdce07ecfcaddc8dc408b7176b79d8711a4161a56f41a5be6714cbcaa70e53387ab049826ac9e636640bc6da919e52f86f3209572b62d9bfd48bd2b5ef217932237b90a70d40167623d0f25a73b753e3214310bc5b6e017aebc1a9ca0c8067a97da6162c70cc754f1b2ac3b05ba834712758c8de4641ef09237edf588989182ab3047ee42da2b840fd3633fa0f34d46ad961", + mul: "06c93a0ebbc8b5cd3af798b8f72442a67aa885b395452a08e48ec80b4e9f1b3f", + neg: false, + expectedTyp: gtP, + expected: "0d6d91f120ab61e14a3163601ce584f053f1de9dc0a548b6fbf37a776ec7b6ce6b866e8c8b0fc0ac8d32a9a9747c98bf0e6aee5bddd058313958bfc3ac1ed75284628f92bb9b99fee101e1bee9d74bad7812287ea76bdbe07f20ff9998d6e9f016689be1cfc4337433644a679945d5c34a6d4dd984c56d6c28428438268b385cb1d86f69b0377b18f9b084e1d0b6596213233d559a1b5caaba38be853f667fc3b1f9f2c4c9020584502ff5f370b0aba7768a1a4ca4328bc3c7be2bc9c3949f5e16fd3bfc16b11da41b7393e56e777640b000db15b6e6192e5c59dfece90c6fc0b6071fdeef7061974b5e967c5b88b1db09f7c92077c16f56aff9e9627f5e09928e965daee17d05ef3fdc0c502b649db473b5b2bba867d829b04d32cfeab7387614190b265382378f75e4e085a5537d4f200fe56b74b7c52c5546b30d51862e1ac1f60eba157880090a42ea9b0295529f134c1fc90f19a4c20dc0be105b07e0c67218b2f5619a66d8d770d539658eb74c255743e5847bc437fef3077d0a6c4f17198d63cf17e6957f2ad9449269af009635697e92254a3f67be9b8760fd9f974826a1829fedb4cf66968b7c63b0c88c510da12e6d52255256757afa03ad29b5c1624292ef7eb463eb4bc81ac7426f36db3fe1513bdd31bc138bfe903bbb0c5207001335f708c16cea15ef6b77c3215326a779e927b8c2081b15adffe71ba75164e376665533c5bb59373b27dbe93a0a0e1796d821a1b9ff01846446c5ad53064cb9b941f97aa870285395e1a44c9f6e5144ea5a0cf57b9fdd962a5ec3ff1f72fe", + }, + { + name: "GT mul by positive scalar", + point: "0e0c651ff4a57adebab1fa41aa8d1e53d1cf6a6cc554282a24bb460ea0dc169d3ede8b5a93a331698f3926d273a729aa18788543413f43ada55a6a7505e3514f0db7e14d58311c3211962a350bcf908b3af90fbae31ff536fe542328ad25cd3e044a796200c8a8ead7edbc3a8a37209c5d37433ca7d8b0e644d7aac9726b524c41fef1cf0d546c252d795dffc445ddee07041f57c4c9a673bd314294e280ab61390731c09ad904bdd7b8c087d0ce857ea86e78f2d98e75d9b5e377e5751d67cf1717cbce31bc7ea6df95132549bf6d284a68005c53228127671afa54ecfd4c5c4debc437c4c6d9b9aeeee8b4159a5691128c6dc68b309fd822b14f3ce8ff390bd6834d30147e8ab2edc59d0d7b14cc13c79e6eed5fd6cae1795ba3760345d59c0c585f79c900902515e3e95938d9929ad8310e71fc7fd54be9c7529f244af40dadaca0b3bd8afd911f24b261079de48b161dd8f340d42bd84e717275193a0375d9e10fbe048bbea30abd64d3fe085c15b9be192f7baaa0b3a9658bcbb4292a0c0149beb30e54b065a75df45e5da77583f4471e3454cea90a00b5a9a224c15e2ebe01f0ab8aa86591c1012c618d41fdce07ecfcaddc8dc408b7176b79d8711a4161a56f41a5be6714cbcaa70e53387ab049826ac9e636640bc6da919e52f86f3209572b62d9bfd48bd2b5ef217932237b90a70d40167623d0f25a73b753e3214310bc5b6e017aebc1a9ca0c8067a97da6162c70cc754f1b2ac3b05ba834712758c8de4641ef09237edf588989182ab3047ee42da2b840fd3633fa0f34d46ad961", + mul: "b0010000000000005e0000000000000071f30400000000006d9189c813000000", + neg: false, + expectedTyp: gtP, + expected: "0919ad29cdbe0b6bbd636fbe3c8930a1b959e5aa37294a6cc7d018e2776580768bb98bf91ce1bc97f2e6fa647e7dad7b15db564645d2e4868129ed414b7e369e831b8ff93997a22b6ca0e2ba288783f535aed4b44cf3e952897db1536da18a120a70da2b9dd901bd12a5a7047d3b6346ba1aea53b642b7355a91f957687fccd840ef24af100d0ada6b49e35183456ec30b505098526b975477b6ca0273d3a841c85e4a8319b950e76ec217a4f939844baa6b875a4046a30c618636fe9b25c620030f31044f883789945c2bcb75d7d4099b2bc97665e75c1bee27bc3864e7e5e2ccb57a9da0b57be1a6aca217a6cfda090c4fd222f7b8cfdc32969da4fe8828a59ee1314546efdf99ef7ede1a42df6e7a126fe83b4c41b5e70a56bd9ab499f7e80e27a08884be05f1d2a527417fc6e30448333c0724463bf92d722ef5fd6f06949e294e6f941976d24c856038b55a2ec200d14d958a688f23b572993bd0f18cbbc20defe88e423b262c552dcc4d9f63ad78e85efbcea9449f81f39e1a887eb79b07056bb5a672444e240660617ba7a40985a622c687c1d05c12cee7b086abfc5f39a83a5ad7638ee559f710013b772d4207924687cb30100bcd4e8c83c9fa19dce7785bf3ae7681a0968fd9661c990e2dace05902dceeed65aacf51a04e72f0fd04858ea70fb72f2a3807dc1839a385d85b536abfd3ec76d4931b3bc5ec4d90e2ebc0342567c9507abdfafa602fc6983f13f20eb26b4169dc3908109fe3c1887db4be8f30edad989dc8caa234f9818ac488b110ad30a30f769277168650b6910e", + }, + { + name: "GT mul by negative scalar", + point: "0bdbfc3b68e7067630a1908de2ce15e1890d57b855ffc2ee0fe765293581c304d0507254fd9921d8ff4bff3185b1e8ae017091a6b9e243c3108b4302f30e2f4cb452c4574d23d06942cf915fb0b64c3546aa0bfbba5182dc42b63ebd09cd950f06ebf85ff360032e63d5422fed5969b80ed4abaf58d29317d9cf8e5a55744993ffc0ccc586a187c63f9c47d4b41870aa0fd73e13a4f7d3b072407a3bfa6539f8d56856542b17326ab77833df274e61a41c237a6dbf20a333698a675fded6ab1a114891795eabbedcb81590ff9bfb4b23b66c8b8376a69cf58511c80f3ac83d52c0c950be8c30d01108479f232d8e4e8919d869dc85db0b9d6ccf40eb8f8ab08e43a910c341737a55e751fa4a097ee82c5ac83d38c543d957bd9850af16039d1a00c96575d2ee24e9990b3401153446aa6593d3afb6ce7ca57d6432b8dda31aaa1a08834ad38deae5a807d11663adc5c20ae7227a2cbb7917d1489175b89ed1ba415e4fc55b7d0a286caf2f5f40b0dd39cdd8fc8c271d8a7ae952fe6ece5f7c1019bfab0167af86314a73bfa37fd16bc6edff6d9ee75610a4eec1818c668ef9f509b1cdd54542e73dc0e343a4fd6e3bb618540c1d060b60b63b645a895105425eb813b08b6ac91be3145da04040f2a45ffcf06e96b685519fca93b0f15238dc0e030c2199127ba82fa8a193f5f01ae24270e9669923653db38cae711d68169aa25df51a8915f3f8219892f4f5e67d550b00910011685017dcc1777a9d48689ce590d57c1fc942d49cfad0ed7efc0169a95d7e7378af26bafb90d1619bcdab64cd", + mul: "688e58217305c1fd2fe0637cbd8e7414d4d0a2113314eb05592f97930d23b34d", + neg: true, + expectedTyp: gtP, + expected: "056fdc84f044148950c0b7c4c0613f5710fcaeb1b023b9d8f814dc39d48702db70ce41aa276566960e37237f22b086b017b9ed0e264e2b7872c8a7affb8b9f847a528d092a038dab4ac58d3a33d30e2e5078b5e39ebb7441c56ae7556b63ecd6139ed9be1c5eb9f987cc704c913c1e23d44d2e04377347f6c471edc40cdb2cd4e32c396194363cd21ceff9bedbd164a41050e701012f0456383210f8054e76c0906e3f37e10d4a3d6342e79e39d566ea785b385bb692cddbd6c16456dfabf19f0f84c27ec4bce096af0369ac070747cd89d97bc287afe5ed5e495ed2d743adbd8eec47df6c3a69628e803e23d824845800e44a8d874756a7541128892e55e9df1d1fe0583ef967db6740617a9ff50766866c0fa631aed8639cd0c13d3d6f6f210b340ee315caec4cc31c916d651db5e002e259fca081fb605258ccf692d786bd5bb45a054c4d8498ac2a7fa241870df60ba0fd8a2b063740af11e7530db1e758a8e2858a443104b8337e18c083035768a0e93126f116bb9c50c8cebe30e0ceaa0c0b53eb2b6a1f96b34b6cc36f3417edda184e19ae1790d255337f14315323e1d2d7382b344bdc0b6b2cfab5837c24c916640ca351539d5459389a9c7f9b0d79e04e4a8392e0c2495dcecf7d48b10c7043825b7c6709108d81856ebf98385f0d099e6521714c48b8eb5d2e97665375175f47c57d427d35a9dc44064a99d1c079028e36d34540baba947333ab3c8976b801ea48578159f041e740ea5bf73c1de3c1043a6e03311d0f2463b72694249ccc5d603e4a93cfd8a6713fb0470383c23f", + }, + { + name: "GT mul by zero scalar", + point: "176ec726aa447f1791e69fc70a71103c84b17385094ef06a9a0235ac7241f6635377f55ad486c216c8701d61ea2ace3e05ca1605f238dc8f29f868b795e45645c6f7ff8d9d8ffd77b5e149b0325c2a8f24dde40e80a3381ae72a9a1104ef02d70af7cf8f2fe6ff38961b352b0fde6f8536424fc9aa5805b8e12313bdfc01d5c1db1c0a37654c307fbd252c265dcbfc040ee5605ffd6ac20aab15b0343e47831f4157a20ecedd7350d2cf070c0c7d423786fd97aa7236b99f4462fb23e173528815bf2cf3ccbfc38303fa8154d70ee5e1e3158cbb14d5c87a773cbe948a5cfec2763c5e7129940906920aed344453b0f801760fd3eac8e254ce8e0ae4edd30c914bea9e2935acd4a6a9d42d185a9a6e786c8e462b769b2112423f6591b093347718897438ba918b9e4525888194b20ee17709f7dea319cfd053bb1c222783340326953fd3763eb6feaaa4d1458ee6ca001818ad88222a97e43a71dca8d2abaef70657b9ff7b94ca422d0c50ddb4265fa35514ed534217ce2f0219c6985ec2827a0ee1dc17940926551072d693d89e36e6d14162f414b52587e5612ed4a562c9ac15df9d5fa68ccf61d52fea64b2f5d7a600e0a8fa735105bc9a2ecb69b6d9161e55a4ccdc2285164c6846fa5bdc106d1e0693ebd5fe86432e5e88c55f0159ec3217332c8492332dfbd93970f002a6a05f23484e081f38815785e766779c843765d58b2444295a87939ad7f8fa4c11e8530a62426063c9a57cf3481a00372e443dc014fd6ef4723dd4636105d7ce7b96c4b2b3b641c3a2b6e0fa9be6187e5bfaf9", + mul: "0000000000000000000000000000000000000000000000000000000000000000", + neg: false, + expectedTyp: gtP, + expected: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + }, + { + name: "G1Affine mul by positive scalar", + point: "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + mul: "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + neg: false, + expectedTyp: g1JacP, + expected: "ae85e3e2d677c9e3424ed79b5a7554262c3d6849202b84d2e7024e4b1f2e9dd3f7cf20b807a9f2a67d87e47e9e94d361", + }, + { + name: "G1Affine mul by negative scalar", + point: "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + mul: "8463159bd9a1d1e1fd815172177ec24c0c291353ed88b3d1838fd9d63b1efd0b", + neg: true, + expectedTyp: g1JacP, + expected: "8e85e3e2d677c9e3424ed79b5a7554262c3d6849202b84d2e7024e4b1f2e9dd3f7cf20b807a9f2a67d87e47e9e94d361", + }, + { + name: "G1Affine mul by zero scalar", + point: "a1f9855f7670a63e4c80d64dfe6ddedc2ed2bfaebae27e4da82d71ba474987a39808e8921d3df97df6e5d4b979234de8", + mul: "0000000000000000000000000000000000000000000000000000000000000000", + neg: false, + expectedTyp: g1JacP, + expected: "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + { + name: "G2Affine mul by positive scalar", + point: "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + mul: "cbfffe3e37e53e31306addde1a1725641fbe88cd047ee7477966c44a3f764b47", + neg: false, + expectedTyp: g2JacP, + expected: "88ae9bba988e854877c66dfb7ff84aa5e107861aa51d1a2a8dac2414d716a7e219bc4b0239e4b12d2182f57b5eea82830639f2e6713098ae8d4b4c3942f366614bac35c91c83ecb57fa90fe03094aca1ecd3555a7a6fdfa2417b5bb06917732e", + }, + { + name: "G2Affine mul by negative scalar", + point: "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + mul: "cbfffe3e37e53e31306addde1a1725641fbe88cd047ee7477966c44a3f764b47", + neg: true, + expectedTyp: g2JacP, + expected: "a8ae9bba988e854877c66dfb7ff84aa5e107861aa51d1a2a8dac2414d716a7e219bc4b0239e4b12d2182f57b5eea82830639f2e6713098ae8d4b4c3942f366614bac35c91c83ecb57fa90fe03094aca1ecd3555a7a6fdfa2417b5bb06917732e", + }, + { + name: "G2Affine mul by zero scalar", + point: "a41e586fdd58d39616fea921a855e65417a5732809afc35e28466e3acaeed3d53dd4b97ca398b2f29bf6bbcaca026a6609a42bdeaaeef42813ae225e35c23c61c293e6ecb6759048fb76ac648ba3bc49f0fcf62f73fca38cdc5e7fa5bf511365", + mul: "0000000000000000000000000000000000000000000000000000000000000000", + neg: false, + expectedTyp: g2JacP, + expected: "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + } + c := newCryptolibClient(t) + + check := func(t *testing.T, resTyp pointType, gt string, mul string, neg bool, expected string) { + gtBytes, err := hex.DecodeString(gt) + require.NoError(t, err) + mulBytes, err := hex.DecodeString(mul) + require.NoError(t, err) + script := io.NewBufBinWriter() + if neg { + emit.Opcodes(script.BinWriter, opcode.PUSHT) + } else { + emit.Opcodes(script.BinWriter, opcode.PUSHF) + } + emit.Bytes(script.BinWriter, mulBytes) + emit.AppCall(script.BinWriter, c.Hash, "bls12381Deserialize", callflag.All, gtBytes) + emit.Opcodes(script.BinWriter, opcode.PUSH3, opcode.PACK) + emit.AppCallNoArgs(script.BinWriter, c.Hash, "bls12381Mul", 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.InteropT, itm.Type()) + var actual []byte + switch resTyp { + case gtP: + gt, ok := itm.(*stackitem.Interop).Value().(*bls12381.GT) + 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) + } + require.Equal(t, strings.ToLower(expected), hex.EncodeToString(actual)) + } + + for i, tc := range tcs { + t.Run(strconv.Itoa(i), func(t *testing.T) { + check(t, tc.expectedTyp, + tc.point, + tc.mul, + tc.neg, + tc.expected) + }) + } +} diff --git a/pkg/neotest/basic.go b/pkg/neotest/basic.go index ce6ed44b1..d904f39ab 100644 --- a/pkg/neotest/basic.go +++ b/pkg/neotest/basic.go @@ -181,13 +181,18 @@ func (e *Executor) InvokeScript(t testing.TB, script []byte, signers []Signer) u // PrepareInvocation creates a transaction with the specified script and signs it // by the provided signer. func (e *Executor) PrepareInvocation(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) *transaction.Transaction { + tx := e.PrepareInvocationNoSign(t, script, validUntilBlock...) + e.SignTx(t, tx, -1, signers...) + return tx +} + +func (e *Executor) PrepareInvocationNoSign(t testing.TB, script []byte, validUntilBlock ...uint32) *transaction.Transaction { tx := transaction.New(script, 0) tx.Nonce = Nonce() tx.ValidUntilBlock = e.Chain.BlockHeight() + 1 if len(validUntilBlock) != 0 { tx.ValidUntilBlock = validUntilBlock[0] } - e.SignTx(t, tx, -1, signers...) return tx } diff --git a/pkg/neotest/client.go b/pkg/neotest/client.go index 9596c12cb..bd96bab47 100644 --- a/pkg/neotest/client.go +++ b/pkg/neotest/client.go @@ -47,7 +47,28 @@ func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker { } } -// TestInvoke creates test the VM and invokes the method with the args. +// TestInvokeScript creates test VM and invokes the script with the args and signers. +func (c *ContractInvoker) TestInvokeScript(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) (*vm.Stack, error) { + tx := c.PrepareInvocationNoSign(t, script, validUntilBlock...) + for _, acc := range signers { + tx.Signers = append(tx.Signers, transaction.Signer{ + Account: acc.ScriptHash(), + Scopes: transaction.Global, + }) + } + b := c.NewUnsignedBlock(t, tx) + ic, err := c.Chain.GetTestVM(trigger.Application, tx, b) + if err != nil { + return nil, err + } + t.Cleanup(ic.Finalize) + + ic.VM.LoadWithFlags(tx.Script, callflag.All) + err = ic.VM.Run() + return ic.VM.Estack(), err +} + +// TestInvoke creates test VM and invokes the method with the args. func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...any) (*vm.Stack, error) { tx := c.PrepareInvokeNoSign(t, method, args...) b := c.NewUnsignedBlock(t, tx) diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go index c5be0f3e0..2b0b9fb50 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -82,7 +82,7 @@ const ( faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60" faultedTxBlock uint32 = 23 invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA" - block20StateRootLE = "811a287a0235cfb3c7def100ae7029335a10e8b90d0ca59c460955c0546a0414" + block20StateRootLE = "33b4cee6a59b9dc9d186fc235dc81e2ffe74418d7d777d538422a62b8e635ef2" ) var (