diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index ddd07a48e..fc4b49893 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -662,7 +662,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { // We can be sure builtins are of type *ast.Ident. c.convertBuiltin(n) case isSyscall(f): - c.convertSyscall(f.selector.Name, f.name) + c.convertSyscall(n, f.selector.Name, f.name) default: emit.Call(c.prog.BinWriter, opcode.CALL, f.label) } @@ -978,12 +978,18 @@ func (c *codegen) getByteArray(expr ast.Expr) []byte { } } -func (c *codegen) convertSyscall(api, name string) { +func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) { api, ok := syscalls[api][name] if !ok { c.prog.Err = fmt.Errorf("unknown VM syscall api: %s", name) return } + switch name { + case "Notify": + numArgs := len(expr.Args) + emit.Int(c.prog.BinWriter, int64(numArgs)) + emit.Opcode(c.prog.BinWriter, opcode.PACK) + } emit.Syscall(c.prog.BinWriter, api) // This NOP instruction is basically not needed, but if we do, we have a diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index e60544fb5..1eaa53752 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -2,6 +2,10 @@ package compiler_test import ( "testing" + + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestStoragePutGet(t *testing.T) { @@ -20,3 +24,24 @@ func TestStoragePutGet(t *testing.T) { ` eval(t, src, []byte("foo")) } + +func TestNotify(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + func Main(arg int) { + runtime.Notify(arg, "sum", arg+1) + runtime.Notify() + runtime.Notify("single") + }` + + v, s := vmAndCompileInterop(t, src) + v.Estack().PushVal(11) + + require.NoError(t, v.Run()) + require.Equal(t, 3, len(s.events)) + + exp0 := []vm.StackItem{vm.NewBigIntegerItem(11), vm.NewByteArrayItem([]byte("sum")), vm.NewBigIntegerItem(12)} + assert.Equal(t, exp0, s.events[0].Value()) + assert.Equal(t, []vm.StackItem{}, s.events[1].Value()) + assert.Equal(t, []vm.StackItem{vm.NewByteArrayItem([]byte("single"))}, s.events[2].Value()) +} diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index 6d9d81e26..b97d14acd 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -53,6 +53,11 @@ func assertResult(t *testing.T, vm *vm.VM, result interface{}) { } func vmAndCompile(t *testing.T, src string) *vm.VM { + v, _ := vmAndCompileInterop(t, src) + return v +} + +func vmAndCompileInterop(t *testing.T, src string) (*vm.VM, *storagePlugin) { vm := vm.New() storePlugin := newStoragePlugin() @@ -61,12 +66,13 @@ func vmAndCompile(t *testing.T, src string) *vm.VM { b, err := compiler.Compile(strings.NewReader(src)) require.NoError(t, err) vm.Load(b) - return vm + return vm, storePlugin } type storagePlugin struct { mem map[string][]byte interops map[uint32]vm.InteropFunc + events []vm.StackItem } func newStoragePlugin() *storagePlugin { @@ -77,6 +83,7 @@ func newStoragePlugin() *storagePlugin { 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 return s } @@ -89,6 +96,11 @@ func (s *storagePlugin) getInterop(id uint32) *vm.InteropFuncPrice { return nil } +func (s *storagePlugin) Notify(v *vm.VM) error { + s.events = append(s.events, v.Estack().Pop().Item()) + return nil +} + func (s *storagePlugin) Delete(vm *vm.VM) error { vm.Estack().Pop() key := vm.Estack().Pop().Bytes() diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 82565cef6..f1e8e1657 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -229,6 +229,12 @@ func TestCreateBasicChain(t *testing.T) { require.NoError(t, bc.AddBlock(b)) t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE()) + // info for getblockheader rpc tests + t.Logf("header hash: %s", b.Hash().StringLE()) + buf := io.NewBufBinWriter() + b.Header().EncodeBinary(buf.BinWriter) + t.Logf("header: %s", hex.EncodeToString(buf.Bytes())) + // Generate some blocks to be able to claim GAS for them. _, err = bc.genBlocks(numOfEmptyBlocks) require.NoError(t, err) diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 62448388a..1ffd72ed9 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -44,20 +44,20 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "d864728bdbc88da799bc43862ae6aaa62adc3a87" +const testContractHash = "c2789e5ab9bab828743833965b1df0d5fbcc206f" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { { name: "positive", - params: `["440b84d1580e36e84379416b58d9a3ad978cc557e54fd7ec6a2648329975b333"]`, + params: `["93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7"]`, 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("440b84d1580e36e84379416b58d9a3ad978cc557e54fd7ec6a2648329975b333") + expectedTxHash, err := util.Uint256DecodeStringLE("93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7") require.NoError(t, err) assert.Equal(t, expectedTxHash, res.TxHash) assert.Equal(t, 1, len(res.Executions)) @@ -406,25 +406,25 @@ var rpcTestCases = map[string][]rpcTestCase{ "getblockheader": { { name: "positive, no verbose", - params: `["77259b951c7eef05fdd91f155c90c698894968c2eafc36c9ef62d6538597ba2d"]`, + params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f"]`, result: func(e *executor) interface{} { - expected := "00000000c0c89580103dfc9e65777591b57aa64af3a499c62c9287329855a2398652ae60edb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf90815615e050000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140dd8367b66a733fd9ab90dfdf3a64f7393d68b69e69ad7f2c89fe9bb1633de2f53a329b56a68fc47284e172e3196b63da510d5d719e9484a5da2e0732d4e85d89406d701e53f89a2037c5247663d1aa81d19899b8212c0b1052c399df11cb2909c62e1367edeb24c990df566f16218de252b87fd664fb07a157138a89fd92df45854013d5a17988a32ef9a766fb3277a7d2466c6e1608e28a3b3102a95450003a70ac63fe406742d8eed96301c51f8eb8f60b0fe77805b1ae2d508ca73897ac7a162b40640c35a3e40415a4e604ebacc3ffd96dda2e6f55589a7e87cf04280cf412c9eff49e0858d001bad2473e0de8fd9ba53c0d1718aafc6b8c3fa4d13451875e01388b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00" + expected := "00000000999086db552ba8f84734bddca55b25a8d3d8c5f866f941209169c38d35376e995f2f29c4685140d85073fec705089706553eae4de3c95d9d8d425af36e597ee651cb8a5e010000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd04014057de8968705f020995662b60c15133846425ea2f786757f2a0fd8845f0d33f6ec35b2ef77a882e4d7560d7667dbf9a6c4b74a51d9e4c52ddce26dd6731047bb340720cd95db06a799c3d121a3b75347c002b0fdc09b45bc2dd5f7fd79c6f674ca9a97cf9c7aff2c8a6ec9f0eefab29a2ae1a758b122f83f4dc34b4d6fa1266b5ae407987727d9a5345d45966e0a6b8e372efc4ce3695c73a2d2f94ba00eee1ce0a75d86ffa60bcfc673c8abc971bf2576ed9c82d5371a235d0168a2fed1ef722f06740c2385bbb75ca72665a2d4f7a9b6ef7f529cd90d55b08bfbaccf4edeee86343e915bb25c5deca6ce2fd9114c44a8963bdfc430d987923caa8ed5f6fb20f81fabe8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00" return &expected }, }, { name: "positive, verbose 0", - params: `["77259b951c7eef05fdd91f155c90c698894968c2eafc36c9ef62d6538597ba2d", 0]`, + params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", 0]`, result: func(e *executor) interface{} { - expected := "00000000c0c89580103dfc9e65777591b57aa64af3a499c62c9287329855a2398652ae60edb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf90815615e050000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140dd8367b66a733fd9ab90dfdf3a64f7393d68b69e69ad7f2c89fe9bb1633de2f53a329b56a68fc47284e172e3196b63da510d5d719e9484a5da2e0732d4e85d89406d701e53f89a2037c5247663d1aa81d19899b8212c0b1052c399df11cb2909c62e1367edeb24c990df566f16218de252b87fd664fb07a157138a89fd92df45854013d5a17988a32ef9a766fb3277a7d2466c6e1608e28a3b3102a95450003a70ac63fe406742d8eed96301c51f8eb8f60b0fe77805b1ae2d508ca73897ac7a162b40640c35a3e40415a4e604ebacc3ffd96dda2e6f55589a7e87cf04280cf412c9eff49e0858d001bad2473e0de8fd9ba53c0d1718aafc6b8c3fa4d13451875e01388b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00" + expected := "00000000999086db552ba8f84734bddca55b25a8d3d8c5f866f941209169c38d35376e995f2f29c4685140d85073fec705089706553eae4de3c95d9d8d425af36e597ee651cb8a5e010000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd04014057de8968705f020995662b60c15133846425ea2f786757f2a0fd8845f0d33f6ec35b2ef77a882e4d7560d7667dbf9a6c4b74a51d9e4c52ddce26dd6731047bb340720cd95db06a799c3d121a3b75347c002b0fdc09b45bc2dd5f7fd79c6f674ca9a97cf9c7aff2c8a6ec9f0eefab29a2ae1a758b122f83f4dc34b4d6fa1266b5ae407987727d9a5345d45966e0a6b8e372efc4ce3695c73a2d2f94ba00eee1ce0a75d86ffa60bcfc673c8abc971bf2576ed9c82d5371a235d0168a2fed1ef722f06740c2385bbb75ca72665a2d4f7a9b6ef7f529cd90d55b08bfbaccf4edeee86343e915bb25c5deca6ce2fd9114c44a8963bdfc430d987923caa8ed5f6fb20f81fabe8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00" return &expected }, }, { name: "positive, verbose !=0", - params: `["77259b951c7eef05fdd91f155c90c698894968c2eafc36c9ef62d6538597ba2d", 2]`, + params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", 2]`, result: func(e *executor) interface{} { - hash, err := util.Uint256DecodeStringLE("77259b951c7eef05fdd91f155c90c698894968c2eafc36c9ef62d6538597ba2d") + hash, err := util.Uint256DecodeStringLE("614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f") if err != nil { panic("can not decode hash parameter") } @@ -456,7 +456,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid verbose type", - params: `["77259b951c7eef05fdd91f155c90c698894968c2eafc36c9ef62d6538597ba2d", true]`, + params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", true]`, fail: true, }, { @@ -822,7 +822,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "submitblock": { { name: "empty block", - params: `["00000000270dd14a8ddb4961cada75e9ec4cce906f9feeaef21b1d6cea5b2f0230baf92e00000000000000000000000000000000000000000000000000000000000000009f0e625ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140bd2b961ca7df75d6d8896fd4ef487128ddd1ca9feda9cb09da58a6a24607fc71cd572ba072e4ec9d9847c7da6a33fc44dc9a64208fd62851d27d330012bd0bf640ee6e92653789b0d1c87e0cd485c5bbd84194479995930567df9ffec7ab370c965df320134632e53d139a785840cecb16c4fe801f3c6bed17a9dd83460f5f297440fbe5f9d3cc23f9c7b9f10e191eae541fcb71eb03799c09886aa04c0bc43e98c2c91686a5b4fb988c8855f4d217807f01ca9a61bc0d5536eb3149dce7ae66965c407af69085454fb39f0c27cf308219c09efbe902f0a092a065b38864c97529f1dd12fa54c346e8d9482106c7154b3170c150887170c78fe78a5c70e3956880489c8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"]`, + params: `["00000000399183d238a2a5a11ae4f2263fa5372a2fc488ad1bb0782b83e66d7fc89637d9000000000000000000000000000000000000000000000000000000000000000021cc8a5ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd04014090fb6263dc6a3009947999d1320844fb08929748ef3c0a6647194a637dea2c4454bfc97cafb1ce46f7df25529ff5f195f62fc455d929b4e89d5a974ad0f6bfdd40b9d36fceb1e3cadbcc88d2d0b6f481c6c3af45fa20b91682d7aed6493bdeed7ee602aeb7f50ea09b6ee5332f9f95f180fa6b3033be4a6c1208e40d75fe73c8804005dcc45a2a94c036597381e6fd3c4f76977f61fdc25f7e99d60577a970a6eeb543b6133b9b6387ec60babe25fb8dd4bfe9874e06c864f21059664c9b4a0f214c40fde0dfd49c32920d2a17bad0acd68b25180aeb137f82fdbd5794ece3d42bf699539928a30413fc9fd367b34465189a740ff41f0861318847fbc77cbe005bb6918b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"]`, 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 - params: `["00000000270dd14a8ddb4961cada75e9ec4cce906f9feeaef21b1d6cea5b2f0230baf92eedb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf9b93a615ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401402dca64bfc20107819171fab7c636427e9634a58f560ff9ef94ff9005a82509dd5e1b054d195201013a8477529d023d794a3ca6efa717f5d7e541e3b81fbabff840b5395429f0da5a8d1023907dca3771c02eca31e520e1acc2851b373a6aedd5bf6ba75a80b0949ee0794156ae48356b98e761143b90ebaa816ca4b0b0f91b38ab40a6feb77e09c0c95c6f2c6571155843f388e1f7cb7ba5af2bc235bac1d57a8c316d817e54895bfdb98b9c34ed9550e7ccff2a06b46ca0f2b99ba720368d9934eb40dbbdc94154cc304296ccda73e4e0257cdc344c4bad4c70f419f59f5d6d862819cead91c55e73e7c30daff2cd38d74eae3e0e49a5226de3060843d0e3b9262dc08b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae0100000000000000000000"]`, + params: `["00000000399183d238a2a5a11ae4f2263fa5372a2fc488ad1bb0782b83e66d7fc89637d9edb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf921cc8a5ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401401b2c9a188c2bf0b14c59dca4c2fccc14664d815204573824d2bc7899aed43e4023d321ce28551875e7459de494d368ffe0d8b04502694640dfe0db795a52b3c340c06924f3f0de04045ab09cb51a7944219fe9f69fbf9c9770fed7712930b1a0e58dd13e78c76afff1c7d7316cf5ff55981917f8c243a33858163557a3f7d0270f4057675127a0355f24ffa2c28b742def8d4c39b4ef79b098028da182a48385608472d3fbed598b806f60b834196222b4d1bc2a65cf465de7fcedba4103dd510ae54036f06134debb8bbecfef297fb98070e242d5eefd607622110adc645d90d40779065819871c739598f04b9ee7311ebaaa048ac403a19542c5b0d2ccf1ba5e16968b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae0100000000000000000000"]`, result: func(e *executor) interface{} { v := true return &v diff --git a/pkg/rpc/server/testdata/test_contract.avm b/pkg/rpc/server/testdata/test_contract.avm index 7d3417c3c..b9f78fdc6 100755 Binary files a/pkg/rpc/server/testdata/test_contract.avm and b/pkg/rpc/server/testdata/test_contract.avm differ diff --git a/pkg/rpc/server/testdata/test_contract.go b/pkg/rpc/server/testdata/test_contract.go index a8da7cf8a..9eb87fe4e 100644 --- a/pkg/rpc/server/testdata/test_contract.go +++ b/pkg/rpc/server/testdata/test_contract.go @@ -12,7 +12,7 @@ const ( ) func Main(operation string, args []interface{}) interface{} { - runtime.Notify([]interface{}{"contract call", operation, args}) + runtime.Notify("contract call", operation, args) switch operation { case "Put": ctx := storage.GetContext() @@ -34,7 +34,7 @@ func Main(operation string, args []interface{}) interface{} { return false } amount := storage.Get(ctx, addr).(int) - runtime.Notify([]interface{}{"balanceOf", addr, amount}) + runtime.Notify("balanceOf", addr, amount) return amount case "transfer": ctx := storage.GetContext() @@ -66,7 +66,7 @@ func Main(operation string, args []interface{}) interface{} { toBalance += amount storage.Put(ctx, to, toBalance) - runtime.Notify([]interface{}{"transfer", from, to, amount}) + runtime.Notify("transfer", from, to, amount) return true case "init": @@ -74,7 +74,7 @@ func Main(operation string, args []interface{}) interface{} { h := engine.GetExecutingScriptHash() amount := totalSupply storage.Put(ctx, h, amount) - runtime.Notify([]interface{}{"transfer", []byte{}, h, amount}) + runtime.Notify("transfer", []byte{}, h, amount) return true default: panic("invalid operation") diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index af3c153a0..c513e075a 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ