Merge pull request #2816 from nspcc-dev/fix-pointer-serialization
Fix pointer serialization
This commit is contained in:
commit
0039615ae3
4 changed files with 86 additions and 22 deletions
|
@ -60,15 +60,22 @@ const (
|
|||
const (
|
||||
verboseFlagFullName = "verbose"
|
||||
historicFlagFullName = "historic"
|
||||
gasFlagFullName = "gas"
|
||||
backwardsFlagFullName = "backwards"
|
||||
diffFlagFullName = "diff"
|
||||
)
|
||||
|
||||
var historicFlag = cli.IntFlag{
|
||||
Name: historicFlagFullName,
|
||||
Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " +
|
||||
"Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.",
|
||||
}
|
||||
var (
|
||||
historicFlag = cli.IntFlag{
|
||||
Name: historicFlagFullName,
|
||||
Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " +
|
||||
"Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.",
|
||||
}
|
||||
gasFlag = cli.Int64Flag{
|
||||
Name: gasFlagFullName,
|
||||
Usage: "GAS limit for this execution (integer number, satoshi).",
|
||||
}
|
||||
)
|
||||
|
||||
var commands = []cli.Command{
|
||||
{
|
||||
|
@ -143,8 +150,8 @@ Example:
|
|||
{
|
||||
Name: "loadnef",
|
||||
Usage: "Load a NEF-consistent script into the VM optionally attaching to it provided signers with scopes",
|
||||
UsageText: `loadnef [--historic <height>] <file> <manifest> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag},
|
||||
UsageText: `loadnef [--historic <height>] [--gas <int>] <file> <manifest> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag, gasFlag},
|
||||
Description: `<file> and <manifest> parameters are mandatory.
|
||||
|
||||
` + cmdargs.SignersParsingDoc + `
|
||||
|
@ -156,8 +163,8 @@ Example:
|
|||
{
|
||||
Name: "loadbase64",
|
||||
Usage: "Load a base64-encoded script string into the VM optionally attaching to it provided signers with scopes",
|
||||
UsageText: `loadbase64 [--historic <height>] <string> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag},
|
||||
UsageText: `loadbase64 [--historic <height>] [--gas <int>] <string> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag, gasFlag},
|
||||
Description: `<string> is mandatory parameter.
|
||||
|
||||
` + cmdargs.SignersParsingDoc + `
|
||||
|
@ -169,8 +176,8 @@ Example:
|
|||
{
|
||||
Name: "loadhex",
|
||||
Usage: "Load a hex-encoded script string into the VM optionally attaching to it provided signers with scopes",
|
||||
UsageText: `loadhex [--historic <height>] <string> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag},
|
||||
UsageText: `loadhex [--historic <height>] [--gas <int>] <string> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag, gasFlag},
|
||||
Description: `<string> is mandatory parameter.
|
||||
|
||||
` + cmdargs.SignersParsingDoc + `
|
||||
|
@ -182,8 +189,8 @@ Example:
|
|||
{
|
||||
Name: "loadgo",
|
||||
Usage: "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes",
|
||||
UsageText: `loadgo [--historic <height>] <file> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag},
|
||||
UsageText: `loadgo [--historic <height>] [--gas <int>] <file> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag, gasFlag},
|
||||
Description: `<file> is mandatory parameter.
|
||||
|
||||
` + cmdargs.SignersParsingDoc + `
|
||||
|
@ -195,10 +202,13 @@ Example:
|
|||
{
|
||||
Name: "loadtx",
|
||||
Usage: "Load transaction into the VM from chain or from parameter context file",
|
||||
UsageText: `loadtx [--historic <height>] <file-or-hash>`,
|
||||
Flags: []cli.Flag{historicFlag},
|
||||
UsageText: `loadtx [--historic <height>] [--gas <int>] <file-or-hash>`,
|
||||
Flags: []cli.Flag{historicFlag, gasFlag},
|
||||
Description: `Load transaction into the VM from chain or from parameter context file.
|
||||
The transaction script will be loaded into VM; the resulting execution context will use the provided transaction as script container including its signers, hash and nonce.
|
||||
The transaction script will be loaded into VM; the resulting execution context
|
||||
will use the provided transaction as script container including its signers,
|
||||
hash and nonce. It'll also use transaction's system fee value as GAS limit if
|
||||
--gas option is not used.
|
||||
|
||||
<file-or-hash> is mandatory parameter.
|
||||
|
||||
|
@ -209,8 +219,8 @@ Example:
|
|||
{
|
||||
Name: "loaddeployed",
|
||||
Usage: "Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes",
|
||||
UsageText: `loaddeployed [--historic <height>] <hash-or-address-or-id> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag},
|
||||
UsageText: `loaddeployed [--historic <height>] [--gas <int>] <hash-or-address-or-id> [<signer-with-scope>, ...]`,
|
||||
Flags: []cli.Flag{historicFlag, gasFlag},
|
||||
Description: `Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes.
|
||||
If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded.
|
||||
|
||||
|
@ -640,12 +650,22 @@ func handleSlots(c *cli.Context) error {
|
|||
// prepareVM retrieves --historic flag from context (if set) and resets app state
|
||||
// (to the specified historic height if given).
|
||||
func prepareVM(c *cli.Context, tx *transaction.Transaction) error {
|
||||
var err error
|
||||
if c.IsSet(historicFlagFullName) {
|
||||
height := c.Int(historicFlagFullName)
|
||||
return resetState(c.App, tx, uint32(height))
|
||||
err = resetState(c.App, tx, uint32(height))
|
||||
} else {
|
||||
err = resetState(c.App, tx)
|
||||
}
|
||||
|
||||
return resetState(c.App, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.IsSet(gasFlagFullName) {
|
||||
gas := c.Int64(gasFlagFullName)
|
||||
v := getVMFromContext(c.App)
|
||||
v.GasLimit = gas
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleLoadNEF(c *cli.Context) error {
|
||||
|
@ -812,6 +832,9 @@ func handleLoadTx(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
v := getVMFromContext(c.App)
|
||||
if v.GasLimit == -1 {
|
||||
v.GasLimit = tx.SystemFee
|
||||
}
|
||||
fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr())
|
||||
changePrompt(c.App)
|
||||
return nil
|
||||
|
@ -856,7 +879,9 @@ func handleLoadDeployed(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
ic = getInteropContextFromContext(c.App) // fetch newly-created IC.
|
||||
ic.ReuseVM(ic.VM) // clear previously loaded program and context.
|
||||
gasLimit := ic.VM.GasLimit
|
||||
ic.ReuseVM(ic.VM) // clear previously loaded program and context.
|
||||
ic.VM.GasLimit = gasLimit
|
||||
ic.VM.LoadScriptWithHash(cs.NEF.Script, cs.Hash, callflag.All)
|
||||
fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", ic.VM.Context().LenInstr())
|
||||
setManifestInContext(c.App, &cs.Manifest)
|
||||
|
|
|
@ -1118,6 +1118,8 @@ func TestLoadtx(t *testing.T) {
|
|||
e.runProg(t,
|
||||
"loadtx "+tx.Hash().StringLE(), // hash LE
|
||||
"run",
|
||||
"loadtx --gas 10000 "+tx.Hash().StringLE(), // with GAS
|
||||
"run",
|
||||
"loadtx 0x"+tx.Hash().StringLE(), // hash LE with 0x prefix
|
||||
"run",
|
||||
"loadtx '"+tmp+"'", // Tx from parameter context file.
|
||||
|
@ -1128,6 +1130,8 @@ func TestLoadtx(t *testing.T) {
|
|||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, 1)
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkError(t, errors.New("at instruction 3 (PACK): gas limit is exceeded"))
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, 1)
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, 1)
|
||||
|
@ -1147,6 +1151,8 @@ func TestLoaddeployed(t *testing.T) {
|
|||
e.runProg(t,
|
||||
"loaddeployed "+h.StringLE(), // hash LE
|
||||
"run get 1",
|
||||
"loaddeployed --gas 420000 "+h.StringLE(), // gas-limited
|
||||
"run get 1",
|
||||
"loaddeployed 0x"+h.StringLE(), // hash LE with 0x prefix
|
||||
"run get 1",
|
||||
"loaddeployed 1", // contract ID
|
||||
|
@ -1172,6 +1178,8 @@ func TestLoaddeployed(t *testing.T) {
|
|||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, []byte{2})
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkError(t, errors.New("at instruction 63 (SYSCALL): failed to invoke syscall 837311890: insufficient amount of gas"))
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, []byte{2})
|
||||
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||
e.checkStack(t, []byte{2})
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// MaxDeserialized is the maximum number one deserialized item can contain
|
||||
|
@ -168,6 +169,13 @@ func (w *SerializationContext) serialize(item Item) error {
|
|||
} else {
|
||||
return fmt.Errorf("%w: Interop", ErrUnserializable)
|
||||
}
|
||||
case *Pointer:
|
||||
if w.allowInvalid {
|
||||
w.data = append(w.data, byte(PointerT))
|
||||
w.appendVarUint(uint64(t.pos))
|
||||
} else {
|
||||
return fmt.Errorf("%w: Pointer", ErrUnserializable)
|
||||
}
|
||||
case *Array:
|
||||
w.data = append(w.data, byte(ArrayT))
|
||||
if err := w.writeArray(item, t.value, start); err != nil {
|
||||
|
@ -311,6 +319,12 @@ func (r *deserContext) decodeBinary() Item {
|
|||
return NewInterop(nil)
|
||||
}
|
||||
fallthrough
|
||||
case PointerT:
|
||||
if r.allowInvalid {
|
||||
pos := int(r.ReadVarUint())
|
||||
return NewPointerWithHash(pos, nil, util.Uint160{})
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
if t == InvalidT && r.allowInvalid {
|
||||
return nil
|
||||
|
|
|
@ -76,6 +76,7 @@ func TestSerialize(t *testing.T) {
|
|||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
testSerialize(t, ErrUnserializable, NewInterop(42))
|
||||
testSerialize(t, ErrUnserializable, NewPointer(42, []byte{}))
|
||||
testSerialize(t, ErrUnserializable, nil)
|
||||
|
||||
t.Run("protected interop", func(t *testing.T) {
|
||||
|
@ -93,6 +94,22 @@ func TestSerialize(t *testing.T) {
|
|||
require.NoError(t, r.Err)
|
||||
require.IsType(t, (*Interop)(nil), item)
|
||||
})
|
||||
t.Run("protected pointer", func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
EncodeBinaryProtected(NewPointer(42, []byte{}), w.BinWriter)
|
||||
require.NoError(t, w.Err)
|
||||
|
||||
data := w.Bytes()
|
||||
r := io.NewBinReaderFromBuf(data)
|
||||
DecodeBinary(r)
|
||||
require.Error(t, r.Err)
|
||||
|
||||
r = io.NewBinReaderFromBuf(data)
|
||||
item := DecodeBinaryProtected(r)
|
||||
require.NoError(t, r.Err)
|
||||
require.IsType(t, (*Pointer)(nil), item)
|
||||
require.Equal(t, 42, item.Value())
|
||||
})
|
||||
t.Run("protected nil", func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
EncodeBinaryProtected(nil, w.BinWriter)
|
||||
|
|
Loading…
Reference in a new issue