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]) }