mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-25 13:47:19 +00:00
core, rpc: move getFakeNextBlock to Blockchain
It's needed for VM CLI as far and may be improved later.
This commit is contained in:
parent
0036c89d63
commit
79e13f73d8
13 changed files with 136 additions and 89 deletions
|
@ -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)
|
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.
|
// 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{
|
vmcli := VMCLI{
|
||||||
chain: chain,
|
chain: chain,
|
||||||
|
@ -483,7 +486,10 @@ func handleSlots(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadNEF(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)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
|
@ -503,7 +509,10 @@ func handleLoadNEF(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadBase64(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)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -520,7 +529,10 @@ func handleLoadBase64(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadHex(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)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -537,7 +549,10 @@ func handleLoadHex(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLoadGo(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)
|
v := getVMFromContext(c.App)
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
|
@ -564,7 +579,10 @@ func handleLoadGo(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleReset(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)
|
changePrompt(c.App)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -577,11 +595,15 @@ func finalizeInteropContext(app *cli.App) {
|
||||||
|
|
||||||
// resetInteropContext calls finalizer for current interop context and replaces
|
// resetInteropContext calls finalizer for current interop context and replaces
|
||||||
// it with the newly created one.
|
// it with the newly created one.
|
||||||
func resetInteropContext(app *cli.App) {
|
func resetInteropContext(app *cli.App) error {
|
||||||
finalizeInteropContext(app)
|
finalizeInteropContext(app)
|
||||||
bc := getChainFromContext(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)
|
setInteropContextInContext(app, newIc)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resetManifest removes manifest from app context.
|
// 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
|
// resetState resets state of the app (clear interop context and manifest) so that it's ready
|
||||||
// to load new program.
|
// to load new program.
|
||||||
func resetState(app *cli.App) {
|
func resetState(app *cli.App) error {
|
||||||
resetInteropContext(app)
|
err := resetInteropContext(app)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to reset interop context state: %w", err)
|
||||||
|
}
|
||||||
resetManifest(app)
|
resetManifest(app)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
func getManifestFromFile(name string) (*manifest.Manifest, error) {
|
||||||
|
|
|
@ -298,7 +298,7 @@ func (chain *FakeChain) GetStorageItem(id int32, key []byte) state.StorageItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTestVM implements the Blockchainer interface.
|
// 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")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2241,19 +2241,28 @@ func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTestVM returns an interop context with VM set up for a test run.
|
// 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 := bc.newInteropContext(t, bc.dao, b, tx)
|
||||||
_ = systemInterop.SpawnVM() // All the other code suppose that the VM is ready.
|
_ = 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.
|
// 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 {
|
if bc.config.KeepOnlyLatestState {
|
||||||
return nil, errors.New("only latest state is supported")
|
return nil, errors.New("only latest state is supported")
|
||||||
}
|
}
|
||||||
if b == nil {
|
b, err := bc.getFakeNextBlock(nextBlockHeight)
|
||||||
return nil, errors.New("block is mandatory to produce test historic VM")
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create fake block for height %d: %w", nextBlockHeight, err)
|
||||||
}
|
}
|
||||||
var mode = mpt.ModeAll
|
var mode = mpt.ModeAll
|
||||||
if bc.config.RemoveUntraceableBlocks {
|
if bc.config.RemoveUntraceableBlocks {
|
||||||
|
@ -2284,6 +2293,18 @@ func (bc *Blockchain) GetTestHistoricVM(t trigger.Type, tx *transaction.Transact
|
||||||
return systemInterop, nil
|
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.
|
// Various witness verification errors.
|
||||||
var (
|
var (
|
||||||
ErrWitnessHashMismatch = errors.New("witness hash mismatch")
|
ErrWitnessHashMismatch = errors.New("witness hash mismatch")
|
||||||
|
|
|
@ -32,7 +32,8 @@ var pathToInternalContracts = filepath.Join("..", "..", "..", "..", "internal",
|
||||||
|
|
||||||
func TestGetCallFlags(t *testing.T) {
|
func TestGetCallFlags(t *testing.T) {
|
||||||
bc, _ := chain.NewSingle(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)
|
ic.VM.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, callflag.All)
|
||||||
require.NoError(t, contract.GetCallFlags(ic))
|
require.NoError(t, contract.GetCallFlags(ic))
|
||||||
|
@ -41,7 +42,8 @@ func TestGetCallFlags(t *testing.T) {
|
||||||
|
|
||||||
func TestCall(t *testing.T) {
|
func TestCall(t *testing.T) {
|
||||||
bc, _ := chain.NewSingle(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
|
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))
|
require.NoError(t, native.PutContractState(ic.DAO, cs))
|
||||||
|
|
|
@ -65,7 +65,8 @@ func getSharpTestGenesis(t *testing.T) *block.Block {
|
||||||
|
|
||||||
func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
|
func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
|
||||||
chain, _ := chain.NewSingle(t)
|
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()
|
v := ic.SpawnVM()
|
||||||
return v, ic, chain
|
return v, ic, chain
|
||||||
}
|
}
|
||||||
|
@ -523,7 +524,8 @@ func TestGetRandomCompatibility(t *testing.T) {
|
||||||
|
|
||||||
b := getSharpTestGenesis(t)
|
b := getSharpTestGenesis(t)
|
||||||
tx := getSharpTestTx(util.Uint160{})
|
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.Network = 860833102 // Old mainnet magic used by C# tests.
|
||||||
|
|
||||||
ic.VM = vm.New()
|
ic.VM = vm.New()
|
||||||
|
@ -550,7 +552,8 @@ func TestNotify(t *testing.T) {
|
||||||
caller := random.Uint160()
|
caller := random.Uint160()
|
||||||
newIC := func(name string, args interface{}) *interop.Context {
|
newIC := func(name string, args interface{}) *interop.Context {
|
||||||
_, _, bc, cs := getDeployedInternal(t)
|
_, _, 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.LoadNEFMethod(&cs.NEF, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
|
||||||
ic.VM.Estack().PushVal(args)
|
ic.VM.Estack().PushVal(args)
|
||||||
ic.VM.Estack().PushVal(name)
|
ic.VM.Estack().PushVal(name)
|
||||||
|
|
|
@ -300,7 +300,8 @@ func TestFind(t *testing.T) {
|
||||||
|
|
||||||
func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
|
func createVM(t testing.TB) (*vm.VM, *interop.Context, *core.Blockchain) {
|
||||||
chain, _ := chain.NewSingle(t)
|
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()
|
v := ic.SpawnVM()
|
||||||
return v, ic, chain
|
return v, ic, chain
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
require.NotNil(t, md)
|
require.NotNil(t, md)
|
||||||
|
|
||||||
t.Run("fail, bad current script hash", func(t *testing.T) {
|
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()
|
v := ic.SpawnVM()
|
||||||
fakeH := util.Uint160{1, 2, 3}
|
fakeH := util.Uint160{1, 2, 3}
|
||||||
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
|
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
|
||||||
|
@ -83,7 +84,7 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
v.Context().Jump(md.Offset)
|
v.Context().Jump(md.Offset)
|
||||||
|
|
||||||
// Bad current script hash
|
// Bad current script hash
|
||||||
err := v.Run()
|
err = v.Run()
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error())
|
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)
|
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 := ic.SpawnVM()
|
||||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||||
input := []byte{1, 2, 3, 4}
|
input := []byte{1, 2, 3, 4}
|
||||||
|
@ -112,13 +114,14 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
v.Context().Jump(md.Offset)
|
v.Context().Jump(md.Offset)
|
||||||
|
|
||||||
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
||||||
err := v.Run()
|
err = v.Run()
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
|
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
|
||||||
|
|
||||||
// Add new block => CryptoLib should be active now.
|
// Add new block => CryptoLib should be active now.
|
||||||
eBad.AddNewBlock(t)
|
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 = ic.SpawnVM()
|
||||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||||
v.Estack().PushVal(input)
|
v.Estack().PushVal(input)
|
||||||
|
@ -130,7 +133,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("success", func(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 := ic.SpawnVM()
|
||||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
|
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
|
||||||
input := []byte{1, 2, 3, 4}
|
input := []byte{1, 2, 3, 4}
|
||||||
|
|
|
@ -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.
|
// `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.
|
// This is unwanted behavior, so we explicitly copy the transaction to perform execution.
|
||||||
ttx := *tx
|
ttx := *tx
|
||||||
ic := bc.GetTestVM(trigger.Application, &ttx, b)
|
ic, _ := bc.GetTestVM(trigger.Application, &ttx, b)
|
||||||
|
|
||||||
defer ic.Finalize()
|
defer ic.Finalize()
|
||||||
|
|
||||||
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
||||||
|
|
|
@ -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) {
|
func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...interface{}) (*vm.Stack, error) {
|
||||||
tx := c.PrepareInvokeNoSign(t, method, args...)
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
||||||
b := c.NewUnsignedBlock(t, tx)
|
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)
|
t.Cleanup(ic.Finalize)
|
||||||
|
|
||||||
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
||||||
err := ic.VM.Run()
|
err = ic.VM.Run()
|
||||||
return ic.VM.Estack(), err
|
return ic.VM.Estack(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ type (
|
||||||
GetBaseExecFee() int64
|
GetBaseExecFee() int64
|
||||||
GetConfig() config.ProtocolConfiguration
|
GetConfig() config.ProtocolConfiguration
|
||||||
GetMaxVerificationGAS() int64
|
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)
|
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package oracle
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
gio "io"
|
gio "io"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"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)
|
size := io.GetVarSize(tx)
|
||||||
tx.Scripts = append(tx.Scripts, transaction.Witness{VerificationScript: oracleSignContract})
|
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 {
|
if !ok {
|
||||||
return nil, errors.New("can't verify transaction")
|
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
|
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
|
// (*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.
|
// 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.
|
// So, make a copy of the tx to avoid wrong hash caching.
|
||||||
cp := *tx
|
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.GasLimit = o.Chain.GetMaxVerificationGAS()
|
||||||
ic.VM.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
|
ic.VM.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
|
||||||
ic.VM.Context().Jump(o.verifyOffset)
|
ic.VM.Context().Jump(o.verifyOffset)
|
||||||
|
|
||||||
ok := isVerifyOk(ic)
|
ok := isVerifyOk(ic)
|
||||||
return ic.VM.GasConsumed(), ok
|
return ic.VM.GasConsumed(), ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isVerifyOk(ic *interop.Context) bool {
|
func isVerifyOk(ic *interop.Context) bool {
|
||||||
|
|
|
@ -1181,7 +1181,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, acc.SignTx(testchain.Network(), tx))
|
require.NoError(t, acc.SignTx(testchain.Network(), tx))
|
||||||
require.NoError(t, chain.VerifyTx(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)
|
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
|
||||||
require.NoError(t, ic.VM.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
})
|
})
|
||||||
|
@ -1195,7 +1196,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, chain.VerifyTx(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)
|
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
|
||||||
require.NoError(t, ic.VM.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
require.Equal(t, 2, len(ic.Notifications))
|
require.Equal(t, 2, len(ic.Notifications))
|
||||||
|
@ -1228,7 +1230,8 @@ func TestCreateNEP17TransferTx(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, acc.SignTx(testchain.Network(), tx))
|
require.NoError(t, acc.SignTx(testchain.Network(), tx))
|
||||||
require.NoError(t, chain.VerifyTx(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)
|
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
|
||||||
require.NoError(t, ic.VM.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
})
|
})
|
||||||
|
|
|
@ -89,8 +89,8 @@ type (
|
||||||
GetNotaryServiceFeePerKey() int64
|
GetNotaryServiceFeePerKey() int64
|
||||||
GetStateModule() core.StateRoot
|
GetStateModule() core.StateRoot
|
||||||
GetStorageItem(id int32, key []byte) state.StorageItem
|
GetStorageItem(id int32, key []byte) state.StorageItem
|
||||||
GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error)
|
GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, nextBlockHeight uint32) (*interop.Context, error)
|
||||||
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)
|
||||||
GetTokenLastUpdated(acc util.Uint160) (map[int32]uint32, error)
|
GetTokenLastUpdated(acc util.Uint160) (map[int32]uint32, error)
|
||||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
GetValidators() ([]*keys.PublicKey, error)
|
GetValidators() ([]*keys.PublicKey, error)
|
||||||
|
@ -1065,11 +1065,10 @@ func (s *Server) invokeReadOnlyMulti(bw *io.BufBinWriter, h util.Uint160, method
|
||||||
}
|
}
|
||||||
script := bw.Bytes()
|
script := bw.Bytes()
|
||||||
tx := &transaction.Transaction{Script: script}
|
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 {
|
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.GasLimit = core.HeaderVerificationGasLimit
|
||||||
ic.VM.LoadScriptWithFlags(script, callflag.All)
|
ic.VM.LoadScriptWithFlags(script, callflag.All)
|
||||||
err = ic.VM.Run()
|
err = ic.VM.Run()
|
||||||
|
@ -1832,7 +1831,7 @@ func (s *Server) invokeFunction(reqParams params.Params) (interface{}, *neorpc.E
|
||||||
|
|
||||||
// invokeFunctionHistoric implements the `invokeFunctionHistoric` RPC call.
|
// invokeFunctionHistoric implements the `invokeFunctionHistoric` RPC call.
|
||||||
func (s *Server) invokeFunctionHistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
|
func (s *Server) invokeFunctionHistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
|
||||||
b, respErr := s.getHistoricParams(reqParams)
|
nextH, respErr := s.getHistoricParams(reqParams)
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
return nil, respErr
|
||||||
}
|
}
|
||||||
|
@ -1843,7 +1842,7 @@ func (s *Server) invokeFunctionHistoric(reqParams params.Params) (interface{}, *
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
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) {
|
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.
|
// invokescripthistoric implements the `invokescripthistoric` RPC call.
|
||||||
func (s *Server) invokescripthistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
|
func (s *Server) invokescripthistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
|
||||||
b, respErr := s.getHistoricParams(reqParams)
|
nextH, respErr := s.getHistoricParams(reqParams)
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
return nil, respErr
|
||||||
}
|
}
|
||||||
|
@ -1910,7 +1909,7 @@ func (s *Server) invokescripthistoric(reqParams params.Params) (interface{}, *ne
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
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) {
|
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.
|
// invokeContractVerifyHistoric implements the `invokecontractverifyhistoric` RPC call.
|
||||||
func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
|
func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (interface{}, *neorpc.Error) {
|
||||||
b, respErr := s.getHistoricParams(reqParams)
|
nextH, respErr := s.getHistoricParams(reqParams)
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
return nil, respErr
|
||||||
}
|
}
|
||||||
|
@ -1964,7 +1963,7 @@ func (s *Server) invokeContractVerifyHistoric(reqParams params.Params) (interfac
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
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) {
|
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
|
// with the specified index to perform the historic call. It also checks that
|
||||||
// specified stateroot is stored at the specified height for further request
|
// specified stateroot is stored at the specified height for further request
|
||||||
// handling consistency.
|
// 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 {
|
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 {
|
if len(reqParams) < 1 {
|
||||||
return nil, neorpc.ErrInvalidParams
|
return 0, neorpc.ErrInvalidParams
|
||||||
}
|
}
|
||||||
height, respErr := s.blockHeightFromParam(reqParams.Value(0))
|
height, respErr := s.blockHeightFromParam(reqParams.Value(0))
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
hash, err := reqParams.Value(0).GetUint256()
|
hash, err := reqParams.Value(0).GetUint256()
|
||||||
if err != nil {
|
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)
|
b, err := s.chain.GetBlock(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stateH, err := s.chain.GetStateModule().GetLatestStateHeight(hash)
|
stateH, err := s.chain.GetStateModule().GetLatestStateHeight(hash)
|
||||||
if err != nil {
|
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)
|
height = int(stateH)
|
||||||
} else {
|
} else {
|
||||||
height = int(b.Index)
|
height = int(b.Index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b, err := s.getFakeNextBlock(uint32(height + 1))
|
return uint32(height) + 1, nil
|
||||||
if err != nil {
|
|
||||||
return nil, neorpc.NewInternalServerError(fmt.Sprintf("can't create fake block for height %d: %s", height+1, err))
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getFakeNextBlock(nextBlockHeight uint32) (*block.Block, error) {
|
func (s *Server) prepareInvocationContext(t trigger.Type, script []byte, contractScriptHash util.Uint160, tx *transaction.Transaction, nextH *uint32, verbose bool) (*interop.Context, *neorpc.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) {
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
ic *interop.Context
|
ic *interop.Context
|
||||||
)
|
)
|
||||||
if b == nil {
|
if nextH == nil {
|
||||||
b, err = s.getFakeNextBlock(s.chain.BlockHeight() + 1)
|
ic, err = s.chain.GetTestVM(t, tx, nil)
|
||||||
if err != 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 {
|
} else {
|
||||||
ic, err = s.chain.GetTestHistoricVM(t, tx, b)
|
ic, err = s.chain.GetTestHistoricVM(t, tx, *nextH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, neorpc.NewInternalServerError(fmt.Sprintf("failed to create historic VM: %s", err))
|
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`
|
// witness invocation script in case of `verification` trigger (it pushes `verify`
|
||||||
// arguments on stack before verification). In case of contract verification
|
// arguments on stack before verification). In case of contract verification
|
||||||
// contractScriptHash should be specified.
|
// 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) {
|
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, b, verbose)
|
ic, respErr := s.prepareInvocationContext(t, script, contractScriptHash, tx, nextH, verbose)
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return nil, respErr
|
return nil, respErr
|
||||||
}
|
}
|
||||||
|
@ -2111,16 +2091,12 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
|
||||||
var id uuid.UUID
|
var id uuid.UUID
|
||||||
|
|
||||||
if sess != nil {
|
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.
|
// the second attempt won't stop here.
|
||||||
if s.config.SessionBackedByMPT && b == nil {
|
if s.config.SessionBackedByMPT && nextH == nil {
|
||||||
ic.Finalize()
|
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.
|
// 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()
|
id = uuid.New()
|
||||||
sessionID := id.String()
|
sessionID := id.String()
|
||||||
|
|
Loading…
Reference in a new issue