diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index ccc2e12ed..f0af87b0a 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -15,6 +15,7 @@ const ( SystemIteratorValue = "System.Iterator.Value" SystemRuntimeBurnGas = "System.Runtime.BurnGas" SystemRuntimeCheckWitness = "System.Runtime.CheckWitness" + SystemRuntimeCurrentSigners = "System.Runtime.CurrentSigners" SystemRuntimeGasLeft = "System.Runtime.GasLeft" SystemRuntimeGetAddressVersion = "System.Runtime.GetAddressVersion" SystemRuntimeGetCallingScriptHash = "System.Runtime.GetCallingScriptHash" @@ -52,6 +53,7 @@ var names = []string{ SystemIteratorValue, SystemRuntimeBurnGas, SystemRuntimeCheckWitness, + SystemRuntimeCurrentSigners, SystemRuntimeGasLeft, SystemRuntimeGetAddressVersion, SystemRuntimeGetCallingScriptHash, diff --git a/pkg/core/interop/runtime/engine.go b/pkg/core/interop/runtime/engine.go index 05ee4a86d..b7a985a33 100644 --- a/pkg/core/interop/runtime/engine.go +++ b/pkg/core/interop/runtime/engine.go @@ -7,6 +7,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -179,3 +180,16 @@ func BurnGas(ic *interop.Context) error { } return nil } + +// CurrentSigners returns signers of the currently loaded transaction or stackitem.Null +// if script container is not a transaction. +func CurrentSigners(ic *interop.Context) error { + tx, ok := ic.Container.(*transaction.Transaction) + if ok { + ic.VM.Estack().PushItem(transaction.SignersToStackItem(tx.Signers)) + } else { + ic.VM.Estack().PushItem(stackitem.Null{}) + } + + return nil +} diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go index 646926d8a..f5a5fe79c 100644 --- a/pkg/core/interop/runtime/engine_test.go +++ b/pkg/core/interop/runtime/engine_test.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" @@ -129,3 +130,45 @@ func TestLog(t *testing.T) { require.Equal(t, h.StringLE(), logMsg["script"]) }) } + +func TestCurrentSigners(t *testing.T) { + t.Run("container is block", func(t *testing.T) { + b := block.New(false) + ic := &interop.Context{VM: vm.New(), Container: b} + require.NoError(t, CurrentSigners(ic)) + checkStack(t, ic.VM, stackitem.Null{}) + }) + + t.Run("container is transaction", func(t *testing.T) { + tx := &transaction.Transaction{ + Signers: []transaction.Signer{ + { + Account: util.Uint160{1}, + Scopes: transaction.None, + }, + { + Account: util.Uint160{2}, + Scopes: transaction.CalledByEntry, + }, + }, + } + ic := &interop.Context{VM: vm.New(), Container: tx} + require.NoError(t, CurrentSigners(ic)) + checkStack(t, ic.VM, stackitem.NewArray([]stackitem.Item{ + stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(util.Uint160{1}.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(transaction.None))), + stackitem.NewArray([]stackitem.Item{}), + stackitem.NewArray([]stackitem.Item{}), + stackitem.NewArray([]stackitem.Item{}), + }), + stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(util.Uint160{2}.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(transaction.CalledByEntry))), + stackitem.NewArray([]stackitem.Item{}), + stackitem.NewArray([]stackitem.Item{}), + stackitem.NewArray([]stackitem.Item{}), + }), + })) + }) +} diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 88de802ba..003f012cd 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -46,6 +46,8 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemRuntimeBurnGas, Func: runtime.BurnGas, Price: 1 << 4, ParamCount: 1}, {Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 1 << 10, RequiredFlags: callflag.NoneFlag, ParamCount: 1}, + {Name: interopnames.SystemRuntimeCurrentSigners, Func: runtime.CurrentSigners, Price: 1 << 4, + RequiredFlags: callflag.NoneFlag}, {Name: interopnames.SystemRuntimeGasLeft, Func: runtime.GasLeft, Price: 1 << 4}, {Name: interopnames.SystemRuntimeGetAddressVersion, Func: runtime.GetAddressVersion, Price: 1 << 3}, {Name: interopnames.SystemRuntimeGetCallingScriptHash, Func: runtime.GetCallingScriptHash, Price: 1 << 4}, diff --git a/pkg/interop/runtime/runtime.go b/pkg/interop/runtime/runtime.go index a23039473..7c041e6ea 100644 --- a/pkg/interop/runtime/runtime.go +++ b/pkg/interop/runtime/runtime.go @@ -7,6 +7,7 @@ package runtime 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/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) @@ -30,6 +31,13 @@ func CheckWitness(hashOrKey []byte) bool { return neogointernal.Syscall1("System.Runtime.CheckWitness", hashOrKey).(bool) } +// CurrentSigners returns signers of the currently loaded transaction or nil if +// executing script container is not a transaction. It uses +// `System.Runtime.CurrentSigners` syscall. +func CurrentSigners() []ledger.TransactionSigner { + return neogointernal.Syscall0("System.Runtime.CurrentSigners").([]ledger.TransactionSigner) +} + // LoadScript loads the given bytecode into the VM and executes it with the // given call flags and arguments. This bytecode is executed as is from byte 0, // it's not a deployed contract that can have methods. The execution context is