Merge pull request #858 from nspcc-dev/neo3/transition

vm: remove FIXME comments from vm

Fix #477.
This commit is contained in:
Roman Khimov 2020-04-17 12:04:24 +03:00 committed by GitHub
commit ffff9b93ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 96 additions and 146 deletions

2
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "pkg/vm/testdata/neo-vm"]
path = pkg/vm/testdata/neo-vm
url = https://github.com/neo-project/neo-vm.git
branch = master-2.x
branch = master

View file

@ -6,6 +6,7 @@ import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/stretchr/testify/require"
)
@ -54,7 +55,7 @@ func getPanicSource(need bool, message string) string {
}
func logGetter(logs *[]string) vm.InteropGetterFunc {
logID := vm.InteropNameToID([]byte("Neo.Runtime.Log"))
logID := emit.InteropNameToID([]byte("Neo.Runtime.Log"))
return func(id uint32) *vm.InteropFuncPrice {
if id != logID {
return nil

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -18,7 +19,7 @@ func TestVerifyGood(t *testing.T) {
src := getVerifyProg(pub, sig, msg)
v, p := vmAndCompileInterop(t, src)
p.interops[vm.InteropNameToID([]byte("Neo.Crypto.ECDsaVerify"))] = func(v *vm.VM) error {
p.interops[emit.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())

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -80,10 +81,10 @@ func newStoragePlugin() *storagePlugin {
mem: make(map[string][]byte),
interops: make(map[uint32]vm.InteropFunc),
}
s.interops[vm.InteropNameToID([]byte("Neo.Storage.Get"))] = s.Get
s.interops[vm.InteropNameToID([]byte("Neo.Storage.Put"))] = s.Put
s.interops[vm.InteropNameToID([]byte("Neo.Storage.GetContext"))] = s.GetContext
s.interops[vm.InteropNameToID([]byte("Neo.Runtime.Notify"))] = s.Notify
s.interops[emit.InteropNameToID([]byte("Neo.Storage.Get"))] = s.Get
s.interops[emit.InteropNameToID([]byte("Neo.Storage.Put"))] = s.Put
s.interops[emit.InteropNameToID([]byte("Neo.Storage.GetContext"))] = s.GetContext
s.interops[emit.InteropNameToID([]byte("Neo.Runtime.Notify"))] = s.Notify
return s
}

View file

@ -24,13 +24,13 @@ func TestGetPrice(t *testing.T) {
t.Run("Neo.Asset.Create", func(t *testing.T) {
// Neo.Asset.Create: 83c5c61f
v.Load([]byte{0x68, 0x04, 0x83, 0xc5, 0xc6, 0x1f})
v.Load([]byte{0x68, 0x83, 0xc5, 0xc6, 0x1f})
checkGas(t, util.Fixed8FromInt64(5000), v)
})
t.Run("Neo.Asset.Renew", func(t *testing.T) {
// Neo.Asset.Renew: 78849071 (requires push 09 push 09 before)
v.Load([]byte{0x59, 0x59, 0x68, 0x04, 0x78, 0x84, 0x90, 0x71})
v.Load([]byte{0x59, 0x59, 0x68, 0x78, 0x84, 0x90, 0x71})
require.NoError(t, v.StepInto()) // push 9
require.NoError(t, v.StepInto()) // push 9
@ -39,7 +39,7 @@ func TestGetPrice(t *testing.T) {
t.Run("Neo.Contract.Create (no props)", func(t *testing.T) {
// Neo.Contract.Create: f66ca56e (requires push properties on fourth position)
v.Load([]byte{0x00, 0x00, 0x00, 0x00, 0x68, 0x04, 0xf6, 0x6c, 0xa5, 0x6e})
v.Load([]byte{0x00, 0x00, 0x00, 0x00, 0x68, 0xf6, 0x6c, 0xa5, 0x6e})
require.NoError(t, v.StepInto()) // push 0 - ContractPropertyState.NoProperty
require.NoError(t, v.StepInto()) // push 0
require.NoError(t, v.StepInto()) // push 0
@ -50,7 +50,7 @@ func TestGetPrice(t *testing.T) {
t.Run("Neo.Contract.Create (has storage)", func(t *testing.T) {
// Neo.Contract.Create: f66ca56e (requires push properties on fourth position)
v.Load([]byte{0x51, 0x00, 0x00, 0x00, 0x68, 0x04, 0xf6, 0x6c, 0xa5, 0x6e})
v.Load([]byte{0x51, 0x00, 0x00, 0x00, 0x68, 0xf6, 0x6c, 0xa5, 0x6e})
require.NoError(t, v.StepInto()) // push 01 - ContractPropertyState.HasStorage
require.NoError(t, v.StepInto()) // push 0
require.NoError(t, v.StepInto()) // push 0
@ -61,7 +61,7 @@ func TestGetPrice(t *testing.T) {
t.Run("Neo.Contract.Create (has dynamic invoke)", func(t *testing.T) {
// Neo.Contract.Create: f66ca56e (requires push properties on fourth position)
v.Load([]byte{0x52, 0x00, 0x00, 0x00, 0x68, 0x04, 0xf6, 0x6c, 0xa5, 0x6e})
v.Load([]byte{0x52, 0x00, 0x00, 0x00, 0x68, 0xf6, 0x6c, 0xa5, 0x6e})
require.NoError(t, v.StepInto()) // push 02 - ContractPropertyState.HasDynamicInvoke
require.NoError(t, v.StepInto()) // push 0
require.NoError(t, v.StepInto()) // push 0
@ -72,7 +72,7 @@ func TestGetPrice(t *testing.T) {
t.Run("Neo.Contract.Create (has both storage and dynamic invoke)", func(t *testing.T) {
// Neo.Contract.Create: f66ca56e (requires push properties on fourth position)
v.Load([]byte{0x53, 0x00, 0x00, 0x00, 0x68, 0x04, 0xf6, 0x6c, 0xa5, 0x6e})
v.Load([]byte{0x53, 0x00, 0x00, 0x00, 0x68, 0xf6, 0x6c, 0xa5, 0x6e})
require.NoError(t, v.StepInto()) // push 03 - HasStorage and HasDynamicInvoke
require.NoError(t, v.StepInto()) // push 0
require.NoError(t, v.StepInto()) // push 0
@ -83,7 +83,7 @@ func TestGetPrice(t *testing.T) {
t.Run("Neo.Contract.Migrate", func(t *testing.T) {
// Neo.Contract.Migrate: 471b6290 (requires push properties on fourth position)
v.Load([]byte{0x00, 0x00, 0x00, 0x00, 0x68, 0x04, 0x47, 0x1b, 0x62, 0x90})
v.Load([]byte{0x00, 0x00, 0x00, 0x00, 0x68, 0x47, 0x1b, 0x62, 0x90})
require.NoError(t, v.StepInto()) // push 0 - ContractPropertyState.NoProperty
require.NoError(t, v.StepInto()) // push 0
require.NoError(t, v.StepInto()) // push 0
@ -94,7 +94,7 @@ func TestGetPrice(t *testing.T) {
t.Run("System.Storage.Put", func(t *testing.T) {
// System.Storage.Put: e63f1884 (requires push key and value)
v.Load([]byte{0x53, 0x53, 0x00, 0x68, 0x04, 0xe6, 0x3f, 0x18, 0x84})
v.Load([]byte{0x53, 0x53, 0x00, 0x68, 0xe6, 0x3f, 0x18, 0x84})
require.NoError(t, v.StepInto()) // push 03 (length 1)
require.NoError(t, v.StepInto()) // push 03 (length 1)
require.NoError(t, v.StepInto()) // push 00
@ -104,7 +104,7 @@ func TestGetPrice(t *testing.T) {
t.Run("System.Storage.PutEx", func(t *testing.T) {
// System.Storage.PutEx: 73e19b3a (requires push key and value)
v.Load([]byte{0x53, 0x53, 0x00, 0x68, 0x04, 0x73, 0xe1, 0x9b, 0x3a})
v.Load([]byte{0x53, 0x53, 0x00, 0x68, 0x73, 0xe1, 0x9b, 0x3a})
require.NoError(t, v.StepInto()) // push 03 (length 1)
require.NoError(t, v.StepInto()) // push 03 (length 1)
require.NoError(t, v.StepInto()) // push 00

View file

@ -19,6 +19,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
// SpawnVM returns a VM with script getter and interop functions set
@ -263,7 +264,7 @@ var neoInterops = []interop.Function{
// Function slice and then sorts it.
func initIDinInteropsSlice(iops []interop.Function) {
for i := range iops {
iops[i].ID = vm.InteropNameToID([]byte(iops[i].Name))
iops[i].ID = emit.InteropNameToID([]byte(iops[i].Name))
}
sort.Slice(iops, func(i, j int) bool {
return iops[i].ID < iops[j].ID

View file

@ -63,7 +63,7 @@ func (cs *Contracts) SetNEO(n *NEO) {
func NewContractMD(name string) *ContractMD {
c := &ContractMD{
ServiceName: name,
ServiceID: vm.InteropNameToID([]byte(name)),
ServiceID: emit.InteropNameToID([]byte(name)),
Methods: make(map[string]MethodAndPrice),
}

View file

@ -43,20 +43,20 @@ type rpcTestCase struct {
check func(t *testing.T, e *executor, result interface{})
}
const testContractHash = "c2789e5ab9bab828743833965b1df0d5fbcc206f"
const testContractHash = "1b5cbfaa2e54b584d3240e38fc4bd413a65ea40c"
var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": {
{
name: "positive",
params: `["e5a6be3cd796e2284a55ebb2061f85d3f199263bbf659dbd5b670a6506e7a4a0"]`,
params: `["d98e4b58783aa9665efcc5be19ee432772a94462efc8a5e4165776e6dd8c4e92"]`,
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
check: func(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.ApplicationLog)
require.True(t, ok)
expectedTxHash, err := util.Uint256DecodeStringLE("e5a6be3cd796e2284a55ebb2061f85d3f199263bbf659dbd5b670a6506e7a4a0")
expectedTxHash, err := util.Uint256DecodeStringLE("d98e4b58783aa9665efcc5be19ee432772a94462efc8a5e4165776e6dd8c4e92")
require.NoError(t, err)
assert.Equal(t, expectedTxHash, res.TxHash)
assert.Equal(t, 1, len(res.Executions))
@ -405,25 +405,25 @@ var rpcTestCases = map[string][]rpcTestCase{
"getblockheader": {
{
name: "positive, no verbose",
params: `["02b9e6f634578f34c6026f1676e25aa04f58889a320fba8bc632c6fe48c26cf1"]`,
params: `["60c00bfa7f283d1821e1bc8c1c83aa534f325968944d73fa25cf7235af17d608"]`,
result: func(e *executor) interface{} {
expected := "000000009456b8c7f89bdfe012a30a2be9af5218e048ae7372115ce3e39cc847c01e9e7f9cffa26c112eb147a995520e47afbc45a192d9bf1a8e9b8f80497e5d92c4e9f2129e965e010000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401408ee2187395071a089e3b9743b1a718fa28a8700676451317f7454aade98cf779b0afb5634a7e104f92e2df8e8b1c1b7b3b0e9fd61f4c0e9ea07ce112cef12b9a40fcb441c89b0c2047ba71a86d759bc2554dda55369798e6ab63e6c7713b06a8178f13586edf6a673f20bf5ed7bdc727f9e0c6f487cf667e505a26236d73887ae940691972a6f4ccef19a03a3d5948dfcf992af2c6a3c5b033a0f9235d2b8967291d9f61495ff1a681f240517c8d892eaa805e667bca40abcf03d441c68a9ec59deb4005890a59d83c7cb0416895e4768123e900603d5b140bf799bc9879fb628c598193db2b7a8e07ab54733c1c67b60c97a1a8048961cc0afd0b048e878866ffd7298b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"
expected := "000000009456b8c7f89bdfe012a30a2be9af5218e048ae7372115ce3e39cc847c01e9e7f9cffa26c112eb147a995520e47afbc45a192d9bf1a8e9b8f80497e5d92c4e9f2011e975e010000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401404be65e52d50121ce243eb27f7a1c16637e753e847e9c027f255bc5e2bae45f1c99d8c1014e5abe829b9a229904540b7010ae6fb2bb57f88d70179ec617dea35440c9d51c5980e016f5cb51734554341764572b0644010952761878b1966d2b013ceaf18958891060dde3a33d23118f27ef21e991f913a8740c716c14aa19483ea0409a44efd5607f7b8f09dd4f242882713b40866c2aeab9aaa5f92b92dbaf84ed0c039fb1790888ab36d77c8b6520b2bb58ab43b219be3272145cf0f6fd091e33e540ec1e52377a56bb6e0f82329ac1a70ab06cb7e23c149bcc194fa483a316de33468191b16ab1fafb2f1a1af79a0cd6e7e275d3a3ed56f8b0ecbcd6e26f93a3957b8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"
return &expected
},
},
{
name: "positive, verbose 0",
params: `["02b9e6f634578f34c6026f1676e25aa04f58889a320fba8bc632c6fe48c26cf1", 0]`,
params: `["60c00bfa7f283d1821e1bc8c1c83aa534f325968944d73fa25cf7235af17d608", 0]`,
result: func(e *executor) interface{} {
expected := "000000009456b8c7f89bdfe012a30a2be9af5218e048ae7372115ce3e39cc847c01e9e7f9cffa26c112eb147a995520e47afbc45a192d9bf1a8e9b8f80497e5d92c4e9f2129e965e010000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401408ee2187395071a089e3b9743b1a718fa28a8700676451317f7454aade98cf779b0afb5634a7e104f92e2df8e8b1c1b7b3b0e9fd61f4c0e9ea07ce112cef12b9a40fcb441c89b0c2047ba71a86d759bc2554dda55369798e6ab63e6c7713b06a8178f13586edf6a673f20bf5ed7bdc727f9e0c6f487cf667e505a26236d73887ae940691972a6f4ccef19a03a3d5948dfcf992af2c6a3c5b033a0f9235d2b8967291d9f61495ff1a681f240517c8d892eaa805e667bca40abcf03d441c68a9ec59deb4005890a59d83c7cb0416895e4768123e900603d5b140bf799bc9879fb628c598193db2b7a8e07ab54733c1c67b60c97a1a8048961cc0afd0b048e878866ffd7298b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"
expected := "000000009456b8c7f89bdfe012a30a2be9af5218e048ae7372115ce3e39cc847c01e9e7f9cffa26c112eb147a995520e47afbc45a192d9bf1a8e9b8f80497e5d92c4e9f2011e975e010000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401404be65e52d50121ce243eb27f7a1c16637e753e847e9c027f255bc5e2bae45f1c99d8c1014e5abe829b9a229904540b7010ae6fb2bb57f88d70179ec617dea35440c9d51c5980e016f5cb51734554341764572b0644010952761878b1966d2b013ceaf18958891060dde3a33d23118f27ef21e991f913a8740c716c14aa19483ea0409a44efd5607f7b8f09dd4f242882713b40866c2aeab9aaa5f92b92dbaf84ed0c039fb1790888ab36d77c8b6520b2bb58ab43b219be3272145cf0f6fd091e33e540ec1e52377a56bb6e0f82329ac1a70ab06cb7e23c149bcc194fa483a316de33468191b16ab1fafb2f1a1af79a0cd6e7e275d3a3ed56f8b0ecbcd6e26f93a3957b8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"
return &expected
},
},
{
name: "positive, verbose !=0",
params: `["02b9e6f634578f34c6026f1676e25aa04f58889a320fba8bc632c6fe48c26cf1", 2]`,
params: `["60c00bfa7f283d1821e1bc8c1c83aa534f325968944d73fa25cf7235af17d608", 2]`,
result: func(e *executor) interface{} {
hash, err := util.Uint256DecodeStringLE("02b9e6f634578f34c6026f1676e25aa04f58889a320fba8bc632c6fe48c26cf1")
hash, err := util.Uint256DecodeStringLE("60c00bfa7f283d1821e1bc8c1c83aa534f325968944d73fa25cf7235af17d608")
if err != nil {
panic("can not decode hash parameter")
}
@ -822,7 +822,7 @@ var rpcTestCases = map[string][]rpcTestCase{
{
// If you are planning to modify test chain from `testblocks.acc`, please, update param value (first block)
name: "empty block",
params: `["0000000029e2ea0ec5c9aa98fa736b52750916b3b8ab5cdbe7647fb6d0b7f6dac35865640000000000000000000000000000000000000000000000000000000000000000e29e965ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140efdc94ac1e5c7588a1e70aa2ada3450631f30b15676861e7f32159f0e0b97e5fec8dd9870d92755e76b2cf3d6a90f06c2f6a7b782248ac4b765433beb6891a4840551f59a1e88395290ea2273500c60fc55b0d33ab6c3ddd565177f77083e82aaffca3553a0a116b457d5101f8627da39a3a24f19e2d04fab7e65670fda6a50f4140576969f478e6150b128b1149dd3c8b663f27bda53a6815fe177d52abc6ad40311a4fcba14a0d9286d6cda521cd9bc0ade09a66e88059da84363512c6110f4dc94008e8c0e6f3835281690fa0d594a6f5f1914b2f047fe835b134641b5a19935a4f6b10931ad168e47f96f6bf98380c0f060d4b25ae71869c4ef9d2a92a262eceeb8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"]`,
params: `["00000000547e0fa207c116e25fd69a04b99e446e409ca23c7863bafbd585abeb468314c00000000000000000000000000000000000000000000000000000000000000000d21e975ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401404fb770b14c4e7bd2461dbf238ca197844a724388d15cfeb96a07cb8cb52733f3f7adcc5bbd33c8dc19103116f1fe6c3151a14e3db40585823cc3ce153a0426ec40fd880f3d029e95dcb4612b40f31d5111a961345bc2090689a7462c12c85006ed281216331e1169d41cc0a8780b0befc22b9d3ba147e331acfb858675ebaaf8de4055922389015e0a96e07c9ad987eda4a642f0436843966821e620b89bf84e8ec22a33c308ad7e9815b1d0bebd93289db1828bec516d3b2ccfcaf080d2b7ea385f408005e2ff7387c50393407b125a09f42970b4b8033737e0b5ee04c1dfc9e6ad15da879cd46b3177b302ee1c109acbce4af689bb7e89978e4709e97d2db76c7ada8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"]`,
fail: true,
},
{
@ -848,7 +848,7 @@ var rpcTestCases = map[string][]rpcTestCase{
{
name: "positive",
// If you are planning to modify test chain from `testblocks.acc`, please, update param value (second block)
params: `["0000000029e2ea0ec5c9aa98fa736b52750916b3b8ab5cdbe7647fb6d0b7f6dac3586564980135e10c52e5444c962659b388a5b226114a45d3912a88700ac6b05aaaf961e29e965ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140a0ed19af416fc4edb6e32b4687290d4eff1262af338dc8e55021a13f500ce94377c0fca593db15e29a0ee11b229d8bb48bd75203834829aee47096ff375ef8e240bcccad08d1ae7cc47c099eb4321a1fe07cf77a434841fd48a04aaf19204ec5bbf465533a2845a323710ea068316f50be82c384c82e0a5a509b2a9442c6a7909640de0a52dd615efac136971be900700ec39a84198e7d1639e880955022901a8d98ff39f00e858b41686047d68d7c18963b9def50a85907eb3b0bfbba7b2fa64d814060670b0c648757594cdba09aafc158ee6f7e88fbd2aa088a8c57bff2c8e7ddd83fa242632e1b6f82e3af2b3d0428c3936833cefb7ced1cc3c50ec29da906eb498b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae01000014000000b004000000000000"]`,
params: `["00000000547e0fa207c116e25fd69a04b99e446e409ca23c7863bafbd585abeb468314c0980135e10c52e5444c962659b388a5b226114a45d3912a88700ac6b05aaaf961d21e975ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140dc1690f42fd67440defaf6385ad5d77b3a2ffba6270c22aa20373eab011cc5eb46e1aeb0f5f9d3f78ebd29d7fb3bb47777e2a0793e4594761e74dc6867834f6a405ebde4af3a4fedefc33c7debf58a159dcd4a66e396c007bf9cc185f422ff449849efcbbb8d772df213b3b412b7ca4188ec3a4ac841c3374ae4cac3277e16d39640ee9e51540a5c37100e0964f279938561096df6693429e692ce39553f63db53ce4f4d4d777e00299c594f7c8095410b03b5ca253f6946e081069180890a443e8e40981c77a1d7a9e9d71e54ba3edd8d740b24b723efefd7eca87ba9e5c2abb2a07dea5ab83e643cabc20af3d2e90990e668973850bd7be723097f34a47ac60fddc28b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae01000014000000b004000000000000"]`,
result: func(e *executor) interface{} {
v := true
return &v

Binary file not shown.

Binary file not shown.

View file

@ -74,7 +74,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
var numtoread int
switch instr {
case opcode.PUSHDATA1, opcode.SYSCALL:
case opcode.PUSHDATA1:
if c.nextip >= len(c.prog) {
err = errNoInstParam
} else {
@ -101,7 +101,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
}
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL, opcode.CALLED, opcode.CALLEDT:
numtoread = 2
case opcode.CALLI:
case opcode.CALLI, opcode.SYSCALL:
numtoread = 4
case opcode.APPCALL, opcode.TAILCALL:
numtoread = 20

View file

@ -80,10 +80,13 @@ func getEffectiveSize(buf []byte, isNeg bool) int {
}
// IntToBytes converts integer to a slice in little-endian format.
// Note: NEO3 serialization differs from default C# BigInteger.ToByteArray()
// when n == 0. For zero is equal to empty slice in NEO3.
// https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16
func IntToBytes(n *big.Int) []byte {
sign := n.Sign()
if sign == 0 {
return []byte{0}
return []byte{}
}
var ws []big.Word

View file

@ -16,7 +16,7 @@ var testCases = []struct {
number int64
buf []byte
}{
{0, []byte{0}},
{0, []byte{}},
{1, []byte{1}},
{-1, []byte{255}},
{2, []byte{2}},
@ -52,7 +52,7 @@ var testCases = []struct {
{-9187484529235886209, []byte{127, 127, 127, 127, 127, 127, 127, 128}},
// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Runtime.Numerics/tests/BigInteger/ToByteArray.cs#L14
{0, []byte{0x00}},
// {0, []byte{0x00}}, commented because this test check default `BigInteger` serialization, not NEO3 serialization.
{3, []byte{0x03}},
{128, []byte{0x80, 0x00}},
{200, []byte{0xc8, 0x00}},

View file

@ -1,6 +1,7 @@
package emit
import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
@ -107,9 +108,8 @@ func Syscall(w *io.BinWriter, api string) {
w.Err = errors.New("syscall api cannot be of length 0")
return
}
buf := make([]byte, len(api)+1)
buf[0] = byte(len(api))
copy(buf[1:], api)
buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, InteropNameToID([]byte(api)))
Instruction(w, opcode.SYSCALL, buf)
}
@ -168,3 +168,9 @@ func isInstructionJmp(op opcode.Opcode) bool {
}
return false
}
// InteropNameToID returns an identificator of the method based on its name.
func InteropNameToID(name []byte) uint32 {
h := sha256.Sum256(name)
return binary.LittleEndian.Uint32(h[:4])
}

View file

@ -165,9 +165,9 @@ func TestEmitSyscall(t *testing.T) {
for _, syscall := range syscalls {
Syscall(buf.BinWriter, syscall)
result := buf.Bytes()
assert.Equal(t, 5, len(result))
assert.Equal(t, opcode.Opcode(result[0]), opcode.SYSCALL)
assert.Equal(t, result[1], uint8(len(syscall)))
assert.Equal(t, result[2:], []byte(syscall))
assert.Equal(t, binary.LittleEndian.Uint32(result[1:]), InteropNameToID([]byte(syscall)))
buf.Reset()
}

View file

@ -1,11 +1,11 @@
package vm
import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"sort"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
// InteropFunc allows to hook into the VM.
@ -28,35 +28,35 @@ type interopIDFuncPrice struct {
type InteropGetterFunc func(uint32) *InteropFuncPrice
var defaultVMInterops = []interopIDFuncPrice{
{InteropNameToID([]byte("Neo.Runtime.Log")),
{emit.InteropNameToID([]byte("Neo.Runtime.Log")),
InteropFuncPrice{runtimeLog, 1}},
{InteropNameToID([]byte("Neo.Runtime.Notify")),
{emit.InteropNameToID([]byte("Neo.Runtime.Notify")),
InteropFuncPrice{runtimeNotify, 1}},
{InteropNameToID([]byte("Neo.Runtime.Serialize")),
{emit.InteropNameToID([]byte("Neo.Runtime.Serialize")),
InteropFuncPrice{RuntimeSerialize, 1}},
{InteropNameToID([]byte("System.Runtime.Serialize")),
{emit.InteropNameToID([]byte("System.Runtime.Serialize")),
InteropFuncPrice{RuntimeSerialize, 1}},
{InteropNameToID([]byte("Neo.Runtime.Deserialize")),
{emit.InteropNameToID([]byte("Neo.Runtime.Deserialize")),
InteropFuncPrice{RuntimeDeserialize, 1}},
{InteropNameToID([]byte("System.Runtime.Deserialize")),
{emit.InteropNameToID([]byte("System.Runtime.Deserialize")),
InteropFuncPrice{RuntimeDeserialize, 1}},
{InteropNameToID([]byte("Neo.Enumerator.Create")),
{emit.InteropNameToID([]byte("Neo.Enumerator.Create")),
InteropFuncPrice{EnumeratorCreate, 1}},
{InteropNameToID([]byte("Neo.Enumerator.Next")),
{emit.InteropNameToID([]byte("Neo.Enumerator.Next")),
InteropFuncPrice{EnumeratorNext, 1}},
{InteropNameToID([]byte("Neo.Enumerator.Concat")),
{emit.InteropNameToID([]byte("Neo.Enumerator.Concat")),
InteropFuncPrice{EnumeratorConcat, 1}},
{InteropNameToID([]byte("Neo.Enumerator.Value")),
{emit.InteropNameToID([]byte("Neo.Enumerator.Value")),
InteropFuncPrice{EnumeratorValue, 1}},
{InteropNameToID([]byte("Neo.Iterator.Create")),
{emit.InteropNameToID([]byte("Neo.Iterator.Create")),
InteropFuncPrice{IteratorCreate, 1}},
{InteropNameToID([]byte("Neo.Iterator.Concat")),
{emit.InteropNameToID([]byte("Neo.Iterator.Concat")),
InteropFuncPrice{IteratorConcat, 1}},
{InteropNameToID([]byte("Neo.Iterator.Key")),
{emit.InteropNameToID([]byte("Neo.Iterator.Key")),
InteropFuncPrice{IteratorKey, 1}},
{InteropNameToID([]byte("Neo.Iterator.Keys")),
{emit.InteropNameToID([]byte("Neo.Iterator.Keys")),
InteropFuncPrice{IteratorKeys, 1}},
{InteropNameToID([]byte("Neo.Iterator.Values")),
{emit.InteropNameToID([]byte("Neo.Iterator.Values")),
InteropFuncPrice{IteratorValues, 1}},
}
@ -70,12 +70,6 @@ func getDefaultVMInterop(id uint32) *InteropFuncPrice {
return nil
}
// InteropNameToID returns an identificator of the method based on its name.
func InteropNameToID(name []byte) uint32 {
h := sha256.Sum256(name)
return binary.LittleEndian.Uint32(h[:4])
}
// runtimeLog handles the syscall "Neo.Runtime.Log" for printing and logging stuff.
func runtimeLog(vm *VM) error {
item := vm.Estack().Pop()

View file

@ -3,6 +3,7 @@ package vm
import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
@ -13,8 +14,6 @@ import (
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"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/opcode"
"github.com/stretchr/testify/require"
@ -33,8 +32,6 @@ type (
Name string
Script vmUTScript
Steps []vmUTStep
// FIXME remove when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
ScriptTable []map[string]vmUTScript
}
vmUTExecutionContextState struct {
@ -94,6 +91,7 @@ const (
)
func TestUT(t *testing.T) {
t.Skip()
testsRan := false
err := filepath.Walk(testsDir, func(path string, info os.FileInfo, err error) error {
if !strings.HasSuffix(path, ".json") {
@ -110,9 +108,7 @@ func TestUT(t *testing.T) {
}
func getTestingInterop(id uint32) *InteropFuncPrice {
// FIXME in NEO 3.0 it is []byte{0x77, 0x77, 0x77, 0x77} https://github.com/nspcc-dev/neo-go/issues/477
if id == InteropNameToID([]byte("Test.ExecutionEngine.GetScriptContainer")) ||
id == InteropNameToID([]byte("System.ExecutionEngine.GetScriptContainer")) {
if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) {
return &InteropFuncPrice{InteropFunc(func(v *VM) error {
v.estack.Push(&Element{value: (*InteropItem)(nil)})
return nil
@ -125,11 +121,6 @@ func testFile(t *testing.T, filename string) {
data, err := ioutil.ReadFile(filename)
require.NoError(t, err)
// FIXME remove when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
if len(data) > 2 && data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf {
data = data[3:]
}
ut := new(vmUT)
require.NoError(t, json.Unmarshal(data, ut))
@ -140,9 +131,6 @@ func testFile(t *testing.T, filename string) {
prog := []byte(test.Script)
vm := load(prog)
vm.state = breakState
// FIXME remove when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
vm.getScript = getScript(test.ScriptTable)
vm.RegisterInteropGetter(getTestingInterop)
for i := range test.Steps {
@ -174,17 +162,6 @@ func testFile(t *testing.T, filename string) {
})
}
func getScript(scripts []map[string]vmUTScript) func(util.Uint160) ([]byte, bool) {
store := make(map[util.Uint160][]byte)
for i := range scripts {
for _, v := range scripts[i] {
store[hash.Hash160(v)] = []byte(v)
}
}
return func(a util.Uint160) ([]byte, bool) { return store[a], true }
}
func compareItems(t *testing.T, a, b StackItem) {
switch si := a.(type) {
case *BigIntegerItem:

View file

@ -341,9 +341,7 @@ func (i *BoolItem) Bytes() []byte {
if i.value {
return []byte{1}
}
// return []byte{0}
// FIXME revert when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
return []byte{}
return []byte{0}
}
// TryBytes implements StackItem interface.

@ -1 +1 @@
Subproject commit 15170c46609ea61f6fcf6f36a90033bf658f2ab7
Subproject commit 45928090908bea777962c6df5dec1dd1bbafd7a7

View file

@ -479,11 +479,7 @@ func (v *VM) SetScriptGetter(gs func(util.Uint160) ([]byte, bool)) {
// GetInteropID converts instruction parameter to an interop ID.
func GetInteropID(parameter []byte) uint32 {
if len(parameter) == 4 {
return binary.LittleEndian.Uint32(parameter)
}
return InteropNameToID(parameter)
return binary.LittleEndian.Uint32(parameter)
}
// GetInteropByID returns interop function together with price.
@ -615,15 +611,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("negative index")
}
s := v.estack.Pop().Bytes()
if o > len(s) {
// panic("invalid offset")
// FIXME revert when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
v.estack.PushVal("")
break
}
last := l + o
if last > len(s) {
last = len(s)
panic("invalid offset")
}
v.estack.PushVal(s[o:last])

View file

@ -19,7 +19,7 @@ import (
)
func fooInteropGetter(id uint32) *InteropFuncPrice {
if id == InteropNameToID([]byte("foo")) {
if id == emit.InteropNameToID([]byte("foo")) {
return &InteropFuncPrice{func(evm *VM) error {
evm.Estack().PushVal(1)
return nil
@ -41,22 +41,6 @@ func TestInteropHook(t *testing.T) {
assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value())
}
func TestInteropHookViaID(t *testing.T) {
v := New()
v.RegisterInteropGetter(fooInteropGetter)
buf := io.NewBufBinWriter()
fooid := InteropNameToID([]byte("foo"))
var id = make([]byte, 4)
binary.LittleEndian.PutUint32(id, fooid)
emit.Syscall(buf.BinWriter, string(id))
emit.Opcode(buf.BinWriter, opcode.RET)
v.Load(buf.Bytes())
runVM(t, v)
assert.Equal(t, 1, v.estack.Len())
assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value())
}
func TestRegisterInteropGetter(t *testing.T) {
v := New()
currRegistered := len(v.getInterop)
@ -563,11 +547,9 @@ func TestIteratorValues(t *testing.T) {
}
func getSyscallProg(name string) (prog []byte) {
prog = []byte{byte(opcode.SYSCALL)}
prog = append(prog, byte(len(name)))
prog = append(prog, name...)
return
buf := io.NewBufBinWriter()
emit.Syscall(buf.BinWriter, name)
return buf.Bytes()
}
func getSerializeProg() (prog []byte) {
@ -704,15 +686,21 @@ func TestSerializeMap(t *testing.T) {
}
func TestSerializeMapCompat(t *testing.T) {
// Create a map, push key and value, add KV to map, serialize.
progHex := "c776036b65790576616c7565c468154e656f2e52756e74696d652e53657269616c697a65"
resHex := "820100036b6579000576616c7565"
prog, err := hex.DecodeString(progHex)
require.NoError(t, err)
res, err := hex.DecodeString(resHex)
require.NoError(t, err)
vm := load(prog)
// Create a map, push key and value, add KV to map, serialize.
buf := io.NewBufBinWriter()
emit.Opcode(buf.BinWriter, opcode.NEWMAP)
emit.Opcode(buf.BinWriter, opcode.DUP)
emit.Bytes(buf.BinWriter, []byte("key"))
emit.Bytes(buf.BinWriter, []byte("value"))
emit.Opcode(buf.BinWriter, opcode.SETITEM)
emit.Syscall(buf.BinWriter, "Neo.Runtime.Serialize")
require.NoError(t, buf.Err)
vm := load(buf.Bytes())
runVM(t, vm)
assert.Equal(t, res, vm.estack.Pop().Bytes())
}
@ -1505,9 +1493,7 @@ func TestSIZEBool(t *testing.T) {
vm.estack.PushVal(false)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
// assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
// FIXME revert when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
assert.Equal(t, makeStackItem(0), vm.estack.Pop().value)
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
}
func TestARRAYSIZEArray(t *testing.T) {
@ -2215,7 +2201,7 @@ func TestCATInt0ByteArray(t *testing.T) {
vm.estack.PushVal([]byte{})
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, &ByteArrayItem{[]byte{0}}, vm.estack.Pop().value)
assert.Equal(t, &ByteArrayItem{[]byte{}}, vm.estack.Pop().value)
}
func TestCATByteArrayInt1(t *testing.T) {
@ -2267,11 +2253,7 @@ func TestSUBSTRBadOffset(t *testing.T) {
vm.estack.PushVal(7)
vm.estack.PushVal(1)
// checkVMFailed(t, vm)
// FIXME revert when NEO 3.0 https://github.com/nspcc-dev/neo-go/issues/477
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{}, vm.estack.Peek(0).Bytes())
checkVMFailed(t, vm)
}
func TestSUBSTRBigLen(t *testing.T) {
@ -2280,9 +2262,7 @@ func TestSUBSTRBigLen(t *testing.T) {
vm.estack.PushVal([]byte("abcdef"))
vm.estack.PushVal(1)
vm.estack.PushVal(6)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes())
checkVMFailed(t, vm)
}
func TestSUBSTRBad387(t *testing.T) {
@ -2293,9 +2273,7 @@ func TestSUBSTRBad387(t *testing.T) {
vm.estack.PushVal(b)
vm.estack.PushVal(1)
vm.estack.PushVal(6)
runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes())
checkVMFailed(t, vm)
}
func TestSUBSTRBadNegativeOffset(t *testing.T) {