forked from TrueCloudLab/neoneo-go
Merge pull request #787 from nspcc-dev/feature/appexec
core/state: marshal AppExecResult.Stack as an array
This commit is contained in:
commit
95f19f93e2
7 changed files with 132 additions and 23 deletions
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue