From f9c1fb952c6a9260443f08ce351c180e1b44d7b3 Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Fri, 4 Aug 2023 12:33:10 +0300 Subject: [PATCH 1/5] [#2] Add a custom vm.Run() function It executes loaded program and accumulates all seen opcodes together with the scripthash they belong to. Signed-off-by: Ekaterina Lebedeva --- covertest/run.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 covertest/run.go diff --git a/covertest/run.go b/covertest/run.go new file mode 100644 index 0000000..9ffaa5e --- /dev/null +++ b/covertest/run.go @@ -0,0 +1,58 @@ +package covertest + +import ( + "errors" + + "github.com/nspcc-dev/neo-go/pkg/util" + "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/vmstate" +) + +// InstrHash maps Instruction with its Script Hash. +type InstrHash struct { + Num int + Instruction opcode.Opcode + ScriptHash util.Uint160 +} + +// Run starts execution of the loaded program and accumulates all seen opcodes +// together with the scripthash they belong to. +func Run(v *vm.VM) ([]InstrHash, error) { + + if !v.Ready() { + return nil, errors.New("no program loaded") + } + + if v.HasFailed() { + // VM already ran something and failed, in general its state is + // undefined in this case so we can't run anything. + return nil, errors.New("VM has failed") + } + + // vmstate.Halt (the default) or vmstate.Break are safe to continue. + var ops []InstrHash + for { + switch { + case v.HasFailed(): + // Should be caught and reported already by the v.Step(), + // but we're checking here anyway just in case. + return ops, errors.New("VM has failed") + case v.HasHalted(), v.AtBreakpoint(): + // Normal exit from this loop. + return ops, nil + case v.State() == vmstate.None: + nStr, curInstr := v.Context().NextInstr() + ops = append(ops, InstrHash{ + Num: nStr, + Instruction: curInstr, + ScriptHash: v.Context().ScriptHash(), + }) + if err := v.Step(); err != nil { + return ops, err + } + default: + return ops, errors.New("unknown state") + } + } +} -- 2.45.2 From e4dff3d6a1776ab04046956e6decb1adc9c125df Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Fri, 4 Aug 2023 12:41:06 +0300 Subject: [PATCH 2/5] [#2] Add a test for covertest.Run() Signed-off-by: Ekaterina Lebedeva --- tests/contract_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/contract_test.go b/tests/contract_test.go index deac7af..bb91372 100644 --- a/tests/contract_test.go +++ b/tests/contract_test.go @@ -5,9 +5,14 @@ import ( "testing" "git.frostfs.info/TrueCloudLab/contract-coverage-primer/covertest" + "github.com/davecgh/go-spew/spew" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/neotest/chain" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "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/stackitem" + "github.com/stretchr/testify/require" ) const ctrPath = "../impulse" @@ -41,3 +46,36 @@ func TestContract(t *testing.T) { inv.InvokeFail(t, "Invalid key size", "putNumber", invalidKey, 42) inv.InvokeFail(t, "Invalid key size", "getNumber", invalidKey) } + +func TestRun(t *testing.T) { + e := newExecutor(t) + ctrDI := covertest.CompileFile(t, e.CommitteeHash, ctrPath, path.Join(ctrPath, "config.yml")) + e.DeployContract(t, ctrDI.Contract, nil) + + // setting up a VM for covertest.Run() + covertestRunVM := setUpVMForPut(t, e, ctrDI.Contract, false, 101, 2, invalidKey) + res, err := covertest.Run(covertestRunVM) + spew.Println("Printing collected instructions:") + spew.Dump(res) + spew.Println("covertest.Run() returned an error: ", err) + + // setting up a VM for vm.Run() + origRunVM := setUpVMForPut(t, e, ctrDI.Contract, false, 101, 2, invalidKey) + runerr := origRunVM.Run() + spew.Println("vm.Run() returned an error: ", err) + + //check if errors are the same + spew.Println("Are errors the same? ", runerr.Error() == runerr.Error()) + + //check if the number of elements on the stack is the same + spew.Println("Is the number of elements on the stack the same? ", origRunVM.Estack().Len() == covertestRunVM.Estack().Len()) +} + +func setUpVMForPut(t *testing.T, e *neotest.Executor, contract *neotest.Contract, hasResult bool, methodOff int, num int, key []byte) (v *vm.VM) { + ic, err := e.Chain.GetTestVM(trigger.Application, nil, nil) + require.NoError(t, err) + ic.VM.LoadNEFMethod(contract.NEF, contract.Hash, contract.Hash, callflag.All, hasResult, methodOff, -1, nil) + ic.VM.Context().Estack().PushVal(num) + ic.VM.Context().Estack().PushVal(key) + return ic.VM +} -- 2.45.2 From 6bdec59abf624c0f14632c6d713bc3abe370ec91 Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Fri, 4 Aug 2023 16:39:34 +0300 Subject: [PATCH 3/5] [#2] Fix bad field naming in InstrHash structure Signed-off-by: Ekaterina Lebedeva --- covertest/run.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/covertest/run.go b/covertest/run.go index 9ffaa5e..173f13d 100644 --- a/covertest/run.go +++ b/covertest/run.go @@ -11,7 +11,7 @@ import ( // InstrHash maps Instruction with its Script Hash. type InstrHash struct { - Num int + Offset int Instruction opcode.Opcode ScriptHash util.Uint160 } @@ -44,7 +44,7 @@ func Run(v *vm.VM) ([]InstrHash, error) { case v.State() == vmstate.None: nStr, curInstr := v.Context().NextInstr() ops = append(ops, InstrHash{ - Num: nStr, + Offset: nStr, Instruction: curInstr, ScriptHash: v.Context().ScriptHash(), }) -- 2.45.2 From 9e3ac59aa8a09db9967329d5c8c45560403c8c80 Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Mon, 7 Aug 2023 11:10:58 +0300 Subject: [PATCH 4/5] [#2] Fix test Use `require` to check for errors and instead of just printing result to Stdout, t.Log to print errors. Added functions to calculate start offset, check if contract method has result and to get random int to put. Signed-off-by: Ekaterina Lebedeva --- tests/contract_test.go | 52 +++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/tests/contract_test.go b/tests/contract_test.go index bb91372..caa41d8 100644 --- a/tests/contract_test.go +++ b/tests/contract_test.go @@ -1,11 +1,14 @@ package contract import ( + "errors" + "math/rand" "path" "testing" "git.frostfs.info/TrueCloudLab/contract-coverage-primer/covertest" "github.com/davecgh/go-spew/spew" + "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/neotest/chain" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" @@ -52,23 +55,31 @@ func TestRun(t *testing.T) { ctrDI := covertest.CompileFile(t, e.CommitteeHash, ctrPath, path.Join(ctrPath, "config.yml")) e.DeployContract(t, ctrDI.Contract, nil) + startOffsetPutNumber, err := getStartOffset(ctrDI.DebugInfo, "PutNumber") + require.NoError(t, err) + + hasResult, err := hasResult(ctrDI.DebugInfo, "PutNumber") + require.NoError(t, err) + + someNum := getNumToPut() + // setting up a VM for covertest.Run() - covertestRunVM := setUpVMForPut(t, e, ctrDI.Contract, false, 101, 2, invalidKey) - res, err := covertest.Run(covertestRunVM) - spew.Println("Printing collected instructions:") + covertestRunVM := setUpVMForPut(t, e, ctrDI.Contract, hasResult, startOffsetPutNumber, someNum, invalidKey) + res, covErr := covertest.Run(covertestRunVM) + t.Log("Printing collected instructions:") spew.Dump(res) - spew.Println("covertest.Run() returned an error: ", err) + t.Log("covertest.Run() returned an error: ", covErr) // setting up a VM for vm.Run() - origRunVM := setUpVMForPut(t, e, ctrDI.Contract, false, 101, 2, invalidKey) + origRunVM := setUpVMForPut(t, e, ctrDI.Contract, hasResult, startOffsetPutNumber, someNum, invalidKey) runerr := origRunVM.Run() - spew.Println("vm.Run() returned an error: ", err) + t.Log("vm.Run() returned an error: ", covErr) //check if errors are the same - spew.Println("Are errors the same? ", runerr.Error() == runerr.Error()) + require.Equal(t, runerr.Error(), covErr.Error()) //check if the number of elements on the stack is the same - spew.Println("Is the number of elements on the stack the same? ", origRunVM.Estack().Len() == covertestRunVM.Estack().Len()) + require.Equal(t, origRunVM.Estack().Len(), covertestRunVM.Estack().Len()) } func setUpVMForPut(t *testing.T, e *neotest.Executor, contract *neotest.Contract, hasResult bool, methodOff int, num int, key []byte) (v *vm.VM) { @@ -79,3 +90,28 @@ func setUpVMForPut(t *testing.T, e *neotest.Executor, contract *neotest.Contract ic.VM.Context().Estack().PushVal(key) return ic.VM } + +func getStartOffset(di *compiler.DebugInfo, methodID string) (int, error) { + for _, method := range di.Methods { + if method.ID == methodID { + return int(method.Range.Start), nil + } + } + return 0, errors.New("Method not found") +} + +func hasResult(di *compiler.DebugInfo, methodID string) (bool, error) { + for _, method := range di.Methods { + if method.ID == methodID { + if method.ReturnType == "Void" { + return false, nil + } + return true, nil + } + } + return false, errors.New("Method not found") +} + +func getNumToPut() int { + return rand.Intn(100) +} -- 2.45.2 From 401d54dda4ebf92a910efe95b7970b8bd4366da3 Mon Sep 17 00:00:00 2001 From: Ekaterina Lebedeva Date: Mon, 7 Aug 2023 11:56:12 +0300 Subject: [PATCH 5/5] [#2] Fix comments Signed-off-by: Ekaterina Lebedeva --- covertest/run.go | 6 +++--- tests/contract_test.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/covertest/run.go b/covertest/run.go index 173f13d..98dae12 100644 --- a/covertest/run.go +++ b/covertest/run.go @@ -9,7 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" ) -// InstrHash maps Instruction with its Script Hash. +// InstrHash maps instruction with scripthash of a contract it belongs to. type InstrHash struct { Offset int Instruction opcode.Opcode @@ -17,9 +17,9 @@ type InstrHash struct { } // Run starts execution of the loaded program and accumulates all seen opcodes -// together with the scripthash they belong to. +// together with the scripthash of a contract they belong to. +// Original vm.Run(): https://github.com/nspcc-dev/neo-go/blob/v0.101.3/pkg/vm/vm.go#L418 func Run(v *vm.VM) ([]InstrHash, error) { - if !v.Ready() { return nil, errors.New("no program loaded") } diff --git a/tests/contract_test.go b/tests/contract_test.go index caa41d8..6f9dc8d 100644 --- a/tests/contract_test.go +++ b/tests/contract_test.go @@ -20,7 +20,7 @@ import ( const ctrPath = "../impulse" -// Key for tests +// keys for tests var ( validKey = []byte{1, 2, 3, 4, 5} invalidKey = []byte{1, 2, 3} @@ -63,22 +63,22 @@ func TestRun(t *testing.T) { someNum := getNumToPut() - // setting up a VM for covertest.Run() + // set up a VM for covertest.Run() covertestRunVM := setUpVMForPut(t, e, ctrDI.Contract, hasResult, startOffsetPutNumber, someNum, invalidKey) res, covErr := covertest.Run(covertestRunVM) t.Log("Printing collected instructions:") spew.Dump(res) t.Log("covertest.Run() returned an error: ", covErr) - // setting up a VM for vm.Run() + // set up a VM for vm.Run() origRunVM := setUpVMForPut(t, e, ctrDI.Contract, hasResult, startOffsetPutNumber, someNum, invalidKey) runerr := origRunVM.Run() t.Log("vm.Run() returned an error: ", covErr) - //check if errors are the same + // check if errors are the same require.Equal(t, runerr.Error(), covErr.Error()) - //check if the number of elements on the stack is the same + // check if the number of elements on the stack is the same require.Equal(t, origRunVM.Estack().Len(), covertestRunVM.Estack().Len()) } -- 2.45.2