From 1611ede58c62f4ab634e1b7743a46e7bd0f988ed Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 7 Apr 2020 10:55:56 +0300 Subject: [PATCH 1/5] crypto/keys: implement NewPublicKeyFromBytes() It is convenient to have a single function instead of allocating new `PublicKey` and using `DecodeBytes()` on it. --- pkg/core/interop_neo.go | 3 +-- pkg/core/interop_system.go | 3 +-- pkg/crypto/keys/publickey.go | 11 ++++++----- pkg/crypto/keys/publickey_test.go | 10 ++++++++++ pkg/vm/vm.go | 4 ++-- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 378214447..5a6990f87 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -628,8 +628,7 @@ func assetCreate(ic *interop.Context, v *vm.VM) error { if amount != -util.Satoshi() && (int64(amount)%int64(math.Pow10(int(MaxAssetPrecision-precision))) != 0) { return errors.New("given asset amount has fractional component") } - owner := &keys.PublicKey{} - err := owner.DecodeBytes(v.Estack().Pop().Bytes()) + owner, err := keys.NewPublicKeyFromBytes(v.Estack().Pop().Bytes()) if err != nil { return gherr.Wrap(err, "failed to get owner key") } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index bb6816f97..e19373b9f 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -324,8 +324,7 @@ func runtimeCheckWitness(ic *interop.Context, v *vm.VM) error { hashOrKey := v.Estack().Pop().Bytes() hash, err := util.Uint160DecodeBytesBE(hashOrKey) if err != nil { - key := &keys.PublicKey{} - err = key.DecodeBytes(hashOrKey) + key, err := keys.NewPublicKeyFromBytes(hashOrKey) if err != nil { return errors.New("parameter given is neither a key nor a hash") } diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 9eaf4688a..358433837 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -83,14 +83,15 @@ func NewPublicKeyFromString(s string) (*PublicKey, error) { if err != nil { return nil, err } + return NewPublicKeyFromBytes(b) +} +// NewPublicKeyFromBytes returns public key created from b. +func NewPublicKeyFromBytes(b []byte) (*PublicKey, error) { pubKey := new(PublicKey) - r := io.NewBinReaderFromBuf(b) - pubKey.DecodeBinary(r) - if r.Err != nil { - return nil, r.Err + if err := pubKey.DecodeBytes(b); err != nil { + return nil, err } - return pubKey, nil } diff --git a/pkg/crypto/keys/publickey_test.go b/pkg/crypto/keys/publickey_test.go index fda5c3b18..1415197a0 100644 --- a/pkg/crypto/keys/publickey_test.go +++ b/pkg/crypto/keys/publickey_test.go @@ -37,6 +37,16 @@ func TestEncodeDecodePublicKey(t *testing.T) { } } +func TestNewPublicKeyFromBytes(t *testing.T) { + priv, err := NewPrivateKey() + require.NoError(t, err) + + b := priv.PublicKey().Bytes() + pub, err := NewPublicKeyFromBytes(b) + require.NoError(t, err) + require.Equal(t, priv.PublicKey(), pub) +} + func TestDecodeFromString(t *testing.T) { str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c" pubKey, err := NewPublicKeyFromString(str) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 788de6c1f..9766fef15 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1595,8 +1595,8 @@ func (v *VM) bytesToPublicKey(b []byte) *keys.PublicKey { if v.keys[s] != nil { pkey = v.keys[s] } else { - pkey = &keys.PublicKey{} - err := pkey.DecodeBytes(b) + var err error + pkey, err = keys.NewPublicKeyFromBytes(b) if err != nil { panic(err.Error()) } From 5a30af2c75d39a8d3e21a4c06458b2d04846873d Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 18 Mar 2020 12:41:09 +0300 Subject: [PATCH 2/5] core: implement Neo.Crypto.ECDsaVerify interop --- pkg/core/interop/crypto/ecdsa.go | 31 ++++++++++++++++ pkg/core/interop_neo_test.go | 63 ++++++++++++++++++++++++++++++++ pkg/core/interops.go | 2 + 3 files changed, 96 insertions(+) create mode 100644 pkg/core/interop/crypto/ecdsa.go diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go new file mode 100644 index 000000000..ab7f5b6b5 --- /dev/null +++ b/pkg/core/interop/crypto/ecdsa.go @@ -0,0 +1,31 @@ +package crypto + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "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/vm" +) + +// ECDSAVerify checks ECDSA signature. +func ECDSAVerify(ic *interop.Context, v *vm.VM) error { + msg := getMessage(ic, v.Estack().Pop().Item()) + hashToCheck := hash.Sha256(msg).BytesBE() + keyb := v.Estack().Pop().Bytes() + signature := v.Estack().Pop().Bytes() + pkey, err := keys.NewPublicKeyFromBytes(keyb) + if err != nil { + return err + } + res := pkey.Verify(signature, hashToCheck) + v.Estack().PushVal(res) + return nil +} + +func getMessage(_ *interop.Context, item vm.StackItem) []byte { + msg, err := item.TryBytes() + if err != nil { + panic(err) + } + return msg +} diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 26068690f..7253edb17 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -1,12 +1,14 @@ package core import ( + "fmt" "math/big" "testing" "github.com/nspcc-dev/neo-go/pkg/core/block" "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/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -231,6 +233,67 @@ func TestWitnessGetVerificationScript(t *testing.T) { require.Equal(t, witness.VerificationScript, value) } +func TestECDSAVerify(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + chain := newTestChain(t) + defer chain.Close() + + ic := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil) + runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + v := vm.New() + for i := range args { + v.Estack().PushVal(args[i]) + } + + var err error + func() { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + err = crypto.ECDSAVerify(ic, v) + }() + + if isErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, 1, v.Estack().Len()) + require.Equal(t, result, v.Estack().Pop().Value().(bool)) + } + + msg := []byte("test message") + + t.Run("success", func(t *testing.T) { + sign := priv.Sign(msg) + runCase(t, false, true, sign, priv.PublicKey().Bytes(), msg) + }) + + t.Run("missing arguments", func(t *testing.T) { + runCase(t, true, false) + sign := priv.Sign(msg) + runCase(t, true, false, sign) + runCase(t, true, false, sign, priv.PublicKey().Bytes()) + }) + + t.Run("invalid signature", func(t *testing.T) { + sign := priv.Sign(msg) + sign[0] ^= sign[0] + runCase(t, false, false, sign, priv.PublicKey().Bytes(), msg) + }) + + t.Run("invalid public key", func(t *testing.T) { + sign := priv.Sign(msg) + pub := priv.PublicKey().Bytes() + pub = pub[10:] + runCase(t, true, false, sign, pub, msg) + }) +} + func TestPopInputFromVM(t *testing.T) { v, tx, _, chain := createVMAndTX(t) defer chain.Close() diff --git a/pkg/core/interops.go b/pkg/core/interops.go index edcdb3310..751035f7d 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -11,6 +11,7 @@ import ( "sort" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -137,6 +138,7 @@ var neoInterops = []interop.Function{ {Name: "Neo.Contract.GetStorageContext", Func: contractGetStorageContext, Price: 1}, {Name: "Neo.Contract.IsPayable", Func: contractIsPayable, Price: 1}, {Name: "Neo.Contract.Migrate", Func: contractMigrate, Price: 0}, + {Name: "Neo.Crypto.ECDsaVerify", Func: crypto.ECDSAVerify, Price: 1}, {Name: "Neo.Enumerator.Concat", Func: enumerator.Concat, Price: 1}, {Name: "Neo.Enumerator.Create", Func: enumerator.Create, Price: 1}, {Name: "Neo.Enumerator.Next", Func: enumerator.Next, Price: 1}, From c23af595c903b53182d681e4c48622ff66d21e88 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 18 Mar 2020 14:04:52 +0300 Subject: [PATCH 3/5] core: implement Neo.Crypto.ECDsaCheckMultisig interop --- pkg/core/interop/crypto/ecdsa.go | 26 ++++++++++++++++++++++++++ pkg/core/interops.go | 1 + pkg/vm/stack.go | 4 ++-- pkg/vm/stack_test.go | 14 +++++++------- pkg/vm/vm.go | 9 +++++---- 5 files changed, 41 insertions(+), 13 deletions(-) diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go index ab7f5b6b5..ae8bf014c 100644 --- a/pkg/core/interop/crypto/ecdsa.go +++ b/pkg/core/interop/crypto/ecdsa.go @@ -1,6 +1,9 @@ package crypto import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -22,6 +25,29 @@ func ECDSAVerify(ic *interop.Context, v *vm.VM) error { return nil } +// ECDSACheckMultisig checks multiple ECDSA signatures at once. +func ECDSACheckMultisig(ic *interop.Context, v *vm.VM) error { + msg := getMessage(ic, v.Estack().Pop().Item()) + hashToCheck := hash.Sha256(msg).BytesBE() + pkeys, err := v.Estack().PopSigElements() + if err != nil { + return fmt.Errorf("wrong parameters: %s", err.Error()) + } + sigs, err := v.Estack().PopSigElements() + if err != nil { + return fmt.Errorf("wrong parameters: %s", err.Error()) + } + // It's ok to have more keys than there are signatures (it would + // just mean that some keys didn't sign), but not the other way around. + if len(pkeys) < len(sigs) { + return errors.New("more signatures than there are keys") + } + v.SetCheckedHash(hashToCheck) + sigok := vm.CheckMultisigPar(v, pkeys, sigs) + v.Estack().PushVal(sigok) + return nil +} + func getMessage(_ *interop.Context, item vm.StackItem) []byte { msg, err := item.TryBytes() if err != nil { diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 751035f7d..5b337957c 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -139,6 +139,7 @@ var neoInterops = []interop.Function{ {Name: "Neo.Contract.IsPayable", Func: contractIsPayable, Price: 1}, {Name: "Neo.Contract.Migrate", Func: contractMigrate, Price: 0}, {Name: "Neo.Crypto.ECDsaVerify", Func: crypto.ECDSAVerify, Price: 1}, + {Name: "Neo.Crypto.ECDsaCheckMultiSig", Func: crypto.ECDSACheckMultisig, Price: 1}, {Name: "Neo.Enumerator.Concat", Func: enumerator.Concat, Price: 1}, {Name: "Neo.Enumerator.Create", Func: enumerator.Create, Price: 1}, {Name: "Neo.Enumerator.Next", Func: enumerator.Next, Price: 1}, diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index fc41a0406..d0f833db0 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -424,9 +424,9 @@ func (s *Stack) Roll(n int) error { return nil } -// popSigElements pops keys or signatures from the stack as needed for +// PopSigElements pops keys or signatures from the stack as needed for // CHECKMULTISIG. -func (s *Stack) popSigElements() ([][]byte, error) { +func (s *Stack) PopSigElements() ([][]byte, error) { var num int var elems [][]byte item := s.Pop() diff --git a/pkg/vm/stack_test.go b/pkg/vm/stack_test.go index a1964bd56..24a0671f6 100644 --- a/pkg/vm/stack_test.go +++ b/pkg/vm/stack_test.go @@ -317,37 +317,37 @@ func TestRoll(t *testing.T) { func TestPopSigElements(t *testing.T) { s := NewStack("test") - _, err := s.popSigElements() + _, err := s.PopSigElements() assert.NotNil(t, err) s.PushVal([]StackItem{}) - _, err = s.popSigElements() + _, err = s.PopSigElements() assert.NotNil(t, err) s.PushVal([]StackItem{NewBoolItem(false)}) - _, err = s.popSigElements() + _, err = s.PopSigElements() assert.NotNil(t, err) b1 := []byte("smth") b2 := []byte("strange") s.PushVal([]StackItem{NewByteArrayItem(b1), NewByteArrayItem(b2)}) - z, err := s.popSigElements() + z, err := s.PopSigElements() assert.Nil(t, err) assert.Equal(t, z, [][]byte{b1, b2}) s.PushVal(2) - _, err = s.popSigElements() + _, err = s.PopSigElements() assert.NotNil(t, err) s.PushVal(b1) s.PushVal(2) - _, err = s.popSigElements() + _, err = s.PopSigElements() assert.NotNil(t, err) s.PushVal(b2) s.PushVal(b1) s.PushVal(2) - z, err = s.popSigElements() + z, err = s.PopSigElements() assert.Nil(t, err) assert.Equal(t, z, [][]byte{b1, b2}) } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 9766fef15..7b2bc967b 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1235,11 +1235,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.estack.PushVal(res) case opcode.CHECKMULTISIG: - pkeys, err := v.estack.popSigElements() + pkeys, err := v.estack.PopSigElements() if err != nil { panic(fmt.Sprintf("wrong parameters: %s", err.Error())) } - sigs, err := v.estack.popSigElements() + sigs, err := v.estack.PopSigElements() if err != nil { panic(fmt.Sprintf("wrong parameters: %s", err.Error())) } @@ -1252,7 +1252,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("VM is not set up properly for signature checks") } - sigok := checkMultisigPar(v, pkeys, sigs) + sigok := CheckMultisigPar(v, pkeys, sigs) v.estack.PushVal(sigok) case opcode.NEWMAP: @@ -1446,7 +1446,8 @@ func (v *VM) getJumpOffset(ctx *Context, parameter []byte, mod int) int { return offset } -func checkMultisigPar(v *VM, pkeys [][]byte, sigs [][]byte) bool { +// CheckMultisigPar checks if sigs contains sufficient valid signatures. +func CheckMultisigPar(v *VM, pkeys [][]byte, sigs [][]byte) bool { if len(sigs) == 1 { return checkMultisig1(v, pkeys, sigs[0]) } From 948729137f041a9340402260fffdaed7279505b7 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 23 Mar 2020 12:32:07 +0300 Subject: [PATCH 4/5] vm: remove HASH160/HASH256 opcodes --- pkg/compiler/codegen.go | 4 ---- pkg/compiler/util_test.go | 30 ------------------------------ pkg/interop/crypto/crypto.go | 10 ---------- pkg/vm/vm.go | 8 -------- pkg/vm/vm_test.go | 22 ---------------------- 5 files changed, 74 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 7f1b33eeb..34c3171ae 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1063,10 +1063,6 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { emit.Opcode(c.prog.BinWriter, opcode.SHA256) case "SHA1": emit.Opcode(c.prog.BinWriter, opcode.SHA1) - case "Hash256": - emit.Opcode(c.prog.BinWriter, opcode.HASH256) - case "Hash160": - emit.Opcode(c.prog.BinWriter, opcode.HASH160) case "VerifySignature": emit.Opcode(c.prog.BinWriter, opcode.VERIFY) case "AppCall": diff --git a/pkg/compiler/util_test.go b/pkg/compiler/util_test.go index 90fc19c35..1f82c73f4 100644 --- a/pkg/compiler/util_test.go +++ b/pkg/compiler/util_test.go @@ -33,33 +33,3 @@ func TestSHA1(t *testing.T) { ` eval(t, src, []byte{0xfa, 0x13, 0x8a, 0xe3, 0x56, 0xd3, 0x5c, 0x8d, 0x77, 0x8, 0x3c, 0x40, 0x6a, 0x5b, 0xe7, 0x37, 0x45, 0x64, 0x3a, 0xae}) } - -func TestHash160(t *testing.T) { - src := ` - package foo - import ( - "github.com/nspcc-dev/neo-go/pkg/interop/crypto" - ) - func Main() []byte { - src := []byte{0x97} - hash := crypto.Hash160(src) - return hash - } - ` - eval(t, src, []byte{0x5f, 0xa4, 0x1c, 0x76, 0xf7, 0xe8, 0xca, 0x72, 0xb7, 0x18, 0xff, 0x59, 0x22, 0x91, 0xc2, 0x3a, 0x3a, 0xf5, 0x58, 0x6c}) -} - -func TestHash256(t *testing.T) { - src := ` - package foo - import ( - "github.com/nspcc-dev/neo-go/pkg/interop/crypto" - ) - func Main() []byte { - src := []byte{0x97} - hash := crypto.Hash256(src) - return hash - } - ` - eval(t, src, []byte{0xc0, 0x85, 0x26, 0xad, 0x17, 0x36, 0x53, 0xee, 0xb8, 0xc7, 0xf4, 0xae, 0x82, 0x8b, 0x6e, 0xa1, 0x84, 0xac, 0x5a, 0x3, 0x8a, 0xf6, 0xc3, 0x68, 0x23, 0xfa, 0x5f, 0x5d, 0xd9, 0x1b, 0x91, 0xa2}) -} diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index abfe586e8..fe8d527f5 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -13,16 +13,6 @@ func SHA256(b []byte) []byte { return nil } -// Hash160 computes the sha256 + ripemd160 of b. -func Hash160(b []byte) []byte { - return nil -} - -// Hash256 computes the sha256^2 hash of b. -func Hash256(b []byte) []byte { - return nil -} - // VerifySignature checks that sig is msg's signature with pub. func VerifySignature(msg []byte, sig []byte, pub []byte) bool { return false diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 7b2bc967b..4b56d1f8f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1332,14 +1332,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro b := v.estack.Pop().Bytes() v.estack.PushVal(hash.Sha256(b).BytesBE()) - case opcode.HASH160: - b := v.estack.Pop().Bytes() - v.estack.PushVal(hash.Hash160(b).BytesBE()) - - case opcode.HASH256: - b := v.estack.Pop().Bytes() - v.estack.PushVal(hash.DoubleSha256(b).BytesBE()) - case opcode.NOP: // unlucky ^^ diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 4c55b88fd..e35b7d162 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -3035,28 +3035,6 @@ func TestSHA256(t *testing.T) { assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes())) } -func TestHASH160(t *testing.T) { - // 0x0100 hashes to fbc22d517f38e7612798ece8e5957cf6c41d8caf - res := "fbc22d517f38e7612798ece8e5957cf6c41d8caf" - prog := makeProgram(opcode.PUSHBYTES2, 1, 0, - opcode.HASH160) - vm := load(prog) - runVM(t, vm) - assert.Equal(t, 1, vm.estack.Len()) - assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes())) -} - -func TestHASH256(t *testing.T) { - // 0x0100 hashes to 677b2d718464ee0121475600b929c0b4155667486577d1320b18c2dc7d4b4f99 - res := "677b2d718464ee0121475600b929c0b4155667486577d1320b18c2dc7d4b4f99" - prog := makeProgram(opcode.PUSHBYTES2, 1, 0, - opcode.HASH256) - vm := load(prog) - runVM(t, vm) - assert.Equal(t, 1, vm.estack.Len()) - assert.Equal(t, res, hex.EncodeToString(vm.estack.Pop().Bytes())) -} - var opcodesTestCases = map[opcode.Opcode][]struct { name string args []interface{} From 4ad29d0867bae1acd5cf94dfd4847073fea9df41 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 23 Mar 2020 12:44:23 +0300 Subject: [PATCH 5/5] compiler: emit Neo.Crypto.ECDsaVerify syscall instead of CHECKSIG Also change the name of `VerifySignature` interop, to match syscall's name. It also accepts arguments in different order. --- pkg/compiler/analysis.go | 2 +- pkg/compiler/codegen.go | 2 -- pkg/compiler/syscall.go | 3 +++ pkg/compiler/verify_test.go | 24 ++++++++++++++---------- pkg/interop/crypto/crypto.go | 4 ++-- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 90dd5e318..fb25eab49 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -14,7 +14,7 @@ var ( builtinFuncs = []string{ "len", "append", "SHA256", "SHA1", "Hash256", "Hash160", - "VerifySignature", "AppCall", + "AppCall", "FromAddress", "Equals", "panic", } diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 34c3171ae..610cafd7c 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1063,8 +1063,6 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { emit.Opcode(c.prog.BinWriter, opcode.SHA256) case "SHA1": emit.Opcode(c.prog.BinWriter, opcode.SHA1) - case "VerifySignature": - emit.Opcode(c.prog.BinWriter, opcode.VERIFY) case "AppCall": numArgs := len(expr.Args) - 1 c.emitReverse(numArgs) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 01234955b..633335b4b 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -1,6 +1,9 @@ package compiler var syscalls = map[string]map[string]string{ + "crypto": { + "ECDsaVerify": "Neo.Crypto.ECDsaVerify", + }, "storage": { "GetContext": "Neo.Storage.GetContext", "Put": "Neo.Storage.Put", diff --git a/pkg/compiler/verify_test.go b/pkg/compiler/verify_test.go index 754a6d7a8..8d0151625 100644 --- a/pkg/compiler/verify_test.go +++ b/pkg/compiler/verify_test.go @@ -5,24 +5,28 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +// In this test we only check that needed interop +// is called with the provided arguments in the right order. func TestVerifyGood(t *testing.T) { msg := []byte("test message") pub, sig := signMessage(t, msg) src := getVerifyProg(pub, sig, msg) - eval(t, src, true) -} + v, p := vmAndCompileInterop(t, src) + p.interops[vm.InteropNameToID([]byte("Neo.Crypto.ECDsaVerify"))] = func(v *vm.VM) error { + assert.Equal(t, msg, v.Estack().Pop().Bytes()) + assert.Equal(t, pub, v.Estack().Pop().Bytes()) + assert.Equal(t, sig, v.Estack().Pop().Bytes()) + v.Estack().PushVal(true) + return nil + } -func TestVerifyBad(t *testing.T) { - msg := []byte("test message") - pub, sig := signMessage(t, msg) - sig[0] = ^sig[0] - src := getVerifyProg(pub, sig, msg) - - eval(t, src, false) + require.NoError(t, v.Run()) } func signMessage(t *testing.T, msg []byte) ([]byte, []byte) { @@ -49,7 +53,7 @@ func getVerifyProg(pub, sig, msg []byte) string { pub := ` + pubS + ` sig := ` + sigS + ` msg := ` + msgS + ` - return crypto.VerifySignature(msg, sig, pub) + return crypto.ECDsaVerify(msg, pub, sig) } ` } diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index fe8d527f5..cce23b916 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -13,7 +13,7 @@ func SHA256(b []byte) []byte { return nil } -// VerifySignature checks that sig is msg's signature with pub. -func VerifySignature(msg []byte, sig []byte, pub []byte) bool { +// ECDsaVerify checks that sig is msg's signature with pub. +func ECDsaVerify(msg []byte, pub []byte, sig []byte) bool { return false }