github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/interop/runtime/ext_test.go (about)

     1  package runtime_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"math"
     6  	"math/big"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/nspcc-dev/neo-go/internal/contracts"
    12  	"github.com/nspcc-dev/neo-go/internal/random"
    13  	"github.com/nspcc-dev/neo-go/pkg/config"
    14  	"github.com/nspcc-dev/neo-go/pkg/config/netmode"
    15  	"github.com/nspcc-dev/neo-go/pkg/core"
    16  	"github.com/nspcc-dev/neo-go/pkg/core/block"
    17  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    18  	"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
    19  	"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
    20  	"github.com/nspcc-dev/neo-go/pkg/core/native"
    21  	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
    22  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    23  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    24  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    25  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    26  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    27  	"github.com/nspcc-dev/neo-go/pkg/io"
    28  	"github.com/nspcc-dev/neo-go/pkg/neotest"
    29  	"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
    30  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    31  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    32  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    33  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
    34  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    35  	"github.com/nspcc-dev/neo-go/pkg/util"
    36  	"github.com/nspcc-dev/neo-go/pkg/vm"
    37  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    38  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    39  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    40  	"github.com/stretchr/testify/require"
    41  )
    42  
    43  var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal", "contracts")
    44  
    45  func getSharpTestTx(sender util.Uint160) *transaction.Transaction {
    46  	tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
    47  	tx.Nonce = 0
    48  	tx.Signers = append(tx.Signers, transaction.Signer{
    49  		Account: sender,
    50  		Scopes:  transaction.CalledByEntry,
    51  	})
    52  	tx.Attributes = []transaction.Attribute{}
    53  	tx.Scripts = append(tx.Scripts, transaction.Witness{InvocationScript: []byte{}, VerificationScript: []byte{}})
    54  	return tx
    55  }
    56  
    57  func getSharpTestGenesis(t *testing.T) *block.Block {
    58  	const configPath = "../../../../config"
    59  
    60  	cfg, err := config.Load(configPath, netmode.MainNet)
    61  	require.NoError(t, err)
    62  	b, err := core.CreateGenesisBlock(cfg.ProtocolConfiguration)
    63  	require.NoError(t, err)
    64  	return b
    65  }
    66  
    67  func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
    68  	chain, _ := chain.NewSingle(t)
    69  	ic, err := chain.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
    70  	require.NoError(t, err)
    71  	v := ic.SpawnVM()
    72  	return v, ic, chain
    73  }
    74  
    75  func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...any) {
    76  	ic.SpawnVM()
    77  	ic.VM.LoadScriptWithHash(script, hash, f)
    78  	for i := range args {
    79  		ic.VM.Estack().PushVal(args[i])
    80  	}
    81  	ic.VM.GasLimit = -1
    82  }
    83  
    84  func wrapDynamicScript(t *testing.T, script []byte, flags callflag.CallFlag, args ...any) []byte {
    85  	b := io.NewBufBinWriter()
    86  
    87  	// Params.
    88  	emit.Array(b.BinWriter, args...)
    89  	emit.Int(b.BinWriter, int64(flags))
    90  	emit.Bytes(b.BinWriter, script)
    91  
    92  	// Wrapped syscall.
    93  	emit.Instruction(b.BinWriter, opcode.TRY, []byte{3 + 5 + 2, 0})  // 3
    94  	emit.Syscall(b.BinWriter, interopnames.SystemRuntimeLoadScript)  // 5
    95  	emit.Instruction(b.BinWriter, opcode.ENDTRY, []byte{1 + 11 + 2}) // 2
    96  
    97  	// Catch block
    98  	emit.Opcodes(b.BinWriter, opcode.DROP)
    99  	emit.String(b.BinWriter, "exception") // 1 + 1 + 9 == 11 bytes
   100  	emit.Opcodes(b.BinWriter, opcode.RET)
   101  
   102  	require.NoError(t, b.Err)
   103  	return b.Bytes()
   104  }
   105  
   106  func getDeployedInternal(t *testing.T) (*neotest.Executor, neotest.Signer, *core.Blockchain, *state.Contract) {
   107  	bc, acc := chain.NewSingle(t)
   108  	e := neotest.NewExecutor(t, bc, acc, acc)
   109  	managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
   110  
   111  	cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash())
   112  	rawManifest, err := json.Marshal(cs.Manifest)
   113  	require.NoError(t, err)
   114  	rawNef, err := cs.NEF.Bytes()
   115  	require.NoError(t, err)
   116  	tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
   117  	e.AddNewBlock(t, tx)
   118  	e.CheckHalt(t, tx.Hash())
   119  
   120  	return e, acc, bc, cs
   121  }
   122  
   123  func TestBurnGas(t *testing.T) {
   124  	e, acc, _, cs := getDeployedInternal(t)
   125  	cInvoker := e.ValidatorInvoker(cs.Hash)
   126  
   127  	t.Run("good", func(t *testing.T) {
   128  		h := cInvoker.Invoke(t, stackitem.Null{}, "burnGas", int64(1))
   129  		res := e.GetTxExecResult(t, h)
   130  
   131  		t.Run("gas limit exceeded", func(t *testing.T) {
   132  			tx := e.NewUnsignedTx(t, cs.Hash, "burnGas", int64(2))
   133  			e.SignTx(t, tx, res.GasConsumed, acc)
   134  			e.AddNewBlock(t, tx)
   135  			e.CheckFault(t, tx.Hash(), "GAS limit exceeded")
   136  		})
   137  	})
   138  	t.Run("too big integer", func(t *testing.T) {
   139  		gas := big.NewInt(math.MaxInt64)
   140  		gas.Add(gas, big.NewInt(1))
   141  
   142  		cInvoker.InvokeFail(t, "invalid GAS value", "burnGas", gas)
   143  	})
   144  	t.Run("zero GAS", func(t *testing.T) {
   145  		cInvoker.InvokeFail(t, "GAS must be positive", "burnGas", int64(0))
   146  	})
   147  }
   148  
   149  func TestCheckWitness(t *testing.T) {
   150  	_, ic, _ := createVM(t)
   151  
   152  	script := []byte{byte(opcode.RET)}
   153  	scriptHash := hash.Hash160(script)
   154  	check := func(t *testing.T, ic *interop.Context, arg any, shouldFail bool, expected ...bool) {
   155  		ic.VM.Estack().PushVal(arg)
   156  		err := runtime.CheckWitness(ic)
   157  		if shouldFail {
   158  			require.Error(t, err)
   159  		} else {
   160  			require.NoError(t, err)
   161  			require.NotNil(t, expected)
   162  			actual, ok := ic.VM.Estack().Pop().Value().(bool)
   163  			require.True(t, ok)
   164  			require.Equal(t, expected[0], actual)
   165  		}
   166  	}
   167  	t.Run("error", func(t *testing.T) {
   168  		t.Run("not a hash or key", func(t *testing.T) {
   169  			check(t, ic, []byte{1, 2, 3}, true)
   170  		})
   171  		t.Run("script container is not a transaction", func(t *testing.T) {
   172  			loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   173  			check(t, ic, random.Uint160().BytesBE(), true)
   174  		})
   175  		t.Run("check scope", func(t *testing.T) {
   176  			t.Run("CustomGroups, missing ReadStates flag", func(t *testing.T) {
   177  				hash := random.Uint160()
   178  				tx := &transaction.Transaction{
   179  					Signers: []transaction.Signer{
   180  						{
   181  							Account:       hash,
   182  							Scopes:        transaction.CustomGroups,
   183  							AllowedGroups: []*keys.PublicKey{},
   184  						},
   185  					},
   186  				}
   187  				ic.Tx = tx
   188  				callingScriptHash := scriptHash
   189  				loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All)
   190  				ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.AllowCall)
   191  				check(t, ic, hash.BytesBE(), true)
   192  			})
   193  			t.Run("Rules, missing ReadStates flag", func(t *testing.T) {
   194  				hash := random.Uint160()
   195  				pk, err := keys.NewPrivateKey()
   196  				require.NoError(t, err)
   197  				tx := &transaction.Transaction{
   198  					Signers: []transaction.Signer{
   199  						{
   200  							Account: hash,
   201  							Scopes:  transaction.Rules,
   202  							Rules: []transaction.WitnessRule{{
   203  								Action:    transaction.WitnessAllow,
   204  								Condition: (*transaction.ConditionGroup)(pk.PublicKey()),
   205  							}},
   206  						},
   207  					},
   208  				}
   209  				ic.Tx = tx
   210  				callingScriptHash := scriptHash
   211  				loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All)
   212  				ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.AllowCall)
   213  				check(t, ic, hash.BytesBE(), true)
   214  			})
   215  		})
   216  	})
   217  	t.Run("positive", func(t *testing.T) {
   218  		t.Run("calling scripthash", func(t *testing.T) {
   219  			t.Run("hashed witness", func(t *testing.T) {
   220  				callingScriptHash := scriptHash
   221  				loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All)
   222  				ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.All)
   223  				check(t, ic, callingScriptHash.BytesBE(), false, true)
   224  			})
   225  			t.Run("keyed witness", func(t *testing.T) {
   226  				pk, err := keys.NewPrivateKey()
   227  				require.NoError(t, err)
   228  				callingScriptHash := pk.PublicKey().GetScriptHash()
   229  				loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All)
   230  				ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.All)
   231  				check(t, ic, pk.PublicKey().Bytes(), false, true)
   232  			})
   233  		})
   234  		t.Run("check scope", func(t *testing.T) {
   235  			t.Run("Global", func(t *testing.T) {
   236  				hash := random.Uint160()
   237  				tx := &transaction.Transaction{
   238  					Signers: []transaction.Signer{
   239  						{
   240  							Account: hash,
   241  							Scopes:  transaction.Global,
   242  						},
   243  					},
   244  				}
   245  				loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   246  				ic.Tx = tx
   247  				check(t, ic, hash.BytesBE(), false, true)
   248  			})
   249  			t.Run("CalledByEntry", func(t *testing.T) {
   250  				hash := random.Uint160()
   251  				tx := &transaction.Transaction{
   252  					Signers: []transaction.Signer{
   253  						{
   254  							Account: hash,
   255  							Scopes:  transaction.CalledByEntry,
   256  						},
   257  					},
   258  				}
   259  				loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   260  				ic.Tx = tx
   261  				check(t, ic, hash.BytesBE(), false, true)
   262  			})
   263  			t.Run("CustomContracts", func(t *testing.T) {
   264  				hash := random.Uint160()
   265  				tx := &transaction.Transaction{
   266  					Signers: []transaction.Signer{
   267  						{
   268  							Account:          hash,
   269  							Scopes:           transaction.CustomContracts,
   270  							AllowedContracts: []util.Uint160{scriptHash},
   271  						},
   272  					},
   273  				}
   274  				loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   275  				ic.Tx = tx
   276  				check(t, ic, hash.BytesBE(), false, true)
   277  			})
   278  			t.Run("CustomGroups", func(t *testing.T) {
   279  				t.Run("unknown scripthash", func(t *testing.T) {
   280  					hash := random.Uint160()
   281  					tx := &transaction.Transaction{
   282  						Signers: []transaction.Signer{
   283  							{
   284  								Account:       hash,
   285  								Scopes:        transaction.CustomGroups,
   286  								AllowedGroups: []*keys.PublicKey{},
   287  							},
   288  						},
   289  					}
   290  					loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   291  					ic.Tx = tx
   292  					check(t, ic, hash.BytesBE(), false, false)
   293  				})
   294  				t.Run("positive", func(t *testing.T) {
   295  					targetHash := random.Uint160()
   296  					pk, err := keys.NewPrivateKey()
   297  					require.NoError(t, err)
   298  					tx := &transaction.Transaction{
   299  						Signers: []transaction.Signer{
   300  							{
   301  								Account:       targetHash,
   302  								Scopes:        transaction.CustomGroups,
   303  								AllowedGroups: []*keys.PublicKey{pk.PublicKey()},
   304  							},
   305  						},
   306  					}
   307  					contractScript := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
   308  					contractScriptHash := hash.Hash160(contractScript)
   309  					ne, err := nef.NewFile(contractScript)
   310  					require.NoError(t, err)
   311  					contractState := &state.Contract{
   312  						ContractBase: state.ContractBase{
   313  							ID:   15,
   314  							Hash: contractScriptHash,
   315  							NEF:  *ne,
   316  							Manifest: manifest.Manifest{
   317  								Groups: []manifest.Group{{PublicKey: pk.PublicKey(), Signature: make([]byte, keys.SignatureLen)}},
   318  							},
   319  						},
   320  					}
   321  					require.NoError(t, native.PutContractState(ic.DAO, contractState))
   322  					loadScriptWithHashAndFlags(ic, contractScript, contractScriptHash, callflag.All)
   323  					ic.Tx = tx
   324  					check(t, ic, targetHash.BytesBE(), false, true)
   325  				})
   326  			})
   327  			t.Run("Rules", func(t *testing.T) {
   328  				t.Run("no match", func(t *testing.T) {
   329  					hash := random.Uint160()
   330  					tx := &transaction.Transaction{
   331  						Signers: []transaction.Signer{
   332  							{
   333  								Account: hash,
   334  								Scopes:  transaction.Rules,
   335  								Rules: []transaction.WitnessRule{{
   336  									Action:    transaction.WitnessAllow,
   337  									Condition: (*transaction.ConditionScriptHash)(&hash),
   338  								}},
   339  							},
   340  						},
   341  					}
   342  					loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   343  					ic.Tx = tx
   344  					check(t, ic, hash.BytesBE(), false, false)
   345  				})
   346  				t.Run("allow", func(t *testing.T) {
   347  					hash := random.Uint160()
   348  					var cond = true
   349  					tx := &transaction.Transaction{
   350  						Signers: []transaction.Signer{
   351  							{
   352  								Account: hash,
   353  								Scopes:  transaction.Rules,
   354  								Rules: []transaction.WitnessRule{{
   355  									Action:    transaction.WitnessAllow,
   356  									Condition: (*transaction.ConditionBoolean)(&cond),
   357  								}},
   358  							},
   359  						},
   360  					}
   361  					loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   362  					ic.Tx = tx
   363  					check(t, ic, hash.BytesBE(), false, true)
   364  				})
   365  				t.Run("deny", func(t *testing.T) {
   366  					hash := random.Uint160()
   367  					var cond = true
   368  					tx := &transaction.Transaction{
   369  						Signers: []transaction.Signer{
   370  							{
   371  								Account: hash,
   372  								Scopes:  transaction.Rules,
   373  								Rules: []transaction.WitnessRule{{
   374  									Action:    transaction.WitnessDeny,
   375  									Condition: (*transaction.ConditionBoolean)(&cond),
   376  								}},
   377  							},
   378  						},
   379  					}
   380  					loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   381  					ic.Tx = tx
   382  					check(t, ic, hash.BytesBE(), false, false)
   383  				})
   384  			})
   385  			t.Run("bad scope", func(t *testing.T) {
   386  				hash := random.Uint160()
   387  				tx := &transaction.Transaction{
   388  					Signers: []transaction.Signer{
   389  						{
   390  							Account: hash,
   391  							Scopes:  transaction.None,
   392  						},
   393  					},
   394  				}
   395  				loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
   396  				ic.Tx = tx
   397  				check(t, ic, hash.BytesBE(), false, false)
   398  			})
   399  		})
   400  	})
   401  }
   402  
   403  func TestLoadScript(t *testing.T) {
   404  	bc, acc := chain.NewSingle(t)
   405  	e := neotest.NewExecutor(t, bc, acc, acc)
   406  
   407  	t.Run("no ret val", func(t *testing.T) {
   408  		script := wrapDynamicScript(t, []byte{byte(opcode.RET)}, callflag.All)
   409  		e.InvokeScriptCheckHALT(t, script, []neotest.Signer{acc}, stackitem.Null{})
   410  	})
   411  	t.Run("empty script", func(t *testing.T) {
   412  		script := wrapDynamicScript(t, []byte{}, callflag.All)
   413  		e.InvokeScriptCheckHALT(t, script, []neotest.Signer{acc}, stackitem.Null{})
   414  	})
   415  	t.Run("bad script", func(t *testing.T) {
   416  		script := wrapDynamicScript(t, []byte{0xff}, callflag.All)
   417  		e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "invalid script")
   418  	})
   419  	t.Run("ret val, no params", func(t *testing.T) {
   420  		script := wrapDynamicScript(t, []byte{byte(opcode.PUSH1)}, callflag.All)
   421  		e.InvokeScriptCheckHALT(t, script, []neotest.Signer{acc}, stackitem.Make(1))
   422  	})
   423  	t.Run("ret val with params", func(t *testing.T) {
   424  		script := wrapDynamicScript(t, []byte{byte(opcode.MUL)}, callflag.All, 2, 2)
   425  		e.InvokeScriptCheckHALT(t, script, []neotest.Signer{acc}, stackitem.Make(4))
   426  	})
   427  	t.Run("two retrun values", func(t *testing.T) {
   428  		script := wrapDynamicScript(t, []byte{byte(opcode.PUSH1), byte(opcode.PUSH1)}, callflag.All, 2, 2)
   429  		e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "multiple return values in a cross-contract call")
   430  	})
   431  	t.Run("invalid flags", func(t *testing.T) {
   432  		script := wrapDynamicScript(t, []byte{byte(opcode.MUL)}, callflag.CallFlag(0xff), 2, 2)
   433  		e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "call flags out of range")
   434  	})
   435  	t.Run("abort", func(t *testing.T) {
   436  		script := wrapDynamicScript(t, []byte{byte(opcode.ABORT)}, callflag.All)
   437  		e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "ABORT")
   438  	})
   439  	t.Run("internal call", func(t *testing.T) {
   440  		script, err := smartcontract.CreateCallScript(e.NativeHash(t, nativenames.Gas), "decimals")
   441  		require.NoError(t, err)
   442  		script = wrapDynamicScript(t, script, callflag.ReadOnly)
   443  		e.InvokeScriptCheckHALT(t, script, []neotest.Signer{acc}, stackitem.Make(8))
   444  	})
   445  	t.Run("forbidden internal call", func(t *testing.T) {
   446  		script, err := smartcontract.CreateCallScript(e.NativeHash(t, nativenames.Neo), "decimals")
   447  		require.NoError(t, err)
   448  		script = wrapDynamicScript(t, script, callflag.ReadStates)
   449  		e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "missing call flags")
   450  	})
   451  	t.Run("internal state-changing call", func(t *testing.T) {
   452  		script, err := smartcontract.CreateCallScript(e.NativeHash(t, nativenames.Neo), "transfer", acc.ScriptHash(), acc.ScriptHash(), 1, nil)
   453  		require.NoError(t, err)
   454  		script = wrapDynamicScript(t, script, callflag.All)
   455  		e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "missing call flags")
   456  	})
   457  	t.Run("exception", func(t *testing.T) {
   458  		script := wrapDynamicScript(t, []byte{byte(opcode.PUSH1), byte(opcode.THROW)}, callflag.ReadOnly)
   459  		e.InvokeScriptCheckHALT(t, script, []neotest.Signer{acc}, stackitem.Make("exception"))
   460  	})
   461  }
   462  
   463  func TestGasLeft(t *testing.T) {
   464  	const runtimeGasLeftPrice = 1 << 4
   465  
   466  	bc, acc := chain.NewSingle(t)
   467  	e := neotest.NewExecutor(t, bc, acc, acc)
   468  	w := io.NewBufBinWriter()
   469  
   470  	gasLimit := 1100
   471  	emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGasLeft)
   472  	emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGasLeft)
   473  	require.NoError(t, w.Err)
   474  	tx := transaction.New(w.Bytes(), int64(gasLimit))
   475  	tx.Nonce = neotest.Nonce()
   476  	tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
   477  	e.SignTx(t, tx, int64(gasLimit), acc)
   478  	e.AddNewBlock(t, tx)
   479  	e.CheckHalt(t, tx.Hash())
   480  	res := e.GetTxExecResult(t, tx.Hash())
   481  	l1 := res.Stack[0].Value().(*big.Int)
   482  	l2 := res.Stack[1].Value().(*big.Int)
   483  
   484  	require.Equal(t, int64(gasLimit-runtimeGasLeftPrice*interop.DefaultBaseExecFee), l1.Int64())
   485  	require.Equal(t, int64(gasLimit-2*runtimeGasLeftPrice*interop.DefaultBaseExecFee), l2.Int64())
   486  }
   487  
   488  func TestGetAddressVersion(t *testing.T) {
   489  	bc, acc := chain.NewSingle(t)
   490  	e := neotest.NewExecutor(t, bc, acc, acc)
   491  	w := io.NewBufBinWriter()
   492  
   493  	emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetAddressVersion)
   494  	require.NoError(t, w.Err)
   495  	e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(big.NewInt(int64(address.NEO3Prefix))))
   496  }
   497  
   498  func TestGetInvocationCounter(t *testing.T) {
   499  	v, ic, _ := createVM(t)
   500  
   501  	cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
   502  	require.NoError(t, native.PutContractState(ic.DAO, cs))
   503  
   504  	ic.Invocations[hash.Hash160([]byte{2})] = 42
   505  
   506  	t.Run("No invocations", func(t *testing.T) {
   507  		v.Load([]byte{1})
   508  		// do not return an error in this case.
   509  		require.NoError(t, runtime.GetInvocationCounter(ic))
   510  		require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64())
   511  	})
   512  	t.Run("NonZero", func(t *testing.T) {
   513  		v.Load([]byte{2})
   514  		require.NoError(t, runtime.GetInvocationCounter(ic))
   515  		require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
   516  	})
   517  	t.Run("Contract", func(t *testing.T) {
   518  		script, err := smartcontract.CreateCallScript(cs.Hash, "invocCounter")
   519  		require.NoError(t, err)
   520  		v.LoadWithFlags(script, callflag.All)
   521  		require.NoError(t, v.Run())
   522  		require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64())
   523  	})
   524  }
   525  
   526  func TestGetNetwork(t *testing.T) {
   527  	bc, acc := chain.NewSingle(t)
   528  	e := neotest.NewExecutor(t, bc, acc, acc)
   529  	w := io.NewBufBinWriter()
   530  
   531  	emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetNetwork)
   532  	require.NoError(t, w.Err)
   533  	e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(big.NewInt(int64(bc.GetConfig().Magic))))
   534  }
   535  
   536  func TestGetNotifications(t *testing.T) {
   537  	v, ic, _ := createVM(t)
   538  
   539  	ic.Notifications = []state.NotificationEvent{
   540  		{ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{11})})},
   541  		{ScriptHash: util.Uint160{2}, Name: "Event2", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{22})})},
   542  		{ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{33})})},
   543  	}
   544  
   545  	t.Run("NoFilter", func(t *testing.T) {
   546  		v.Estack().PushVal(stackitem.Null{})
   547  		require.NoError(t, runtime.GetNotifications(ic))
   548  
   549  		arr := v.Estack().Pop().Array()
   550  		require.Equal(t, len(ic.Notifications), len(arr))
   551  		for i := range arr {
   552  			elem := arr[i].Value().([]stackitem.Item)
   553  			require.Equal(t, ic.Notifications[i].ScriptHash.BytesBE(), elem[0].Value())
   554  			name, err := stackitem.ToString(elem[1])
   555  			require.NoError(t, err)
   556  			require.Equal(t, ic.Notifications[i].Name, name)
   557  			ic.Notifications[i].Item.MarkAsReadOnly() // tiny hack for test to be able to compare object references.
   558  			require.Equal(t, ic.Notifications[i].Item, elem[2])
   559  		}
   560  	})
   561  
   562  	t.Run("WithFilter", func(t *testing.T) {
   563  		h := util.Uint160{2}.BytesBE()
   564  		v.Estack().PushVal(h)
   565  		require.NoError(t, runtime.GetNotifications(ic))
   566  
   567  		arr := v.Estack().Pop().Array()
   568  		require.Equal(t, 1, len(arr))
   569  		elem := arr[0].Value().([]stackitem.Item)
   570  		require.Equal(t, h, elem[0].Value())
   571  		name, err := stackitem.ToString(elem[1])
   572  		require.NoError(t, err)
   573  		require.Equal(t, ic.Notifications[1].Name, name)
   574  		require.Equal(t, ic.Notifications[1].Item, elem[2])
   575  	})
   576  }
   577  
   578  func TestGetRandom_DifferentTransactions(t *testing.T) {
   579  	bc, acc := chain.NewSingle(t)
   580  	e := neotest.NewExecutor(t, bc, acc, acc)
   581  
   582  	w := io.NewBufBinWriter()
   583  	emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetRandom)
   584  	require.NoError(t, w.Err)
   585  	script := w.Bytes()
   586  
   587  	tx1 := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
   588  	tx2 := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
   589  	e.AddNewBlock(t, tx1, tx2)
   590  	e.CheckHalt(t, tx1.Hash())
   591  	e.CheckHalt(t, tx2.Hash())
   592  
   593  	res1 := e.GetTxExecResult(t, tx1.Hash())
   594  	res2 := e.GetTxExecResult(t, tx2.Hash())
   595  
   596  	r1, err := res1.Stack[0].TryInteger()
   597  	require.NoError(t, err)
   598  	r2, err := res2.Stack[0].TryInteger()
   599  	require.NoError(t, err)
   600  	require.NotEqual(t, r1, r2)
   601  }
   602  
   603  // Tests are taken from
   604  // https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs
   605  func TestGetRandomCompatibility(t *testing.T) {
   606  	bc, _ := chain.NewSingle(t)
   607  
   608  	b := getSharpTestGenesis(t)
   609  	tx := getSharpTestTx(util.Uint160{})
   610  	ic, err := bc.GetTestVM(trigger.Application, tx, b)
   611  	require.NoError(t, err)
   612  	ic.Network = 860833102 // Old mainnet magic used by C# tests.
   613  
   614  	ic.VM = vm.New()
   615  	ic.VM.LoadScript([]byte{0x01})
   616  	ic.VM.GasLimit = 1100_00000000
   617  
   618  	require.NoError(t, runtime.GetRandom(ic))
   619  	require.Equal(t, "271339657438512451304577787170704246350", ic.VM.Estack().Pop().BigInt().String())
   620  
   621  	require.NoError(t, runtime.GetRandom(ic))
   622  	require.Equal(t, "98548189559099075644778613728143131367", ic.VM.Estack().Pop().BigInt().String())
   623  
   624  	require.NoError(t, runtime.GetRandom(ic))
   625  	require.Equal(t, "247654688993873392544380234598471205121", ic.VM.Estack().Pop().BigInt().String())
   626  
   627  	require.NoError(t, runtime.GetRandom(ic))
   628  	require.Equal(t, "291082758879475329976578097236212073607", ic.VM.Estack().Pop().BigInt().String())
   629  
   630  	require.NoError(t, runtime.GetRandom(ic))
   631  	require.Equal(t, "247152297361212656635216876565962360375", ic.VM.Estack().Pop().BigInt().String())
   632  }
   633  
   634  func TestNotify(t *testing.T) {
   635  	caller := random.Uint160()
   636  	newIC := func(name string, args any) *interop.Context {
   637  		_, _, bc, cs := getDeployedInternal(t)
   638  		ic, err := bc.GetTestVM(trigger.Application, nil, nil)
   639  		require.NoError(t, err)
   640  		ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
   641  		ic.VM.Estack().PushVal(args)
   642  		ic.VM.Estack().PushVal(name)
   643  		return ic
   644  	}
   645  	t.Run("big name", func(t *testing.T) {
   646  		ic := newIC(string(make([]byte, runtime.MaxEventNameLen+1)), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}))
   647  		err := runtime.Notify(ic)
   648  		require.Error(t, err)
   649  		require.True(t, strings.Contains(err.Error(), "event name must be less than 32"), err)
   650  	})
   651  	t.Run("dynamic script", func(t *testing.T) {
   652  		ic := newIC("some", stackitem.Null{})
   653  		ic.VM.LoadScriptWithHash([]byte{1}, random.Uint160(), callflag.NoneFlag)
   654  		ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(42)}))
   655  		ic.VM.Estack().PushVal("event")
   656  		err := runtime.Notify(ic)
   657  		require.Error(t, err)
   658  		require.True(t, strings.Contains(err.Error(), "notifications are not allowed in dynamic scripts"), err)
   659  	})
   660  	t.Run("recursive struct", func(t *testing.T) {
   661  		arr := stackitem.NewArray([]stackitem.Item{stackitem.Null{}})
   662  		arr.Append(arr)
   663  		ic := newIC("event", stackitem.NewArray([]stackitem.Item{arr})) // upper array is needed to match manifest event signature.
   664  		err := runtime.Notify(ic)
   665  		require.Error(t, err)
   666  		require.True(t, strings.Contains(err.Error(), "bad notification: recursive item"), err)
   667  	})
   668  	t.Run("big notification", func(t *testing.T) {
   669  		bs := stackitem.NewByteArray(make([]byte, runtime.MaxNotificationSize+1))
   670  		arr := stackitem.NewArray([]stackitem.Item{bs})
   671  		ic := newIC("event", arr)
   672  		err := runtime.Notify(ic)
   673  		require.Error(t, err)
   674  		require.True(t, strings.Contains(err.Error(), "notification size shouldn't exceed 1024"), err)
   675  	})
   676  	t.Run("good", func(t *testing.T) {
   677  		arr := stackitem.NewArray([]stackitem.Item{stackitem.Make(42)})
   678  		ic := newIC("event", arr)
   679  		require.NoError(t, runtime.Notify(ic))
   680  		require.Equal(t, 1, len(ic.Notifications))
   681  
   682  		arr.MarkAsReadOnly() // tiny hack for test to be able to compare object references.
   683  		ev := ic.Notifications[0]
   684  		require.Equal(t, "event", ev.Name)
   685  		require.Equal(t, ic.VM.GetCurrentScriptHash(), ev.ScriptHash)
   686  		require.Equal(t, arr, ev.Item)
   687  		// Check deep copy.
   688  		arr.Value().([]stackitem.Item)[0] = stackitem.Null{}
   689  		require.NotEqual(t, arr, ev.Item)
   690  	})
   691  }
   692  
   693  func TestSystemRuntimeNotify_HFBasilisk(t *testing.T) {
   694  	const (
   695  		ntfName       = "Hello, world!"
   696  		enabledHeight = 3
   697  	)
   698  
   699  	bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) {
   700  		c.Hardforks = map[string]uint32{
   701  			config.HFBasilisk.String(): enabledHeight,
   702  		}
   703  	})
   704  	e := neotest.NewExecutor(t, bc, acc, acc)
   705  
   706  	script := io.NewBufBinWriter()
   707  	emit.Array(script.BinWriter, stackitem.Make(true)) // Boolean instead of Integer declared in manifest
   708  	emit.String(script.BinWriter, ntfName)
   709  	emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify)
   710  	require.NoError(t, script.Err)
   711  	ne, err := nef.NewFile(script.Bytes())
   712  	require.NoError(t, err)
   713  
   714  	m := &manifest.Manifest{
   715  		Name: "ctr",
   716  		ABI: manifest.ABI{
   717  			Methods: []manifest.Method{
   718  				{
   719  					Name:       "main",
   720  					Offset:     0,
   721  					ReturnType: smartcontract.VoidType,
   722  				},
   723  			},
   724  			Events: []manifest.Event{
   725  				{
   726  					Name: ntfName,
   727  					Parameters: []manifest.Parameter{
   728  						{
   729  							Name: "int",
   730  							Type: smartcontract.IntegerType,
   731  						},
   732  					},
   733  				},
   734  			},
   735  		},
   736  	}
   737  	ctr := &neotest.Contract{
   738  		Hash:     state.CreateContractHash(e.Validator.ScriptHash(), ne.Checksum, m.Name),
   739  		NEF:      ne,
   740  		Manifest: m,
   741  	}
   742  	ctrInv := e.NewInvoker(ctr.Hash, e.Validator)
   743  
   744  	// Block 0 is genesis.
   745  
   746  	// Block 1: deploy contract.
   747  	e.DeployContract(t, ctr, nil)
   748  
   749  	// Block 2: bad event should be logged.
   750  	ctrInv.Invoke(t, nil, "main")
   751  
   752  	// Block 3: bad event should fault the execution.
   753  	require.Equal(t, uint32(enabledHeight-1), e.Chain.BlockHeight())
   754  	ctrInv.InvokeFail(t,
   755  		"System.Runtime.Notify failed: notification Hello, world! is invalid: parameter 0 type mismatch: Integer (manifest) vs Boolean (notification)",
   756  		"main")
   757  }