core, rpc: move getFakeNextBlock to Blockchain

It's needed for VM CLI as far and may be improved later.
This commit is contained in:
Anna Shaleva 2022-10-06 13:24:57 +03:00
parent 0036c89d63
commit 79e13f73d8
13 changed files with 136 additions and 89 deletions

View file

@ -333,7 +333,10 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
}
// Do not run chain, we need only state-related functionality from it.
ic := chain.GetTestVM(trigger.Application, nil, nil)
ic, err := chain.GetTestVM(trigger.Application, nil, nil)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to create test VM: %w", err), 1)
}
vmcli := VMCLI{
chain: chain,
@ -483,7 +486,10 @@ func handleSlots(c *cli.Context) error {
}
func handleLoadNEF(c *cli.Context) error {
resetState(c.App)
err := resetState(c.App)
if err != nil {
return err
}
v := getVMFromContext(c.App)
args := c.Args()
if len(args) < 2 {
@ -503,7 +509,10 @@ func handleLoadNEF(c *cli.Context) error {
}
func handleLoadBase64(c *cli.Context) error {
resetState(c.App)
err := resetState(c.App)
if err != nil {
return err
}
v := getVMFromContext(c.App)
args := c.Args()
if len(args) < 1 {
@ -520,7 +529,10 @@ func handleLoadBase64(c *cli.Context) error {
}
func handleLoadHex(c *cli.Context) error {
resetState(c.App)
err := resetState(c.App)
if err != nil {
return err
}
v := getVMFromContext(c.App)
args := c.Args()
if len(args) < 1 {
@ -537,7 +549,10 @@ func handleLoadHex(c *cli.Context) error {
}
func handleLoadGo(c *cli.Context) error {
resetState(c.App)
err := resetState(c.App)
if err != nil {
return err
}
v := getVMFromContext(c.App)
args := c.Args()
if len(args) < 1 {
@ -564,7 +579,10 @@ func handleLoadGo(c *cli.Context) error {
}
func handleReset(c *cli.Context) error {
resetState(c.App)
err := resetState(c.App)
if err != nil {
return err
}
changePrompt(c.App)
return nil
}
@ -577,11 +595,15 @@ func finalizeInteropContext(app *cli.App) {
// resetInteropContext calls finalizer for current interop context and replaces
// it with the newly created one.
func resetInteropContext(app *cli.App) {
func resetInteropContext(app *cli.App) error {
finalizeInteropContext(app)
bc := getChainFromContext(app)
newIc := bc.GetTestVM(trigger.Application, nil, nil)
newIc, err := bc.GetTestVM(trigger.Application, nil, nil)
if err != nil {
return fmt.Errorf("failed to create test VM: %w", err)
}
setInteropContextInContext(app, newIc)
return nil
}
// resetManifest removes manifest from app context.
@ -591,9 +613,13 @@ func resetManifest(app *cli.App) {
// resetState resets state of the app (clear interop context and manifest) so that it's ready
// to load new program.
func resetState(app *cli.App) {
resetInteropContext(app)
func resetState(app *cli.App) error {
err := resetInteropContext(app)
if err != nil {
return fmt.Errorf("failed to reset interop context state: %w", err)
}
resetManifest(app)
return nil
}
func getManifestFromFile(name string) (*manifest.Manifest, error) {

View file

@ -298,7 +298,7 @@ func (chain *FakeChain) GetStorageItem(id int32, key []byte) state.StorageItem {
}
// GetTestVM implements the Blockchainer interface.
func (chain *FakeChain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context {
func (chain *FakeChain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error) {
panic("TODO")
}

View file

@ -2241,19 +2241,28 @@ func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
}
// GetTestVM returns an interop context with VM set up for a test run.
func (bc *Blockchain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context {
func (bc *Blockchain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error) {
if b == nil {
var err error
h := bc.BlockHeight() + 1
b, err = bc.getFakeNextBlock(h)
if err != nil {
return nil, fmt.Errorf("failed to create fake block for height %d: %w", h, err)
}
}
systemInterop := bc.newInteropContext(t, bc.dao, b, tx)
_ = systemInterop.SpawnVM() // All the other code suppose that the VM is ready.
return systemInterop
return systemInterop, nil
}
// GetTestHistoricVM returns an interop context with VM set up for a test run.
func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error) {
func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, nextBlockHeight uint32) (*interop.Context, error) {
if bc.config.KeepOnlyLatestState {
return nil, errors.New("only latest state is supported")
}
if b == nil {
return nil, errors.New("block is mandatory to produce test historic VM")
b, err := bc.getFakeNextBlock(nextBlockHeight)
if err != nil {
return nil, fmt.Errorf("failed to create fake block for height %d: %w", nextBlockHeight, err)
}
var mode = mpt.ModeAll
if bc.config.RemoveUntraceableBlocks {
@ -2284,6 +2293,18 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact
return systemInterop, nil
}
// getFakeNextBlock returns fake block with the specified index and pre-filled Timestamp field.
func (bc *Blockchain) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, error) {
b := block.New(bc.config.StateRootInHeader)
b.Index = nextBlockHeight
hdr, err := bc.GetHeader(bc.GetHeaderHash(int(nextBlockHeight - 1)))
if err != nil {
return nil, err
}
b.Timestamp = hdr.Timestamp + uint64(bc.config.SecondsPerBlock*int(time.Second/time.Millisecond))
return b, nil
}
// Various witness verification errors.
var (
ErrWitnessHashMismatch = errors.New("witness hash mismatch")

View file

@ -32,7 +32,8 @@ var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal",
func TestGetCallFlags(t *testing.T) {
bc, _ := chain.NewSingle(t)
ic := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
ic, err := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
require.NoError(t, err)
ic.VM.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, callflag.All)
require.NoError(t, contract.GetCallFlags(ic))
@ -41,7 +42,8 @@ func TestGetCallFlags(t *testing.T) {
func TestCall(t *testing.T) {
bc, _ := chain.NewSingle(t)
ic := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
ic, err := bc.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
require.NoError(t, err)
cs, currCs := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
require.NoError(t, native.PutContractState(ic.DAO, cs))

View file

@ -65,7 +65,8 @@ func getSharpTestGenesis(t *testing.T) *block.Block {
func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
chain, _ := chain.NewSingle(t)
ic := chain.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
ic, err := chain.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
require.NoError(t, err)
v := ic.SpawnVM()
return v, ic, chain
}
@ -523,7 +524,8 @@ func TestGetRandomCompatibility(t *testing.T) {
b := getSharpTestGenesis(t)
tx := getSharpTestTx(util.Uint160{})
ic := bc.GetTestVM(trigger.Application, tx, b)
ic, err := bc.GetTestVM(trigger.Application, tx, b)
require.NoError(t, err)
ic.Network = 860833102 // Old mainnet magic used by C# tests.
ic.VM = vm.New()
@ -550,7 +552,8 @@ func TestNotify(t *testing.T) {
caller := random.Uint160()
newIC := func(name string, args interface{}) *interop.Context {
_, _, bc, cs := getDeployedInternal(t)
ic := bc.GetTestVM(trigger.Application, nil, nil)
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err)
ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
ic.VM.Estack().PushVal(args)
ic.VM.Estack().PushVal(name)

View file

@ -300,7 +300,8 @@ func TestFind(t *testing.T) {
func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
chain, _ := chain.NewSingle(t)
ic := chain.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
ic, err := chain.GetTestVM(trigger.Application, &transaction.Transaction{}, &block.Block{})
require.NoError(t, err)
v := ic.SpawnVM()
return v, ic, chain
}

View file

@ -74,7 +74,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
require.NotNil(t, md)
t.Run("fail, bad current script hash", func(t *testing.T) {
ic := bc.GetTestVM(trigger.Application, nil, nil)
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err)
v := ic.SpawnVM()
fakeH := util.Uint160{1, 2, 3}
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
@ -83,7 +84,7 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
v.Context().Jump(md.Offset)
// Bad current script hash
err := v.Run()
err = v.Run()
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error())
})
@ -104,7 +105,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
})
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad)
ic := bcBad.GetTestVM(trigger.Application, nil, nil)
ic, err := bcBad.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err)
v := ic.SpawnVM()
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
input := []byte{1, 2, 3, 4}
@ -112,13 +114,14 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
v.Context().Jump(md.Offset)
// It's prohibited to call natives before NativeUpdateHistory[0] height.
err := v.Run()
err = v.Run()
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
// Add new block => CryptoLib should be active now.
eBad.AddNewBlock(t)
ic = bcBad.GetTestVM(trigger.Application, nil, nil)
ic, err = bcBad.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err)
v = ic.SpawnVM()
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
v.Estack().PushVal(input)
@ -130,7 +133,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
})
t.Run("success", func(t *testing.T) {
ic := bc.GetTestVM(trigger.Application, nil, nil)
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
require.NoError(t, err)
v := ic.SpawnVM()
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
input := []byte{1, 2, 3, 4}

View file

@ -374,7 +374,8 @@ func TestInvoke(bc *core.Blockchain, tx *transaction.Transaction) (*vm.VM, error
// `GetTestVM` as well as `Run` can use a transaction hash which will set a cached value.
// This is unwanted behavior, so we explicitly copy the transaction to perform execution.
ttx := *tx
ic := bc.GetTestVM(trigger.Application, &ttx, b)
ic, _ := bc.GetTestVM(trigger.Application, &ttx, b)
defer ic.Finalize()
ic.VM.LoadWithFlags(tx.Script, callflag.All)

View file

@ -51,11 +51,14 @@ func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker {
func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...interface{}) (*vm.Stack, error) {
tx := c.PrepareInvokeNoSign(t, method, args...)
b := c.NewUnsignedBlock(t, tx)
ic := c.Chain.GetTestVM(trigger.Application, tx, b)
ic, err := c.Chain.GetTestVM(trigger.Application, tx, b)
if err != nil {
return nil, err
}
t.Cleanup(ic.Finalize)
ic.VM.LoadWithFlags(tx.Script, callflag.All)
err := ic.VM.Run()
err = ic.VM.Run()
return ic.VM.Estack(), err
}

View file

@ -29,7 +29,7 @@ type (
GetBaseExecFee() int64
GetConfig() config.ProtocolConfiguration
GetMaxVerificationGAS() int64
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error)
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
}

View file

@ -3,6 +3,7 @@ package oracle
import (
"encoding/hex"
"errors"
"fmt"
gio "io"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
@ -107,7 +108,10 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transa
size := io.GetVarSize(tx)
tx.Scripts = append(tx.Scripts, transaction.Witness{VerificationScript: oracleSignContract})
gasConsumed, ok := o.testVerify(tx)
gasConsumed, ok, err := o.testVerify(tx)
if err != nil {
return nil, fmt.Errorf("failed to prepare `verify` invocation: %w", err)
}
if !ok {
return nil, errors.New("can't verify transaction")
}
@ -131,18 +135,21 @@ func (o *Oracle) CreateResponseTx(gasForResponse int64, vub uint32, resp *transa
return tx, nil
}
func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool) {
func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool, error) {
// (*Blockchain).GetTestVM calls Hash() method of the provided transaction; once being called, this
// method caches transaction hash, but tx building is not yet completed and hash will be changed.
// So, make a copy of the tx to avoid wrong hash caching.
cp := *tx
ic := o.Chain.GetTestVM(trigger.Verification, &cp, nil)
ic, err := o.Chain.GetTestVM(trigger.Verification, &cp, nil)
if err != nil {
return 0, false, fmt.Errorf("failed to create test VM: %w", err)
}
ic.VM.GasLimit = o.Chain.GetMaxVerificationGAS()
ic.VM.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
ic.VM.Context().Jump(o.verifyOffset)
ok := isVerifyOk(ic)
return ic.VM.GasConsumed(), ok
return ic.VM.GasConsumed(), ok, nil
}
func isVerifyOk(ic *interop.Context) bool {

View file

@ -1181,7 +1181,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
require.NoError(t, err)
require.NoError(t, acc.SignTx(testchain.Network(), tx))
require.NoError(t, chain.VerifyTx(tx))
ic := chain.GetTestVM(trigger.Application, tx, nil)
ic, err := chain.GetTestVM(trigger.Application, tx, nil)
require.NoError(t, err)
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, ic.VM.Run())
})
@ -1195,7 +1196,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
})
require.NoError(t, err)
require.NoError(t, chain.VerifyTx(tx))
ic := chain.GetTestVM(trigger.Application, tx, nil)
ic, err := chain.GetTestVM(trigger.Application, tx, nil)
require.NoError(t, err)
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, ic.VM.Run())
require.Equal(t, 2, len(ic.Notifications))
@ -1228,7 +1230,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
require.NoError(t, err)
require.NoError(t, acc.SignTx(testchain.Network(), tx))
require.NoError(t, chain.VerifyTx(tx))
ic := chain.GetTestVM(trigger.Application, tx, nil)
ic, err := chain.GetTestVM(trigger.Application, tx, nil)
require.NoError(t, err)
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, ic.VM.Run())
})

View file

@ -89,8 +89,8 @@ type (
GetNotaryServiceFeePerKey() int64
GetStateModule() core.StateRoot
GetStorageItem(id int32, key []byte) state.StorageItem
GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error)
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context
GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, nextBlockHeight uint32) (*interop.Context, error)
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error)
GetTokenLastUpdated(acc util.Uint160) (map[int32]uint32, error)
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
GetValidators() ([]*keys.PublicKey, error)
@ -1065,11 +1065,10 @@ func (s *Server) invokeReadOnlyMulti(bw *io.BufBinWriter, h util.Uint160, method
}
script := bw.Bytes()
tx := &transaction.Transaction{Script: script}
b, err := s.getFakeNextBlock(s.chain.BlockHeight() + 1)
ic, err := s.chain.GetTestVM(trigger.Application, tx, nil)
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("faile to prepare test VM: %w", err)
}
ic := s.chain.GetTestVM(trigger.Application, tx, b)
ic.VM.GasLimit = core.HeaderVerificationGasLimit
ic.VM.LoadScriptWithFlags(script, callflag.All)
err = ic.VM.Run()
@ -1832,7 +1831,7 @@ func (s *Server) invokeFunction(reqParams params.Params) (interface{}, *neorpc.E
// invokeFunctionHistoric implements the `invokeFunctionHistoric` RPC call.
func (s *Server) invokeFunctionHistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
b, respErr := s.getHistoricParams(reqParams)
nextH, respErr := s.getHistoricParams(reqParams)
if respErr != nil {
return nil, respErr
}
@ -1843,7 +1842,7 @@ func (s *Server) invokeFunctionHistoric(reqParams params.Params) (interface{}, *
if respErr != nil {
return nil, respErr
}
return s.runScriptInVM(trigger.Application, tx.Script, util.Uint160{}, tx, b, verbose)
return s.runScriptInVM(trigger.Application, tx.Script, util.Uint160{}, tx, &nextH, verbose)
}
func (s *Server) getInvokeFunctionParams(reqParams params.Params) (*transaction.Transaction, bool, *neorpc.Error) {
@ -1899,7 +1898,7 @@ func (s *Server) invokescript(reqParams params.Params) (interface{}, *neorpc.Err
// invokescripthistoric implements the `invokescripthistoric` RPC call.
func (s *Server) invokescripthistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
b, respErr := s.getHistoricParams(reqParams)
nextH, respErr := s.getHistoricParams(reqParams)
if respErr != nil {
return nil, respErr
}
@ -1910,7 +1909,7 @@ func (s *Server) invokescripthistoric(reqParams params.Params) (interface{}, *ne
if respErr != nil {
return nil, respErr
}
return s.runScriptInVM(trigger.Application, tx.Script, util.Uint160{}, tx, b, verbose)
return s.runScriptInVM(trigger.Application, tx.Script, util.Uint160{}, tx, &nextH, verbose)
}
func (s *Server) getInvokeScriptParams(reqParams params.Params) (*transaction.Transaction, bool, *neorpc.Error) {
@ -1953,7 +1952,7 @@ func (s *Server) invokeContractVerify(reqParams params.Params) (interface{}, *ne
// invokeContractVerifyHistoric implements the `invokecontractverifyhistoric` RPC call.
func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
b, respErr := s.getHistoricParams(reqParams)
nextH, respErr := s.getHistoricParams(reqParams)
if respErr != nil {
return nil, respErr
}
@ -1964,7 +1963,7 @@ func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (interfac
if respErr != nil {
return nil, respErr
}
return s.runScriptInVM(trigger.Verification, invocationScript, scriptHash, tx, b, false)
return s.runScriptInVM(trigger.Verification, invocationScript, scriptHash, tx, &nextH, false)
}
func (s *Server) getInvokeContractVerifyParams(reqParams params.Params) (util.Uint160, *transaction.Transaction, []byte, *neorpc.Error) {
@ -2007,64 +2006,45 @@ func (s *Server) getInvokeContractVerifyParams(reqParams params.Params) (util.Ui
// with the specified index to perform the historic call. It also checks that
// specified stateroot is stored at the specified height for further request
// handling consistency.
func (s *Server) getHistoricParams(reqParams params.Params) (*block.Block, *neorpc.Error) {
func (s *Server) getHistoricParams(reqParams params.Params) (uint32, *neorpc.Error) {
if s.chain.GetConfig().KeepOnlyLatestState {
return nil, neorpc.NewInvalidRequestError(fmt.Sprintf("only latest state is supported: %s", errKeepOnlyLatestState))
return 0, neorpc.NewInvalidRequestError(fmt.Sprintf("only latest state is supported: %s", errKeepOnlyLatestState))
}
if len(reqParams) < 1 {
return nil, neorpc.ErrInvalidParams
return 0, neorpc.ErrInvalidParams
}
height, respErr := s.blockHeightFromParam(reqParams.Value(0))
if respErr != nil {
hash, err := reqParams.Value(0).GetUint256()
if err != nil {
return nil, neorpc.NewInvalidParamsError(fmt.Sprintf("invalid block hash or index or stateroot hash: %s", err))
return 0, neorpc.NewInvalidParamsError(fmt.Sprintf("invalid block hash or index or stateroot hash: %s", err))
}
b, err := s.chain.GetBlock(hash)
if err != nil {
stateH, err := s.chain.GetStateModule().GetLatestStateHeight(hash)
if err != nil {
return nil, neorpc.NewInvalidParamsError(fmt.Sprintf("unknown block or stateroot: %s", err))
return 0, neorpc.NewInvalidParamsError(fmt.Sprintf("unknown block or stateroot: %s", err))
}
height = int(stateH)
} else {
height = int(b.Index)
}
}
b, err := s.getFakeNextBlock(uint32(height + 1))
if err != nil {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("can't create fake block for height %d: %s", height+1, err))
}
return b, nil
return uint32(height) + 1, nil
}
func (s *Server) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, error) {
// When transferring funds, script execution does no auto GAS claim,
// because it depends on persisting tx height.
// This is why we provide block here.
b := block.New(s.stateRootEnabled)
b.Index = nextBlockHeight
hdr, err := s.chain.GetHeader(s.chain.GetHeaderHash(int(nextBlockHeight - 1)))
if err != nil {
return nil, err
}
b.Timestamp = hdr.Timestamp + uint64(s.chain.GetConfig().SecondsPerBlock*int(time.Second/time.Millisecond))
return b, nil
}
func (s *Server) prepareInvocationContext(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, b *block.Block, verbose bool) (*interop.Context, *neorpc.Error) {
func (s *Server) prepareInvocationContext(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, nextH *uint32, verbose bool) (*interop.Context, *neorpc.Error) {
var (
err error
ic *interop.Context
)
if b == nil {
b, err = s.getFakeNextBlock(s.chain.BlockHeight() + 1)
if nextH == nil {
ic, err = s.chain.GetTestVM(t, tx, nil)
if err != nil {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("can't create fake block: %s", err))
return nil, neorpc.NewInternalServerError(fmt.Sprintf("failed to create test VM: %s", err))
}
ic = s.chain.GetTestVM(t, tx, b)
} else {
ic, err = s.chain.GetTestHistoricVM(t, tx, b)
ic, err = s.chain.GetTestHistoricVM(t, tx, *nextH)
if err != nil {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("failed to create historic VM: %s", err))
}
@ -2096,8 +2076,8 @@ func (s *Server) prepareInvocationContext(t trigger.Type, script []byte, contrac
// witness invocation script in case of `verification` trigger (it pushes `verify`
// arguments on stack before verification). In case of contract verification
// contractScriptHash should be specified.
func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, b *block.Block, verbose bool) (*result.Invoke, *neorpc.Error) {
ic, respErr := s.prepareInvocationContext(t, script, contractScriptHash, tx, b, verbose)
func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, nextH *uint32, verbose bool) (*result.Invoke, *neorpc.Error) {
ic, respErr := s.prepareInvocationContext(t, script, contractScriptHash, tx, nextH, verbose)
if respErr != nil {
return nil, respErr
}
@ -2111,16 +2091,12 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
var id uuid.UUID
if sess != nil {
// b == nil only when we're not using MPT-backed storage, therefore
// nextH == nil only when we're not using MPT-backed storage, therefore
// the second attempt won't stop here.
if s.config.SessionBackedByMPT && b == nil {
if s.config.SessionBackedByMPT && nextH == nil {
ic.Finalize()
b, err = s.getFakeNextBlock(ic.Block.Index)
if err != nil {
return nil, neorpc.NewInternalServerError(fmt.Sprintf("unable to prepare block for historic call: %s", err))
}
// Rerun with MPT-backed storage.
return s.runScriptInVM(t, script, contractScriptHash, tx, b, verbose)
return s.runScriptInVM(t, script, contractScriptHash, tx, &ic.Block.Index, verbose)
}
id = uuid.New()
sessionID := id.String()