33c971b0e4
Make the contracts cache initialization unified. The order of cache iniitialization is not important and Nottary contract is added to the bc.contracts.Contracts wrt P2PSigExtensions setting, thus no functional changes, just refactoring for future applications. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
484 lines
14 KiB
Go
484 lines
14 KiB
Go
package native
|
|
|
|
import (
|
|
"crypto/elliptic"
|
|
"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/dao"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/twmb/murmur3"
|
|
)
|
|
|
|
// Crypto represents CryptoLib contract.
|
|
type Crypto struct {
|
|
interop.ContractMD
|
|
}
|
|
|
|
// NamedCurve identifies named elliptic curves.
|
|
type NamedCurve byte
|
|
|
|
// Various named elliptic curves.
|
|
const (
|
|
Secp256k1 NamedCurve = 22
|
|
Secp256r1 NamedCurve = 23
|
|
)
|
|
|
|
const cryptoContractID = -3
|
|
|
|
func newCrypto() *Crypto {
|
|
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
|
defer c.UpdateHash()
|
|
|
|
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
|
md := newMethodAndPrice(c.sha256, 1<<15, callflag.NoneFlag)
|
|
c.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("ripemd160", smartcontract.ByteArrayType,
|
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
|
md = newMethodAndPrice(c.ripemd160, 1<<15, callflag.NoneFlag)
|
|
c.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("murmur32", smartcontract.ByteArrayType,
|
|
manifest.NewParameter("data", smartcontract.ByteArrayType),
|
|
manifest.NewParameter("seed", smartcontract.IntegerType))
|
|
md = newMethodAndPrice(c.murmur32, 1<<13, callflag.NoneFlag)
|
|
c.AddMethod(md, desc)
|
|
|
|
desc = newDescriptor("verifyWithECDsa", smartcontract.BoolType,
|
|
manifest.NewParameter("message", smartcontract.ByteArrayType),
|
|
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
|
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
|
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
|
|
}
|
|
|
|
func (c *Crypto) sha256(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
bs, err := args[0].TryBytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return stackitem.NewByteArray(hash.Sha256(bs).BytesBE())
|
|
}
|
|
|
|
func (c *Crypto) ripemd160(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
bs, err := args[0].TryBytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return stackitem.NewByteArray(hash.RipeMD160(bs).BytesBE())
|
|
}
|
|
|
|
func (c *Crypto) murmur32(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
bs, err := args[0].TryBytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
seed := toUint32(args[1])
|
|
h := murmur3.SeedSum32(seed, bs)
|
|
result := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(result, h)
|
|
return stackitem.NewByteArray(result)
|
|
}
|
|
|
|
func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
|
msg, err := args[0].TryBytes()
|
|
if err != nil {
|
|
panic(fmt.Errorf("invalid message stackitem: %w", err))
|
|
}
|
|
hashToCheck := hash.Sha256(msg)
|
|
pubkey, err := args[1].TryBytes()
|
|
if err != nil {
|
|
panic(fmt.Errorf("invalid pubkey stackitem: %w", err))
|
|
}
|
|
signature, err := args[2].TryBytes()
|
|
if err != nil {
|
|
panic(fmt.Errorf("invalid signature stackitem: %w", err))
|
|
}
|
|
curve, err := curveFromStackitem(args[3])
|
|
if err != nil {
|
|
panic(fmt.Errorf("invalid curve stackitem: %w", err))
|
|
}
|
|
pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to decode pubkey: %w", err))
|
|
}
|
|
res := pkey.Verify(signature, hashToCheck.BytesBE())
|
|
return stackitem.NewBool(res)
|
|
}
|
|
|
|
func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) {
|
|
curve, err := si.TryInteger()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !curve.IsInt64() {
|
|
return nil, errors.New("not an int64")
|
|
}
|
|
c := curve.Int64()
|
|
switch c {
|
|
case int64(Secp256k1):
|
|
return secp256k1.S256(), nil
|
|
case int64(Secp256r1):
|
|
return elliptic.P256(), nil
|
|
default:
|
|
return nil, errors.New("unsupported curve type")
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Initialize implements the Contract interface.
|
|
func (c *Crypto) Initialize(ic *interop.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// InitializeCache implements the Contract interface.
|
|
func (c *Crypto) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
|
return nil
|
|
}
|
|
|
|
// OnPersist implements the Contract interface.
|
|
func (c *Crypto) OnPersist(ic *interop.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// PostPersist implements the Contract interface.
|
|
func (c *Crypto) PostPersist(ic *interop.Context) error {
|
|
return nil
|
|
}
|