diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 74904dcb1..07a617560 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" @@ -519,3 +520,31 @@ func TestCallTConversionErrors(t *testing.T) { require.Error(t, err) }) } + +func TestCallWithVersion(t *testing.T) { + bc, acc := chain.NewSingle(t) + e := neotest.NewExecutor(t, bc, acc, acc) + src := `package foo + import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + util "github.com/nspcc-dev/neo-go/pkg/interop/lib/contract" + ) + func CallWithVersion(hash interop.Hash160, version int, method string) interface{} { + return util.CallWithVersion(hash, version, method, contract.All) + }` + ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"}) + e.DeployContract(t, ctr, nil) + c := e.CommitteeInvoker(ctr.Hash) + + policyH := state.CreateNativeContractHash(nativenames.Policy) + t.Run("good", func(t *testing.T) { + c.Invoke(t, e.Chain.GetBaseExecFee(), "callWithVersion", policyH.BytesBE(), 0, "getExecFeeFactor") + }) + t.Run("unknown contract", func(t *testing.T) { + c.InvokeFail(t, "unknown contract", "callWithVersion", util.Uint160{1, 2, 3}.BytesBE(), 0, "getExecFeeFactor") + }) + t.Run("invalid version", func(t *testing.T) { + c.InvokeFail(t, "contract version mismatch", "callWithVersion", policyH.BytesBE(), 1, "getExecFeeFactor") + }) +} diff --git a/pkg/interop/lib/contract/contract.go b/pkg/interop/lib/contract/contract.go new file mode 100644 index 000000000..735db00ba --- /dev/null +++ b/pkg/interop/lib/contract/contract.go @@ -0,0 +1,23 @@ +package contract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/native/management" +) + +// CallWithVersion is a utility function that executes the previously deployed +// blockchain contract with the specified version (update counter) and hash +// (20 bytes in BE form) using the provided arguments and call flags. It fails +// if the contract has version mismatch. It returns whatever this contract +// returns. This function uses `System.Contract.Call` syscall. +func CallWithVersion(scriptHash interop.Hash160, version int, method string, f contract.CallFlag, args ...interface{}) interface{} { + cs := management.GetContract(scriptHash) + if cs == nil { + panic("unknown contract") + } + if cs.UpdateCounter != version { + panic("contract version mismatch") + } + return contract.Call(scriptHash, method, f, args...) +}