vm: remove CHECKSIG/VERIFY/CHECKMULTISIG opcodes

This commit is contained in:
Evgenii Stratonikov 2020-04-20 11:52:05 +03:00
parent 4740d937aa
commit cde4ccf01c
4 changed files with 110 additions and 350 deletions

View file

@ -29,29 +29,6 @@ func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) util.Fixed8 {
return toFixed8(10) return toFixed8(10)
case opcode.HASH160, opcode.HASH256: case opcode.HASH160, opcode.HASH256:
return toFixed8(20) return toFixed8(20)
case opcode.CHECKSIG, opcode.VERIFY:
return toFixed8(100)
case opcode.CHECKMULTISIG:
estack := v.Estack()
if estack.Len() == 0 {
return toFixed8(1)
}
var cost int
item := estack.Peek(0)
switch item.Item().(type) {
case *vm.ArrayItem, *vm.StructItem:
cost = len(item.Array())
default:
cost = int(item.BigInt().Int64())
}
if cost < 1 {
return toFixed8(1)
}
return toFixed8(int64(100 * cost))
default: default:
return toFixed8(1) return toFixed8(1)
} }

View file

@ -0,0 +1,110 @@
package crypto
import (
"encoding/binary"
"testing"
"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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func initCHECKMULTISIG(msg []byte, n int) ([]vm.StackItem, []vm.StackItem, map[string]*keys.PublicKey, error) {
var err error
keyMap := make(map[string]*keys.PublicKey)
pkeys := make([]*keys.PrivateKey, n)
pubs := make([]vm.StackItem, n)
for i := range pubs {
pkeys[i], err = keys.NewPrivateKey()
if err != nil {
return nil, nil, nil, err
}
pk := pkeys[i].PublicKey()
data := pk.Bytes()
pubs[i] = vm.NewByteArrayItem(data)
keyMap[string(data)] = pk
}
sigs := make([]vm.StackItem, n)
for i := range sigs {
sig := pkeys[i].Sign(msg)
sigs[i] = vm.NewByteArrayItem(sig)
}
return pubs, sigs, keyMap, nil
}
func subSlice(arr []vm.StackItem, indices []int) []vm.StackItem {
if indices == nil {
return arr
}
result := make([]vm.StackItem, len(indices))
for i, j := range indices {
result[i] = arr[j]
}
return result
}
func initCHECKMULTISIGVM(t *testing.T, n int, ik, is []int) *vm.VM {
buf := make([]byte, 5)
buf[0] = byte(opcode.SYSCALL)
binary.LittleEndian.PutUint32(buf[1:], ecdsaCheckMultisigID)
v := vm.New()
ic := &interop.Context{Trigger: trigger.Verification}
v.RegisterInteropGetter(GetInterop(ic))
v.LoadScript(buf)
msg := []byte("NEO - An Open Network For Smart Economy")
pubs, sigs, _, err := initCHECKMULTISIG(msg, n)
require.NoError(t, err)
pubs = subSlice(pubs, ik)
sigs = subSlice(sigs, is)
v.Estack().PushVal(sigs)
v.Estack().PushVal(pubs)
v.Estack().PushVal(msg)
return v
}
func testCHECKMULTISIGGood(t *testing.T, n int, is []int) {
v := initCHECKMULTISIGVM(t, n, nil, is)
require.NoError(t, v.Run())
assert.Equal(t, 1, v.Estack().Len())
assert.True(t, v.Estack().Pop().Bool())
}
func TestCHECKMULTISIGGood(t *testing.T) {
t.Run("3_1", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{1}) })
t.Run("2_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 2, []int{0, 1}) })
t.Run("3_3", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 1, 2}) })
t.Run("3_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 2}) })
t.Run("4_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 4, []int{0, 2}) })
t.Run("10_7", func(t *testing.T) { testCHECKMULTISIGGood(t, 10, []int{2, 3, 4, 5, 6, 8, 9}) })
t.Run("12_9", func(t *testing.T) { testCHECKMULTISIGGood(t, 12, []int{0, 1, 4, 5, 6, 7, 8, 9}) })
}
func testCHECKMULTISIGBad(t *testing.T, n int, ik, is []int) {
v := initCHECKMULTISIGVM(t, n, ik, is)
require.NoError(t, v.Run())
assert.Equal(t, 1, v.Estack().Len())
assert.False(t, v.Estack().Pop().Bool())
}
func TestCHECKMULTISIGBad(t *testing.T) {
t.Run("1_1 wrong signature", func(t *testing.T) { testCHECKMULTISIGBad(t, 2, []int{0}, []int{1}) })
t.Run("3_2 wrong order", func(t *testing.T) { testCHECKMULTISIGBad(t, 3, []int{0, 2}, []int{2, 0}) })
t.Run("3_2 duplicate sig", func(t *testing.T) { testCHECKMULTISIGBad(t, 3, nil, []int{0, 0}) })
}

View file

@ -1196,45 +1196,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.astack = v.Context().astack v.astack = v.Context().astack
} }
case opcode.CHECKSIG, opcode.VERIFY:
var hashToCheck []byte
keyb := v.estack.Pop().Bytes()
signature := v.estack.Pop().Bytes()
if op == opcode.CHECKSIG {
if v.checkhash == nil {
panic("VM is not set up properly for signature checks")
}
hashToCheck = v.checkhash
} else { // VERIFY
msg := v.estack.Pop().Bytes()
hashToCheck = hash.Sha256(msg).BytesBE()
}
pkey := v.bytesToPublicKey(keyb)
res := pkey.Verify(signature, hashToCheck)
v.estack.PushVal(res)
case opcode.CHECKMULTISIG:
pkeys, err := v.estack.PopSigElements()
if err != nil {
panic(fmt.Sprintf("wrong parameters: %s", err.Error()))
}
sigs, err := v.estack.PopSigElements()
if err != nil {
panic(fmt.Sprintf("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) {
panic("more signatures than there are keys")
}
if v.checkhash == nil {
panic("VM is not set up properly for signature checks")
}
sigok := CheckMultisigPar(v, v.checkhash, pkeys, sigs)
v.estack.PushVal(sigok)
case opcode.NEWMAP: case opcode.NEWMAP:
v.estack.Push(&Element{value: NewMapItem()}) v.estack.Push(&Element{value: NewMapItem()})

View file

@ -8,8 +8,6 @@ import (
"math/rand" "math/rand"
"testing" "testing"
"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/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
@ -2603,292 +2601,6 @@ func TestREMOVEMap(t *testing.T) {
assert.Equal(t, makeStackItem(false), vm.estack.Pop().value) assert.Equal(t, makeStackItem(false), vm.estack.Pop().value)
} }
func TestCHECKSIGNoArgs(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestCHECKSIGOneArg(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(pbytes)
checkVMFailed(t, vm)
}
func TestCHECKSIGNoSigLoaded(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := "NEO - An Open Network For Smart Economy"
sig := pk.Sign([]byte(msg))
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
checkVMFailed(t, vm)
}
func TestCHECKSIGBadKey(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig := pk.Sign(msg)
pbytes := pk.PublicKey().Bytes()[:4]
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
checkVMFailed(t, vm)
}
func TestCHECKSIGWrongSig(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig := pk.Sign(msg)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal(util.ArrayReverse(sig))
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestCHECKSIGGood(t *testing.T) {
prog := makeProgram(opcode.CHECKSIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig := pk.Sign(msg)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestVERIFYGood(t *testing.T) {
prog := makeProgram(opcode.VERIFY)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig := pk.Sign(msg)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(msg)
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, true, vm.estack.Pop().Bool())
}
func TestVERIFYBad(t *testing.T) {
prog := makeProgram(opcode.VERIFY)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig := pk.Sign(msg)
pbytes := pk.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal(util.ArrayReverse(msg))
vm.estack.PushVal(sig)
vm.estack.PushVal(pbytes)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func TestCHECKMULTISIGNoArgs(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
vm := load(prog)
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGOneArg(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk, err := keys.NewPrivateKey()
assert.Nil(t, err)
vm := load(prog)
pbytes := pk.PublicKey().Bytes()
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGNotEnoughKeys(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1 := pk1.Sign(msg)
sig2 := pk2.Sign(msg)
pbytes1 := pk1.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGNoHash(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1 := pk1.Sign(msg)
sig2 := pk2.Sign(msg)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()
vm := load(prog)
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGBadKey(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1 := pk1.Sign(msg)
sig2 := pk2.Sign(msg)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()[:4]
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(sig1), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
checkVMFailed(t, vm)
}
func TestCHECKMULTISIGBadSig(t *testing.T) {
prog := makeProgram(opcode.CHECKMULTISIG)
pk1, err := keys.NewPrivateKey()
assert.Nil(t, err)
pk2, err := keys.NewPrivateKey()
assert.Nil(t, err)
msg := []byte("NEO - An Open Network For Smart Economy")
sig1 := pk1.Sign(msg)
sig2 := pk2.Sign(msg)
pbytes1 := pk1.PublicKey().Bytes()
pbytes2 := pk2.PublicKey().Bytes()
vm := load(prog)
vm.SetCheckedHash(hash.Sha256(msg).BytesBE())
vm.estack.PushVal([]StackItem{NewByteArrayItem(util.ArrayReverse(sig1)), NewByteArrayItem(sig2)})
vm.estack.PushVal([]StackItem{NewByteArrayItem(pbytes1), NewByteArrayItem(pbytes2)})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, false, vm.estack.Pop().Bool())
}
func initCHECKMULTISIG(msg []byte, n int) ([]StackItem, []StackItem, map[string]*keys.PublicKey, error) {
var err error
keyMap := make(map[string]*keys.PublicKey)
pkeys := make([]*keys.PrivateKey, n)
pubs := make([]StackItem, n)
for i := range pubs {
pkeys[i], err = keys.NewPrivateKey()
if err != nil {
return nil, nil, nil, err
}
pk := pkeys[i].PublicKey()
data := pk.Bytes()
pubs[i] = NewByteArrayItem(data)
keyMap[string(data)] = pk
}
sigs := make([]StackItem, n)
for i := range sigs {
sig := pkeys[i].Sign(msg)
sigs[i] = NewByteArrayItem(sig)
}
return pubs, sigs, keyMap, nil
}
func subSlice(arr []StackItem, indices []int) []StackItem {
if indices == nil {
return arr
}
result := make([]StackItem, len(indices))
for i, j := range indices {
result[i] = arr[j]
}
return result
}
func initCHECKMULTISIGVM(t *testing.T, n int, ik, is []int) *VM {
prog := makeProgram(opcode.CHECKMULTISIG)
v := load(prog)
msg := []byte("NEO - An Open Network For Smart Economy")
v.SetCheckedHash(hash.Sha256(msg).BytesBE())
pubs, sigs, _, err := initCHECKMULTISIG(msg, n)
require.NoError(t, err)
pubs = subSlice(pubs, ik)
sigs = subSlice(sigs, is)
v.estack.PushVal(sigs)
v.estack.PushVal(pubs)
return v
}
func testCHECKMULTISIGGood(t *testing.T, n int, is []int) {
v := initCHECKMULTISIGVM(t, n, nil, is)
runVM(t, v)
assert.Equal(t, 1, v.estack.Len())
assert.True(t, v.estack.Pop().Bool())
}
func TestCHECKMULTISIGGood(t *testing.T) {
t.Run("3_1", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{1}) })
t.Run("2_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 2, []int{0, 1}) })
t.Run("3_3", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 1, 2}) })
t.Run("3_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 2}) })
t.Run("4_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 4, []int{0, 2}) })
t.Run("10_7", func(t *testing.T) { testCHECKMULTISIGGood(t, 10, []int{2, 3, 4, 5, 6, 8, 9}) })
t.Run("12_9", func(t *testing.T) { testCHECKMULTISIGGood(t, 12, []int{0, 1, 4, 5, 6, 7, 8, 9}) })
}
func testCHECKMULTISIGBad(t *testing.T, n int, ik, is []int) {
v := initCHECKMULTISIGVM(t, n, ik, is)
runVM(t, v)
assert.Equal(t, 1, v.estack.Len())
assert.False(t, v.estack.Pop().Bool())
}
func TestCHECKMULTISIGBad(t *testing.T) {
t.Run("1_1 wrong signature", func(t *testing.T) { testCHECKMULTISIGBad(t, 2, []int{0}, []int{1}) })
t.Run("3_2 wrong order", func(t *testing.T) { testCHECKMULTISIGBad(t, 3, []int{0, 2}, []int{2, 0}) })
t.Run("3_2 duplicate sig", func(t *testing.T) { testCHECKMULTISIGBad(t, 3, nil, []int{0, 0}) })
}
func TestSWAPGood(t *testing.T) { func TestSWAPGood(t *testing.T) {
prog := makeProgram(opcode.SWAP) prog := makeProgram(opcode.SWAP)
vm := load(prog) vm := load(prog)