6a4e312eac
We have additional logic for getting BaseExecFee policy value. This logic should be moved to interop context instead of being in Policer, because Policer is just an interface over Policy contract. After moving this logic to interop context, we need to use it to define BaseExecFee instead of (Policer).BaseExecFee. Thus, moving (*Blockchain).GetPrice to (*Context).GetPrice is necessary.
281 lines
7.9 KiB
Go
281 lines
7.9 KiB
Go
package crypto
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"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/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestECDSASecp256r1Verify(t *testing.T) {
|
|
testECDSAVerify(t, true)
|
|
}
|
|
|
|
func TestECDSASecp256k1Verify(t *testing.T) {
|
|
testECDSAVerify(t, false)
|
|
}
|
|
|
|
func testECDSAVerify(t *testing.T, isR1 bool) {
|
|
var priv *keys.PrivateKey
|
|
var err error
|
|
if isR1 {
|
|
priv, err = keys.NewPrivateKey()
|
|
} else {
|
|
priv, err = keys.NewSecp256k1PrivateKey()
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
verifyFunc := ECDSASecp256r1Verify
|
|
if !isR1 {
|
|
verifyFunc = ECDSASecp256k1Verify
|
|
}
|
|
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
|
ic := &interop.Context{DAO: dao.NewCached(d)}
|
|
runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) {
|
|
ic.SpawnVM()
|
|
for i := range args {
|
|
ic.VM.Estack().PushVal(args[i])
|
|
}
|
|
|
|
var err error
|
|
func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = fmt.Errorf("panic: %v", r)
|
|
}
|
|
}()
|
|
err = verifyFunc(ic)
|
|
}()
|
|
|
|
if isErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, ic.VM.Estack().Len())
|
|
require.Equal(t, result, ic.VM.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("signed interop item", func(t *testing.T) {
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{0, 1, 2}, 1)
|
|
msg := tx.GetSignedPart()
|
|
sign := priv.Sign(msg)
|
|
runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.NewInterop(tx))
|
|
})
|
|
|
|
t.Run("signed script container", func(t *testing.T) {
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{0, 1, 2}, 1)
|
|
msg := tx.GetSignedPart()
|
|
sign := priv.Sign(msg)
|
|
ic.Container = tx
|
|
runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.Null{})
|
|
})
|
|
|
|
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[0] = 0xFF // invalid prefix
|
|
runCase(t, true, false, sign, pub, msg)
|
|
})
|
|
|
|
t.Run("invalid message", func(t *testing.T) {
|
|
sign := priv.Sign(msg)
|
|
runCase(t, true, false, sign, priv.PublicKey().Bytes(),
|
|
stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(msg)}))
|
|
})
|
|
}
|
|
|
|
func initCHECKMULTISIG(isR1 bool, msg []byte, n int) ([]stackitem.Item, []stackitem.Item, map[string]*keys.PublicKey, error) {
|
|
var err error
|
|
|
|
keyMap := make(map[string]*keys.PublicKey)
|
|
pkeys := make([]*keys.PrivateKey, n)
|
|
pubs := make([]stackitem.Item, n)
|
|
for i := range pubs {
|
|
if isR1 {
|
|
pkeys[i], err = keys.NewPrivateKey()
|
|
} else {
|
|
pkeys[i], err = keys.NewSecp256k1PrivateKey()
|
|
}
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
pk := pkeys[i].PublicKey()
|
|
data := pk.Bytes()
|
|
pubs[i] = stackitem.NewByteArray(data)
|
|
keyMap[string(data)] = pk
|
|
}
|
|
|
|
sigs := make([]stackitem.Item, n)
|
|
for i := range sigs {
|
|
sig := pkeys[i].Sign(msg)
|
|
sigs[i] = stackitem.NewByteArray(sig)
|
|
}
|
|
|
|
return pubs, sigs, keyMap, nil
|
|
}
|
|
|
|
func subSlice(arr []stackitem.Item, indices []int) []stackitem.Item {
|
|
if indices == nil {
|
|
return arr
|
|
}
|
|
|
|
result := make([]stackitem.Item, len(indices))
|
|
for i, j := range indices {
|
|
result[i] = arr[j]
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func initCheckMultisigVMNoArgs(isR1 bool) *vm.VM {
|
|
buf := make([]byte, 5)
|
|
buf[0] = byte(opcode.SYSCALL)
|
|
if isR1 {
|
|
binary.LittleEndian.PutUint32(buf[1:], ecdsaSecp256r1CheckMultisigID)
|
|
} else {
|
|
binary.LittleEndian.PutUint32(buf[1:], ecdsaSecp256k1CheckMultisigID)
|
|
}
|
|
|
|
ic := &interop.Context{Trigger: trigger.Verification}
|
|
Register(ic)
|
|
v := ic.SpawnVM()
|
|
v.LoadScript(buf)
|
|
return v
|
|
}
|
|
|
|
func initCHECKMULTISIGVM(t *testing.T, isR1 bool, n int, ik, is []int) *vm.VM {
|
|
v := initCheckMultisigVMNoArgs(isR1)
|
|
msg := []byte("NEO - An Open Network For Smart Economy")
|
|
|
|
pubs, sigs, _, err := initCHECKMULTISIG(isR1, 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, isR1 bool, n int, is []int) {
|
|
v := initCHECKMULTISIGVM(t, isR1, n, nil, is)
|
|
|
|
require.NoError(t, v.Run())
|
|
assert.Equal(t, 1, v.Estack().Len())
|
|
assert.True(t, v.Estack().Pop().Bool())
|
|
}
|
|
|
|
func TestECDSASecp256r1CheckMultisigGood(t *testing.T) {
|
|
testCurveCHECKMULTISIGGood(t, true)
|
|
}
|
|
|
|
func TestECDSASecp256k1CheckMultisigGood(t *testing.T) {
|
|
testCurveCHECKMULTISIGGood(t, false)
|
|
}
|
|
|
|
func testCurveCHECKMULTISIGGood(t *testing.T, isR1 bool) {
|
|
t.Run("3_1", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 3, []int{1}) })
|
|
t.Run("2_2", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 2, []int{0, 1}) })
|
|
t.Run("3_3", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 3, []int{0, 1, 2}) })
|
|
t.Run("3_2", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 3, []int{0, 2}) })
|
|
t.Run("4_2", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 4, []int{0, 2}) })
|
|
t.Run("10_7", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 10, []int{2, 3, 4, 5, 6, 8, 9}) })
|
|
t.Run("12_9", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 12, []int{0, 1, 4, 5, 6, 7, 8, 9}) })
|
|
}
|
|
|
|
func testCHECKMULTISIGBad(t *testing.T, isR1 bool, isErr bool, n int, ik, is []int) {
|
|
v := initCHECKMULTISIGVM(t, isR1, n, ik, is)
|
|
|
|
if isErr {
|
|
require.Error(t, v.Run())
|
|
return
|
|
}
|
|
require.NoError(t, v.Run())
|
|
assert.Equal(t, 1, v.Estack().Len())
|
|
assert.False(t, v.Estack().Pop().Bool())
|
|
}
|
|
|
|
func TestECDSASecp256r1CheckMultisigBad(t *testing.T) {
|
|
testCurveCHECKMULTISIGBad(t, true)
|
|
}
|
|
|
|
func TestECDSASecp256k1CheckMultisigBad(t *testing.T) {
|
|
testCurveCHECKMULTISIGBad(t, false)
|
|
}
|
|
|
|
func testCurveCHECKMULTISIGBad(t *testing.T, isR1 bool) {
|
|
t.Run("1_1 wrong signature", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, false, 2, []int{0}, []int{1}) })
|
|
t.Run("3_2 wrong order", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, false, 3, []int{0, 2}, []int{2, 0}) })
|
|
t.Run("3_2 duplicate sig", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, false, 3, nil, []int{0, 0}) })
|
|
t.Run("1_2 too many signatures", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, true, 2, []int{0}, []int{0, 1}) })
|
|
t.Run("gas limit exceeded", func(t *testing.T) {
|
|
v := initCHECKMULTISIGVM(t, isR1, 1, []int{0}, []int{0})
|
|
v.GasLimit = fee.ECDSAVerifyPrice - 1
|
|
require.Error(t, v.Run())
|
|
})
|
|
|
|
msg := []byte("NEO - An Open Network For Smart Economy")
|
|
pubs, sigs, _, err := initCHECKMULTISIG(isR1, msg, 1)
|
|
require.NoError(t, err)
|
|
arr := stackitem.NewArray([]stackitem.Item{stackitem.NewArray(nil)})
|
|
|
|
t.Run("invalid message type", func(t *testing.T) {
|
|
v := initCheckMultisigVMNoArgs(isR1)
|
|
v.Estack().PushVal(sigs)
|
|
v.Estack().PushVal(pubs)
|
|
v.Estack().PushVal(stackitem.NewArray(nil))
|
|
require.Error(t, v.Run())
|
|
})
|
|
t.Run("invalid public keys", func(t *testing.T) {
|
|
v := initCheckMultisigVMNoArgs(isR1)
|
|
v.Estack().PushVal(sigs)
|
|
v.Estack().PushVal(arr)
|
|
v.Estack().PushVal(msg)
|
|
require.Error(t, v.Run())
|
|
})
|
|
t.Run("invalid signatures", func(t *testing.T) {
|
|
v := initCheckMultisigVMNoArgs(isR1)
|
|
v.Estack().PushVal(arr)
|
|
v.Estack().PushVal(pubs)
|
|
v.Estack().PushVal(msg)
|
|
require.Error(t, v.Run())
|
|
})
|
|
}
|