Merge pull request #787 from nspcc-dev/feature/appexec

core/state: marshal AppExecResult.Stack as an array
This commit is contained in:
Roman Khimov 2020-03-23 17:07:53 +03:00 committed by GitHub
commit 95f19f93e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 132 additions and 23 deletions

View file

@ -29,7 +29,7 @@ import (
// Tuning parameters. // Tuning parameters.
const ( const (
headerBatchCount = 2000 headerBatchCount = 2000
version = "0.0.8" version = "0.0.9"
// This one comes from C# code and it's different from the constant used // This one comes from C# code and it's different from the constant used
// when creating an asset with Neo.Asset.Create interop call. It looks // when creating an asset with Neo.Asset.Create interop call. It looks
@ -708,7 +708,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
Trigger: trigger.Application, Trigger: trigger.Application,
VMState: v.State(), VMState: v.State(),
GasConsumed: v.GasConsumed(), GasConsumed: v.GasConsumed(),
Stack: v.Stack("estack"), Stack: v.Estack().ToContractParameters(),
Events: systemInterop.notifications, Events: systemInterop.notifications,
} }
err = cache.PutAppExecResult(aer) err = cache.PutAppExecResult(aer)

View file

@ -171,7 +171,11 @@ func TestGetValidators(t *testing.T) {
func TestPutGetAppExecResult(t *testing.T) { func TestPutGetAppExecResult(t *testing.T) {
dao := newDao(storage.NewMemoryStore()) dao := newDao(storage.NewMemoryStore())
hash := random.Uint256() hash := random.Uint256()
appExecResult := &state.AppExecResult{TxHash: hash, Events: []state.NotificationEvent{}} appExecResult := &state.AppExecResult{
TxHash: hash,
Events: []state.NotificationEvent{},
Stack: []smartcontract.Parameter{},
}
err := dao.PutAppExecResult(appExecResult) err := dao.PutAppExecResult(appExecResult)
require.NoError(t, err) require.NoError(t, err)
gotAppExecResult, err := dao.GetAppExecResult(hash) gotAppExecResult, err := dao.GetAppExecResult(hash)

View file

@ -2,6 +2,7 @@ package state
import ( import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -21,7 +22,7 @@ type AppExecResult struct {
Trigger trigger.Type Trigger trigger.Type
VMState string VMState string
GasConsumed util.Fixed8 GasConsumed util.Fixed8
Stack string // JSON Stack []smartcontract.Parameter
Events []NotificationEvent Events []NotificationEvent
} }
@ -43,7 +44,7 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
w.WriteB(byte(aer.Trigger)) w.WriteB(byte(aer.Trigger))
w.WriteString(aer.VMState) w.WriteString(aer.VMState)
aer.GasConsumed.EncodeBinary(w) aer.GasConsumed.EncodeBinary(w)
w.WriteString(aer.Stack) w.WriteArray(aer.Stack)
w.WriteArray(aer.Events) w.WriteArray(aer.Events)
} }
@ -53,6 +54,6 @@ func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
aer.Trigger = trigger.Type(r.ReadB()) aer.Trigger = trigger.Type(r.ReadB())
aer.VMState = r.ReadString() aer.VMState = r.ReadString()
aer.GasConsumed.DecodeBinary(r) aer.GasConsumed.DecodeBinary(r)
aer.Stack = r.ReadString() r.ReadArray(&aer.Stack)
r.ReadArray(&aer.Events) r.ReadArray(&aer.Events)
} }

View file

@ -5,6 +5,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -30,7 +31,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
Trigger: 1, Trigger: 1,
VMState: "Hault", VMState: "Hault",
GasConsumed: 10, GasConsumed: 10,
Stack: "", Stack: []smartcontract.Parameter{},
Events: []NotificationEvent{}, Events: []NotificationEvent{},
} }
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()

View file

@ -1,8 +1,6 @@
package result package result
import ( import (
"encoding/json"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -18,12 +16,12 @@ type ApplicationLog struct {
// Execution response wrapper // Execution response wrapper
type Execution struct { type Execution struct {
Trigger string `json:"trigger"` Trigger string `json:"trigger"`
ScriptHash util.Uint160 `json:"contract"` ScriptHash util.Uint160 `json:"contract"`
VMState string `json:"vmstate"` VMState string `json:"vmstate"`
GasConsumed util.Fixed8 `json:"gas_consumed"` GasConsumed util.Fixed8 `json:"gas_consumed"`
Stack json.RawMessage `json:"stack"` Stack []smartcontract.Parameter `json:"stack"`
Events []NotificationEvent `json:"notifications"` Events []NotificationEvent `json:"notifications"`
} }
//NotificationEvent response wrapper //NotificationEvent response wrapper
@ -46,18 +44,12 @@ func NewApplicationLog(appExecRes *state.AppExecResult, scriptHash util.Uint160)
triggerString := appExecRes.Trigger.String() triggerString := appExecRes.Trigger.String()
var rawStack json.RawMessage
if len(appExecRes.Stack) != 0 {
rawStack = json.RawMessage(appExecRes.Stack)
} else {
rawStack = json.RawMessage("[]")
}
executions := []Execution{{ executions := []Execution{{
Trigger: triggerString, Trigger: triggerString,
ScriptHash: scriptHash, ScriptHash: scriptHash,
VMState: appExecRes.VMState, VMState: appExecRes.VMState,
GasConsumed: appExecRes.GasConsumed, GasConsumed: appExecRes.GasConsumed,
Stack: rawStack, Stack: appExecRes.Stack,
Events: events, Events: events,
}} }}

View file

@ -4,11 +4,14 @@ import (
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"math"
"math/bits" "math/bits"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -210,6 +213,87 @@ func (p *Parameter) UnmarshalJSON(data []byte) (err error) {
return return
} }
// EncodeBinary implements io.Serializable interface.
func (p *Parameter) EncodeBinary(w *io.BinWriter) {
w.WriteB(byte(p.Type))
switch p.Type {
case BoolType:
w.WriteBool(p.Value.(bool))
case ByteArrayType, PublicKeyType, SignatureType:
if p.Value == nil {
w.WriteVarUint(math.MaxUint64)
} else {
w.WriteVarBytes(p.Value.([]byte))
}
case StringType:
w.WriteString(p.Value.(string))
case IntegerType:
w.WriteU64LE(uint64(p.Value.(int64)))
case ArrayType:
w.WriteArray(p.Value.([]Parameter))
case MapType:
m := p.Value.(map[Parameter]Parameter)
w.WriteVarUint(uint64(len(m)))
for k := range m {
v := m[k]
k.EncodeBinary(w)
v.EncodeBinary(w)
}
case Hash160Type:
w.WriteBytes(p.Value.(util.Uint160).BytesBE())
case Hash256Type:
w.WriteBytes(p.Value.(util.Uint256).BytesBE())
case InteropInterfaceType:
default:
w.Err = fmt.Errorf("unknown type: %x", p.Type)
}
}
// DecodeBinary implements io.Serializable interface.
func (p *Parameter) DecodeBinary(r *io.BinReader) {
p.Type = ParamType(r.ReadB())
switch p.Type {
case BoolType:
p.Value = r.ReadBool()
case ByteArrayType, PublicKeyType, SignatureType:
ln := r.ReadVarUint()
if ln != math.MaxUint64 {
b := make([]byte, ln)
r.ReadBytes(b)
p.Value = b
}
case StringType:
p.Value = r.ReadString()
case IntegerType:
p.Value = int64(r.ReadU64LE())
case ArrayType:
ps := []Parameter{}
r.ReadArray(&ps)
p.Value = ps
case MapType:
ln := r.ReadVarUint()
m := make(map[Parameter]Parameter, ln)
for i := uint64(0); i < ln; i++ {
var k, v Parameter
k.DecodeBinary(r)
v.DecodeBinary(r)
m[k] = v
}
p.Value = m
case Hash160Type:
var u util.Uint160
r.ReadBytes(u[:])
p.Value = u
case Hash256Type:
var u util.Uint256
r.ReadBytes(u[:])
p.Value = u
case InteropInterfaceType:
default:
r.Err = fmt.Errorf("unknown type: %x", p.Type)
}
}
// Params is an array of Parameter (TODO: drop it?). // Params is an array of Parameter (TODO: drop it?).
type Params []Parameter type Params []Parameter

View file

@ -6,8 +6,10 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var marshalJSONTestCases = []struct { var marshalJSONTestCases = []struct {
@ -71,7 +73,7 @@ var marshalJSONTestCases = []struct {
input: Parameter{ input: Parameter{
Type: MapType, Type: MapType,
Value: map[Parameter]Parameter{ Value: map[Parameter]Parameter{
{Type: StringType, Value: "key1"}: {Type: IntegerType, Value: 1}, {Type: StringType, Value: "key1"}: {Type: IntegerType, Value: int64(1)},
{Type: StringType, Value: "key2"}: {Type: StringType, Value: "two"}, {Type: StringType, Value: "key2"}: {Type: StringType, Value: "two"},
}, },
}, },
@ -441,3 +443,28 @@ func TestNewParameterFromString(t *testing.T) {
} }
} }
} }
func TestEncodeDecodeBinary(t *testing.T) {
for _, tc := range marshalJSONTestCases {
w := io.NewBufBinWriter()
tc.input.EncodeBinary(w.BinWriter)
require.NoError(t, w.Err)
r := io.NewBinReaderFromBuf(w.Bytes())
var p Parameter
p.DecodeBinary(r)
require.NoError(t, r.Err)
require.Equal(t, tc.input, p)
}
t.Run("unknown", func(t *testing.T) {
p := Parameter{Type: UnknownType}
w := io.NewBufBinWriter()
p.EncodeBinary(w.BinWriter)
require.Error(t, w.Err)
r := io.NewBinReaderFromBuf([]byte{0xAA})
p.DecodeBinary(r)
require.Error(t, r.Err)
})
}