mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-11 15:30:07 +00:00
Merge pull request #2586 from nspcc-dev/move-res-rpc-code
Move response-related RPC code
This commit is contained in:
commit
953f291836
58 changed files with 652 additions and 695 deletions
|
@ -23,8 +23,8 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"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/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
@ -354,7 +354,7 @@ func TestContractDeployWithData(t *testing.T) {
|
||||||
|
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
require.Equal(t, []byte{12}, res.Stack[0].Value())
|
require.Equal(t, []byte{12}, res.Stack[0].Value())
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ func TestContractDeployWithData(t *testing.T) {
|
||||||
|
|
||||||
res = new(result.Invoke)
|
res = new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
|
require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
|
||||||
}
|
}
|
||||||
|
@ -672,7 +672,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
|
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
|
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
e.Run(t, append(cmd, strconv.FormatInt(storage.FindKeysOnly, 10))...)
|
e.Run(t, append(cmd, strconv.FormatInt(storage.FindKeysOnly, 10))...)
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State)
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
require.Equal(t, []stackitem.Item{
|
require.Equal(t, []stackitem.Item{
|
||||||
stackitem.Make("findkey1"),
|
stackitem.Make("findkey1"),
|
||||||
|
@ -832,7 +832,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
e.Run(t, append(cmd, strconv.FormatInt(storage.FindDefault, 10))...)
|
e.Run(t, append(cmd, strconv.FormatInt(storage.FindDefault, 10))...)
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State)
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
|
|
||||||
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||||
|
@ -883,7 +883,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
|
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State)
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
require.Equal(t, []byte("on update|sub update"), res.Stack[0].Value())
|
require.Equal(t, []byte("on update|sub update"), res.Stack[0].Value())
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/server"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/server"
|
||||||
"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/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -293,7 +293,7 @@ func (e *executor) checkTxPersisted(t *testing.T, prefix ...string) (*transactio
|
||||||
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(aer))
|
require.Equal(t, 1, len(aer))
|
||||||
require.Equal(t, vm.HaltState, aer[0].VMState)
|
require.Equal(t, vmstate.Halt, aer[0].VMState)
|
||||||
return tx, height
|
return tx, height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ func TestSignMultisigTx(t *testing.T) {
|
||||||
e.checkTxTestInvokeOutput(t, 11)
|
e.checkTxTestInvokeOutput(t, 11)
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
|
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
|
||||||
})
|
})
|
||||||
|
|
||||||
e.In.WriteString("pass\r")
|
e.In.WriteString("pass\r")
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ func DumpApplicationLog(
|
||||||
if len(res.Executions) != 1 {
|
if len(res.Executions) != 1 {
|
||||||
_, _ = tw.Write([]byte("Success:\tunknown (no execution data)\n"))
|
_, _ = tw.Write([]byte("Success:\tunknown (no execution data)\n"))
|
||||||
} else {
|
} else {
|
||||||
_, _ = tw.Write([]byte(fmt.Sprintf("Success:\t%t\n", res.Executions[0].VMState == vm.HaltState)))
|
_, _ = tw.Write([]byte(fmt.Sprintf("Success:\t%t\n", res.Executions[0].VMState == vmstate.Halt)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if verbose {
|
if verbose {
|
||||||
|
@ -146,7 +147,7 @@ func DumpApplicationLog(
|
||||||
v.PrintOps(tw)
|
v.PrintOps(tw)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
for _, e := range res.Executions {
|
for _, e := range res.Executions {
|
||||||
if e.VMState != vm.HaltState {
|
if e.VMState != vmstate.Halt {
|
||||||
_, _ = tw.Write([]byte("Exception:\t" + e.FaultException + "\n"))
|
_, _ = tw.Write([]byte("Exception:\t" + e.FaultException + "\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"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"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -117,7 +118,7 @@ func (e *executor) compareQueryTxVerbose(t *testing.T, tx *transaction.Transacti
|
||||||
e.checkNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(int(height)).StringLE())
|
e.checkNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(int(height)).StringLE())
|
||||||
|
|
||||||
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
e.checkNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vm.HaltState))
|
e.checkNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vmstate.Halt))
|
||||||
for _, s := range tx.Signers {
|
for _, s := range tx.Signers {
|
||||||
e.checkNextLine(t, fmt.Sprintf(`Signer:\s+%s\s*\(%s\)`, address.Uint160ToString(s.Account), s.Scopes.String()))
|
e.checkNextLine(t, fmt.Sprintf(`Signer:\s+%s\s*\(%s\)`, address.Uint160ToString(s.Account), s.Scopes.String()))
|
||||||
}
|
}
|
||||||
|
@ -132,7 +133,7 @@ func (e *executor) compareQueryTxVerbose(t *testing.T, tx *transaction.Transacti
|
||||||
}
|
}
|
||||||
e.checkScriptDump(t, n)
|
e.checkScriptDump(t, n)
|
||||||
|
|
||||||
if res[0].Execution.VMState != vm.HaltState {
|
if res[0].Execution.VMState != vmstate.Halt {
|
||||||
e.checkNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
|
e.checkNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
|
||||||
}
|
}
|
||||||
e.checkEOF(t)
|
e.checkEOF(t)
|
||||||
|
|
|
@ -7,14 +7,15 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dump []blockDump
|
type dump []blockDump
|
||||||
|
|
||||||
type blockDump struct {
|
type blockDump struct {
|
||||||
Block uint32 `json:"block"`
|
Block uint32 `json:"block"`
|
||||||
Size int `json:"size"`
|
Size int `json:"size"`
|
||||||
Storage []storage.Operation `json:"storage"`
|
Storage []dboper.Operation `json:"storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDump() *dump {
|
func newDump() *dump {
|
||||||
|
|
|
@ -9,8 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/metrics"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -317,7 +316,7 @@ func TestConfigureAddresses(t *testing.T) {
|
||||||
t.Run("custom Pprof address", func(t *testing.T) {
|
t.Run("custom Pprof address", func(t *testing.T) {
|
||||||
cfg := &config.ApplicationConfiguration{
|
cfg := &config.ApplicationConfiguration{
|
||||||
Address: defaultAddress,
|
Address: defaultAddress,
|
||||||
Pprof: metrics.Config{
|
Pprof: config.BasicService{
|
||||||
Address: customAddress,
|
Address: customAddress,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -330,7 +329,7 @@ func TestConfigureAddresses(t *testing.T) {
|
||||||
t.Run("custom Prometheus address", func(t *testing.T) {
|
t.Run("custom Prometheus address", func(t *testing.T) {
|
||||||
cfg := &config.ApplicationConfiguration{
|
cfg := &config.ApplicationConfiguration{
|
||||||
Address: defaultAddress,
|
Address: defaultAddress,
|
||||||
Prometheus: metrics.Config{
|
Prometheus: config.BasicService{
|
||||||
Address: customAddress,
|
Address: customAddress,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -350,7 +349,7 @@ func TestInitBlockChain(t *testing.T) {
|
||||||
t.Run("empty logger", func(t *testing.T) {
|
t.Run("empty logger", func(t *testing.T) {
|
||||||
_, err := initBlockChain(config.Config{
|
_, err := initBlockChain(config.Config{
|
||||||
ApplicationConfiguration: config.ApplicationConfiguration{
|
ApplicationConfiguration: config.ApplicationConfiguration{
|
||||||
DBConfiguration: storage.DBConfiguration{
|
DBConfiguration: dbconfig.DBConfiguration{
|
||||||
Type: "inmemory",
|
Type: "inmemory",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -116,10 +117,10 @@ func TestLedgerTransactionWitnessCondition(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLedgerVMStates(t *testing.T) {
|
func TestLedgerVMStates(t *testing.T) {
|
||||||
require.EqualValues(t, ledger.NoneState, vm.NoneState)
|
require.EqualValues(t, ledger.NoneState, vmstate.None)
|
||||||
require.EqualValues(t, ledger.HaltState, vm.HaltState)
|
require.EqualValues(t, ledger.HaltState, vmstate.Halt)
|
||||||
require.EqualValues(t, ledger.FaultState, vm.FaultState)
|
require.EqualValues(t, ledger.FaultState, vmstate.Fault)
|
||||||
require.EqualValues(t, ledger.BreakState, vm.BreakState)
|
require.EqualValues(t, ledger.BreakState, vmstate.Break)
|
||||||
}
|
}
|
||||||
|
|
||||||
type nativeTestCase struct {
|
type nativeTestCase struct {
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/metrics"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplicationConfiguration config specific to the node.
|
// ApplicationConfiguration config specific to the node.
|
||||||
type ApplicationConfiguration struct {
|
type ApplicationConfiguration struct {
|
||||||
Address string `yaml:"Address"`
|
Address string `yaml:"Address"`
|
||||||
AnnouncedNodePort uint16 `yaml:"AnnouncedPort"`
|
AnnouncedNodePort uint16 `yaml:"AnnouncedPort"`
|
||||||
AttemptConnPeers int `yaml:"AttemptConnPeers"`
|
AttemptConnPeers int `yaml:"AttemptConnPeers"`
|
||||||
DBConfiguration storage.DBConfiguration `yaml:"DBConfiguration"`
|
DBConfiguration dbconfig.DBConfiguration `yaml:"DBConfiguration"`
|
||||||
DialTimeout int64 `yaml:"DialTimeout"`
|
DialTimeout int64 `yaml:"DialTimeout"`
|
||||||
LogPath string `yaml:"LogPath"`
|
LogPath string `yaml:"LogPath"`
|
||||||
MaxPeers int `yaml:"MaxPeers"`
|
MaxPeers int `yaml:"MaxPeers"`
|
||||||
MinPeers int `yaml:"MinPeers"`
|
MinPeers int `yaml:"MinPeers"`
|
||||||
NodePort uint16 `yaml:"NodePort"`
|
NodePort uint16 `yaml:"NodePort"`
|
||||||
PingInterval int64 `yaml:"PingInterval"`
|
PingInterval int64 `yaml:"PingInterval"`
|
||||||
PingTimeout int64 `yaml:"PingTimeout"`
|
PingTimeout int64 `yaml:"PingTimeout"`
|
||||||
Pprof metrics.Config `yaml:"Pprof"`
|
Pprof BasicService `yaml:"Pprof"`
|
||||||
Prometheus metrics.Config `yaml:"Prometheus"`
|
Prometheus BasicService `yaml:"Prometheus"`
|
||||||
ProtoTickInterval int64 `yaml:"ProtoTickInterval"`
|
ProtoTickInterval int64 `yaml:"ProtoTickInterval"`
|
||||||
Relay bool `yaml:"Relay"`
|
Relay bool `yaml:"Relay"`
|
||||||
RPC rpc.Config `yaml:"RPC"`
|
RPC rpc.Config `yaml:"RPC"`
|
||||||
UnlockWallet Wallet `yaml:"UnlockWallet"`
|
UnlockWallet Wallet `yaml:"UnlockWallet"`
|
||||||
Oracle OracleConfiguration `yaml:"Oracle"`
|
Oracle OracleConfiguration `yaml:"Oracle"`
|
||||||
P2PNotary P2PNotary `yaml:"P2PNotary"`
|
P2PNotary P2PNotary `yaml:"P2PNotary"`
|
||||||
StateRoot StateRoot `yaml:"StateRoot"`
|
StateRoot StateRoot `yaml:"StateRoot"`
|
||||||
// ExtensiblePoolSize is the maximum amount of the extensible payloads from a single sender.
|
// ExtensiblePoolSize is the maximum amount of the extensible payloads from a single sender.
|
||||||
ExtensiblePoolSize int `yaml:"ExtensiblePoolSize"`
|
ExtensiblePoolSize int `yaml:"ExtensiblePoolSize"`
|
||||||
}
|
}
|
||||||
|
|
8
pkg/config/basic_service.go
Normal file
8
pkg/config/basic_service.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
// BasicService is used for simple services like Pprof or Prometheus monitoring.
|
||||||
|
type BasicService struct {
|
||||||
|
Enabled bool `yaml:"Enabled"`
|
||||||
|
Address string `yaml:"Address"`
|
||||||
|
Port string `yaml:"Port"`
|
||||||
|
}
|
17
pkg/config/limits/limits.go
Normal file
17
pkg/config/limits/limits.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
Package limits contains a number of system-wide hardcoded constants.
|
||||||
|
Many of the Neo protocol parameters can be adjusted by the configuration, but
|
||||||
|
some can not and this package contains hardcoded limits that are relevant for
|
||||||
|
many applications.
|
||||||
|
*/
|
||||||
|
package limits
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxStorageKeyLen is the maximum length of a key for storage items.
|
||||||
|
// Contracts can't use keys longer than that in their requests to the DB.
|
||||||
|
MaxStorageKeyLen = 64
|
||||||
|
// MaxStorageValueLen is the maximum length of a value for storage items.
|
||||||
|
// It is set to be the maximum value for uint16, contracts can't put
|
||||||
|
// values longer than that into the DB.
|
||||||
|
MaxStorageValueLen = 65535
|
||||||
|
)
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -136,7 +137,7 @@ func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBloc
|
||||||
|
|
||||||
func newLevelDBForTesting(t testing.TB) storage.Store {
|
func newLevelDBForTesting(t testing.TB) storage.Store {
|
||||||
dbPath := t.TempDir()
|
dbPath := t.TempDir()
|
||||||
dbOptions := storage.LevelDBOptions{
|
dbOptions := dbconfig.LevelDBOptions{
|
||||||
DataDirectoryPath: dbPath,
|
DataDirectoryPath: dbPath,
|
||||||
}
|
}
|
||||||
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
||||||
|
@ -147,7 +148,7 @@ func newLevelDBForTesting(t testing.TB) storage.Store {
|
||||||
func newBoltStoreForTesting(t testing.TB) storage.Store {
|
func newBoltStoreForTesting(t testing.TB) storage.Store {
|
||||||
d := t.TempDir()
|
d := t.TempDir()
|
||||||
dbPath := filepath.Join(d, "test_bolt_db")
|
dbPath := filepath.Join(d, "test_bolt_db")
|
||||||
boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath})
|
boltDBStore, err := storage.NewBoltDBStore(dbconfig.BoltDBOptions{FilePath: dbPath})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return boltDBStore
|
return boltDBStore
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer/services"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer/services"
|
||||||
|
@ -40,6 +41,7 @@ import (
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -816,7 +818,7 @@ func (bc *Blockchain) notificationDispatcher() {
|
||||||
for ch := range executionFeed {
|
for ch := range executionFeed {
|
||||||
ch <- aer
|
ch <- aer
|
||||||
}
|
}
|
||||||
if aer.VMState == vm.HaltState {
|
if aer.VMState == vmstate.Halt {
|
||||||
for i := range aer.Events {
|
for i := range aer.Events {
|
||||||
for ch := range notificationFeed {
|
for ch := range notificationFeed {
|
||||||
ch <- &subscriptions.NotificationEvent{
|
ch <- &subscriptions.NotificationEvent{
|
||||||
|
@ -1065,7 +1067,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
||||||
err = fmt.Errorf("failed to store exec result: %w", err)
|
err = fmt.Errorf("failed to store exec result: %w", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if aer.Execution.VMState == vm.HaltState {
|
if aer.Execution.VMState == vmstate.Halt {
|
||||||
for j := range aer.Execution.Events {
|
for j := range aer.Execution.Events {
|
||||||
bc.handleNotification(&aer.Execution.Events[j], kvcache, transCache, block, aer.Container)
|
bc.handleNotification(&aer.Execution.Events[j], kvcache, transCache, block, aer.Container)
|
||||||
}
|
}
|
||||||
|
@ -1327,7 +1329,7 @@ func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.S
|
||||||
var id []byte
|
var id []byte
|
||||||
if len(arr) == 4 {
|
if len(arr) == 4 {
|
||||||
id, err = arr[3].TryBytes()
|
id, err = arr[3].TryBytes()
|
||||||
if err != nil || len(id) > storage.MaxStorageKeyLen {
|
if err != nil || len(id) > limits.MaxStorageKeyLen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -39,10 +40,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"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/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -52,7 +53,7 @@ func newLevelDBForTestingWithPath(t testing.TB, dbPath string) (storage.Store, s
|
||||||
if dbPath == "" {
|
if dbPath == "" {
|
||||||
dbPath = t.TempDir()
|
dbPath = t.TempDir()
|
||||||
}
|
}
|
||||||
dbOptions := storage.LevelDBOptions{
|
dbOptions := dbconfig.LevelDBOptions{
|
||||||
DataDirectoryPath: dbPath,
|
DataDirectoryPath: dbPath,
|
||||||
}
|
}
|
||||||
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
||||||
|
@ -826,7 +827,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
|
||||||
|
|
||||||
exec := <-executionCh
|
exec := <-executionCh
|
||||||
require.Equal(t, b.Hash(), exec.Container)
|
require.Equal(t, b.Hash(), exec.Container)
|
||||||
require.Equal(t, exec.VMState, vm.HaltState)
|
require.Equal(t, exec.VMState, vmstate.Halt)
|
||||||
|
|
||||||
// 3 burn events for every tx and 1 mint for primary node
|
// 3 burn events for every tx and 1 mint for primary node
|
||||||
require.True(t, len(notificationCh) >= 4)
|
require.True(t, len(notificationCh) >= 4)
|
||||||
|
@ -841,7 +842,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
|
||||||
require.Equal(t, txExpected, tx)
|
require.Equal(t, txExpected, tx)
|
||||||
exec := <-executionCh
|
exec := <-executionCh
|
||||||
require.Equal(t, tx.Hash(), exec.Container)
|
require.Equal(t, tx.Hash(), exec.Container)
|
||||||
if exec.VMState == vm.HaltState {
|
if exec.VMState == vmstate.Halt {
|
||||||
notif := <-notificationCh
|
notif := <-notificationCh
|
||||||
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
|
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
|
||||||
}
|
}
|
||||||
|
@ -855,7 +856,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
|
||||||
|
|
||||||
exec = <-executionCh
|
exec = <-executionCh
|
||||||
require.Equal(t, b.Hash(), exec.Container)
|
require.Equal(t, b.Hash(), exec.Container)
|
||||||
require.Equal(t, exec.VMState, vm.HaltState)
|
require.Equal(t, exec.VMState, vmstate.Halt)
|
||||||
|
|
||||||
bc.UnsubscribeFromBlocks(blockCh)
|
bc.UnsubscribeFromBlocks(blockCh)
|
||||||
bc.UnsubscribeFromTransactions(txCh)
|
bc.UnsubscribeFromTransactions(txCh)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
@ -830,7 +831,7 @@ func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32,
|
||||||
func (dao *Simple) getKeyBuf(len int) []byte {
|
func (dao *Simple) getKeyBuf(len int) []byte {
|
||||||
if dao.private {
|
if dao.private {
|
||||||
if dao.keyBuf == nil {
|
if dao.keyBuf == nil {
|
||||||
dao.keyBuf = make([]byte, 0, 1+4+storage.MaxStorageKeyLen) // Prefix, uint32, key.
|
dao.keyBuf = make([]byte, 0, 1+4+limits.MaxStorageKeyLen) // Prefix, uint32, key.
|
||||||
}
|
}
|
||||||
return dao.keyBuf[:len] // Should have enough capacity.
|
return dao.keyBuf[:len] // Should have enough capacity.
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"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/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -168,7 +167,7 @@ func CallFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract
|
||||||
return fmt.Errorf("%w: %v", ErrNativeCall, err)
|
return fmt.Errorf("%w: %v", ErrNativeCall, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ic.VM.State() == vm.FaultState {
|
if ic.VM.HasFailed() {
|
||||||
return ErrNativeCall
|
return ErrNativeCall
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,10 +81,10 @@ func getContextInternal(ic *interop.Context, isReadOnly bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func putWithContext(ic *interop.Context, stc *Context, key []byte, value []byte) error {
|
func putWithContext(ic *interop.Context, stc *Context, key []byte, value []byte) error {
|
||||||
if len(key) > storage.MaxStorageKeyLen {
|
if len(key) > limits.MaxStorageKeyLen {
|
||||||
return errors.New("key is too big")
|
return errors.New("key is too big")
|
||||||
}
|
}
|
||||||
if len(value) > storage.MaxStorageValueLen {
|
if len(value) > limits.MaxStorageValueLen {
|
||||||
return errors.New("value is too big")
|
return errors.New("value is too big")
|
||||||
}
|
}
|
||||||
if stc.ReadOnly {
|
if stc.ReadOnly {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
@ -12,7 +13,6 @@ import (
|
||||||
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"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/core/storage"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
|
@ -61,7 +61,7 @@ func TestPut(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("check limits", func(t *testing.T) {
|
t.Run("check limits", func(t *testing.T) {
|
||||||
initVM(t, make([]byte, storage.MaxStorageKeyLen), make([]byte, storage.MaxStorageValueLen), -1)
|
initVM(t, make([]byte, limits.MaxStorageKeyLen), make([]byte, limits.MaxStorageValueLen), -1)
|
||||||
require.NoError(t, istorage.Put(ic))
|
require.NoError(t, istorage.Put(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -72,11 +72,11 @@ func TestPut(t *testing.T) {
|
||||||
require.Error(t, istorage.Put(ic))
|
require.Error(t, istorage.Put(ic))
|
||||||
})
|
})
|
||||||
t.Run("big key", func(t *testing.T) {
|
t.Run("big key", func(t *testing.T) {
|
||||||
initVM(t, make([]byte, storage.MaxStorageKeyLen+1), []byte{1}, -1)
|
initVM(t, make([]byte, limits.MaxStorageKeyLen+1), []byte{1}, -1)
|
||||||
require.Error(t, istorage.Put(ic))
|
require.Error(t, istorage.Put(ic))
|
||||||
})
|
})
|
||||||
t.Run("big value", func(t *testing.T) {
|
t.Run("big value", func(t *testing.T) {
|
||||||
initVM(t, []byte{1}, make([]byte, storage.MaxStorageValueLen+1), -1)
|
initVM(t, []byte{1}, make([]byte, limits.MaxStorageValueLen+1), -1)
|
||||||
require.Error(t, istorage.Put(ic))
|
require.Error(t, istorage.Put(ic))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,14 +6,14 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// maxPathLength is the max length of the extension node key.
|
// maxPathLength is the max length of the extension node key.
|
||||||
maxPathLength = (storage.MaxStorageKeyLen + 4) * 2
|
maxPathLength = (limits.MaxStorageKeyLen + 4) * 2
|
||||||
|
|
||||||
// MaxKeyLength is the max length of the key to put in the trie
|
// MaxKeyLength is the max length of the key to put in the trie
|
||||||
// before transforming to nibbles.
|
// before transforming to nibbles.
|
||||||
|
|
|
@ -5,13 +5,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxValueLength is the max length of a leaf node value.
|
// MaxValueLength is the max length of a leaf node value.
|
||||||
const MaxValueLength = 3 + storage.MaxStorageValueLen + 1
|
const MaxValueLength = 3 + limits.MaxStorageValueLen + 1
|
||||||
|
|
||||||
// LeafNode represents an MPT's leaf node.
|
// LeafNode represents an MPT's leaf node.
|
||||||
type LeafNode struct {
|
type LeafNode struct {
|
||||||
|
|
|
@ -13,8 +13,8 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"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/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ledger provides an interface to blocks/transactions storage for smart
|
// Ledger provides an interface to blocks/transactions storage for smart
|
||||||
|
@ -169,7 +169,7 @@ func (l *Ledger) getTransactionVMState(ic *interop.Context, params []stackitem.I
|
||||||
}
|
}
|
||||||
h, _, aer, err := ic.DAO.GetTxExecResult(hash)
|
h, _, aer, err := ic.DAO.GetTxExecResult(hash)
|
||||||
if err != nil || !isTraceableBlock(ic, h) {
|
if err != nil || !isTraceableBlock(ic, h) {
|
||||||
return stackitem.Make(vm.NoneState)
|
return stackitem.Make(vmstate.None)
|
||||||
}
|
}
|
||||||
return stackitem.Make(aer.VMState)
|
return stackitem.Make(aer.VMState)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
"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/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,22 +56,22 @@ func TestLedger_GetTransactionState(t *testing.T) {
|
||||||
hash := e.InvokeScript(t, []byte{byte(opcode.RET)}, []neotest.Signer{c.Committee})
|
hash := e.InvokeScript(t, []byte{byte(opcode.RET)}, []neotest.Signer{c.Committee})
|
||||||
|
|
||||||
t.Run("unknown transaction", func(t *testing.T) {
|
t.Run("unknown transaction", func(t *testing.T) {
|
||||||
ledgerInvoker.Invoke(t, vm.NoneState, "getTransactionVMState", util.Uint256{1, 2, 3})
|
ledgerInvoker.Invoke(t, vmstate.None, "getTransactionVMState", util.Uint256{1, 2, 3})
|
||||||
})
|
})
|
||||||
t.Run("not a hash", func(t *testing.T) {
|
t.Run("not a hash", func(t *testing.T) {
|
||||||
ledgerInvoker.InvokeFail(t, "expected []byte of size 32", "getTransactionVMState", []byte{1, 2, 3})
|
ledgerInvoker.InvokeFail(t, "expected []byte of size 32", "getTransactionVMState", []byte{1, 2, 3})
|
||||||
})
|
})
|
||||||
t.Run("good: HALT", func(t *testing.T) {
|
t.Run("good: HALT", func(t *testing.T) {
|
||||||
ledgerInvoker.Invoke(t, vm.HaltState, "getTransactionVMState", hash)
|
ledgerInvoker.Invoke(t, vmstate.Halt, "getTransactionVMState", hash)
|
||||||
})
|
})
|
||||||
t.Run("isn't traceable", func(t *testing.T) {
|
t.Run("isn't traceable", func(t *testing.T) {
|
||||||
// Add more blocks so that tx becomes untraceable.
|
// Add more blocks so that tx becomes untraceable.
|
||||||
e.GenerateNewBlocks(t, int(e.Chain.GetConfig().MaxTraceableBlocks))
|
e.GenerateNewBlocks(t, int(e.Chain.GetConfig().MaxTraceableBlocks))
|
||||||
ledgerInvoker.Invoke(t, vm.NoneState, "getTransactionVMState", hash)
|
ledgerInvoker.Invoke(t, vmstate.None, "getTransactionVMState", hash)
|
||||||
})
|
})
|
||||||
t.Run("good: FAULT", func(t *testing.T) {
|
t.Run("good: FAULT", func(t *testing.T) {
|
||||||
faultedH := e.InvokeScript(t, []byte{byte(opcode.ABORT)}, []neotest.Signer{c.Committee})
|
faultedH := e.InvokeScript(t, []byte{byte(opcode.ABORT)}, []neotest.Signer{c.Committee})
|
||||||
ledgerInvoker.Invoke(t, vm.FaultState, "getTransactionVMState", faultedH)
|
ledgerInvoker.Invoke(t, vmstate.Fault, "getTransactionVMState", faultedH)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -21,10 +22,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"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/vm"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ func TestManagement_ContractCache(t *testing.T) {
|
||||||
managementInvoker.CheckHalt(t, tx1.Hash())
|
managementInvoker.CheckHalt(t, tx1.Hash())
|
||||||
aer, err := managementInvoker.Chain.GetAppExecResults(tx2.Hash(), trigger.Application)
|
aer, err := managementInvoker.Chain.GetAppExecResults(tx2.Hash(), trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
require.Equal(t, vmstate.Halt, aer[0].VMState, aer[0].FaultException)
|
||||||
require.NotEqual(t, stackitem.Null{}, aer[0].Stack)
|
require.NotEqual(t, stackitem.Null{}, aer[0].Stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,9 +282,9 @@ func TestManagement_ContractDeploy(t *testing.T) {
|
||||||
func TestManagement_StartFromHeight(t *testing.T) {
|
func TestManagement_StartFromHeight(t *testing.T) {
|
||||||
// Create database to be able to start another chain from the same height later.
|
// Create database to be able to start another chain from the same height later.
|
||||||
ldbDir := t.TempDir()
|
ldbDir := t.TempDir()
|
||||||
dbConfig := storage.DBConfiguration{
|
dbConfig := dbconfig.DBConfiguration{
|
||||||
Type: "leveldb",
|
Type: "leveldb",
|
||||||
LevelDBOptions: storage.LevelDBOptions{
|
LevelDBOptions: dbconfig.LevelDBOptions{
|
||||||
DataDirectoryPath: ldbDir,
|
DataDirectoryPath: ldbDir,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ 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/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/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotificationEvent is a tuple of the scripthash that has emitted the Item as a
|
// NotificationEvent is a tuple of the scripthash that has emitted the Item as a
|
||||||
|
@ -94,7 +94,7 @@ func (aer *AppExecResult) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem
|
||||||
func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
|
func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
|
||||||
r.ReadBytes(aer.Container[:])
|
r.ReadBytes(aer.Container[:])
|
||||||
aer.Trigger = trigger.Type(r.ReadB())
|
aer.Trigger = trigger.Type(r.ReadB())
|
||||||
aer.VMState = vm.State(r.ReadB())
|
aer.VMState = vmstate.State(r.ReadB())
|
||||||
aer.GasConsumed = int64(r.ReadU64LE())
|
aer.GasConsumed = int64(r.ReadU64LE())
|
||||||
sz := r.ReadVarUint()
|
sz := r.ReadVarUint()
|
||||||
if stackitem.MaxDeserialized < sz && r.Err == nil {
|
if stackitem.MaxDeserialized < sz && r.Err == nil {
|
||||||
|
@ -197,7 +197,7 @@ func (aer *AppExecResult) UnmarshalJSON(data []byte) error {
|
||||||
// all resulting notifications, state, stack and other metadata.
|
// all resulting notifications, state, stack and other metadata.
|
||||||
type Execution struct {
|
type Execution struct {
|
||||||
Trigger trigger.Type
|
Trigger trigger.Type
|
||||||
VMState vm.State
|
VMState vmstate.State
|
||||||
GasConsumed int64
|
GasConsumed int64
|
||||||
Stack []stackitem.Item
|
Stack []stackitem.Item
|
||||||
Events []NotificationEvent
|
Events []NotificationEvent
|
||||||
|
@ -266,7 +266,7 @@ func (e *Execution) UnmarshalJSON(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.Trigger = trigger
|
e.Trigger = trigger
|
||||||
state, err := vm.StateFromString(aux.VMState)
|
state, err := vmstate.FromString(aux.VMState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"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/vm"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ func BenchmarkAppExecResult_EncodeBinary(b *testing.B) {
|
||||||
Container: random.Uint256(),
|
Container: random.Uint256(),
|
||||||
Execution: Execution{
|
Execution: Execution{
|
||||||
Trigger: trigger.Application,
|
Trigger: trigger.Application,
|
||||||
VMState: vm.HaltState,
|
VMState: vmstate.Halt,
|
||||||
GasConsumed: 12345,
|
GasConsumed: 12345,
|
||||||
Stack: []stackitem.Item{},
|
Stack: []stackitem.Item{},
|
||||||
Events: []NotificationEvent{{
|
Events: []NotificationEvent{{
|
||||||
|
@ -54,7 +54,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
|
||||||
Container: random.Uint256(),
|
Container: random.Uint256(),
|
||||||
Execution: Execution{
|
Execution: Execution{
|
||||||
Trigger: 1,
|
Trigger: 1,
|
||||||
VMState: vm.HaltState,
|
VMState: vmstate.Halt,
|
||||||
GasConsumed: 10,
|
GasConsumed: 10,
|
||||||
Stack: []stackitem.Item{stackitem.NewBool(true)},
|
Stack: []stackitem.Item{stackitem.NewBool(true)},
|
||||||
Events: []NotificationEvent{},
|
Events: []NotificationEvent{},
|
||||||
|
@ -63,12 +63,12 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Run("halt", func(t *testing.T) {
|
t.Run("halt", func(t *testing.T) {
|
||||||
appExecResult := newAer()
|
appExecResult := newAer()
|
||||||
appExecResult.VMState = vm.HaltState
|
appExecResult.VMState = vmstate.Halt
|
||||||
testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
|
testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
|
||||||
})
|
})
|
||||||
t.Run("fault", func(t *testing.T) {
|
t.Run("fault", func(t *testing.T) {
|
||||||
appExecResult := newAer()
|
appExecResult := newAer()
|
||||||
appExecResult.VMState = vm.FaultState
|
appExecResult.VMState = vmstate.Fault
|
||||||
testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
|
testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
|
||||||
})
|
})
|
||||||
t.Run("with interop", func(t *testing.T) {
|
t.Run("with interop", func(t *testing.T) {
|
||||||
|
@ -150,7 +150,7 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
||||||
Container: random.Uint256(),
|
Container: random.Uint256(),
|
||||||
Execution: Execution{
|
Execution: Execution{
|
||||||
Trigger: trigger.Application,
|
Trigger: trigger.Application,
|
||||||
VMState: vm.HaltState,
|
VMState: vmstate.Halt,
|
||||||
GasConsumed: 10,
|
GasConsumed: 10,
|
||||||
Stack: []stackitem.Item{},
|
Stack: []stackitem.Item{},
|
||||||
Events: []NotificationEvent{},
|
Events: []NotificationEvent{},
|
||||||
|
@ -164,7 +164,7 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
||||||
Container: random.Uint256(),
|
Container: random.Uint256(),
|
||||||
Execution: Execution{
|
Execution: Execution{
|
||||||
Trigger: trigger.Application,
|
Trigger: trigger.Application,
|
||||||
VMState: vm.FaultState,
|
VMState: vmstate.Fault,
|
||||||
GasConsumed: 10,
|
GasConsumed: 10,
|
||||||
Stack: []stackitem.Item{stackitem.NewBool(true)},
|
Stack: []stackitem.Item{stackitem.NewBool(true)},
|
||||||
Events: []NotificationEvent{},
|
Events: []NotificationEvent{},
|
||||||
|
@ -178,7 +178,7 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
||||||
Container: random.Uint256(),
|
Container: random.Uint256(),
|
||||||
Execution: Execution{
|
Execution: Execution{
|
||||||
Trigger: trigger.OnPersist,
|
Trigger: trigger.OnPersist,
|
||||||
VMState: vm.HaltState,
|
VMState: vmstate.Halt,
|
||||||
GasConsumed: 10,
|
GasConsumed: 10,
|
||||||
Stack: []stackitem.Item{},
|
Stack: []stackitem.Item{},
|
||||||
Events: []NotificationEvent{},
|
Events: []NotificationEvent{},
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -224,5 +224,5 @@ func (t *NEP11Transfer) EncodeBinary(w *io.BinWriter) {
|
||||||
// DecodeBinary implements the io.Serializable interface.
|
// DecodeBinary implements the io.Serializable interface.
|
||||||
func (t *NEP11Transfer) DecodeBinary(r *io.BinReader) {
|
func (t *NEP11Transfer) DecodeBinary(r *io.BinReader) {
|
||||||
t.NEP17Transfer.DecodeBinary(r)
|
t.NEP17Transfer.DecodeBinary(r)
|
||||||
t.ID = r.ReadVarBytes(storage.MaxStorageKeyLen)
|
t.ID = r.ReadVarBytes(limits.MaxStorageKeyLen)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BoltDBOptions configuration for boltdb.
|
|
||||||
type BoltDBOptions struct {
|
|
||||||
FilePath string `yaml:"FilePath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bucket represents bucket used in boltdb to store all the data.
|
// Bucket represents bucket used in boltdb to store all the data.
|
||||||
var Bucket = []byte("DB")
|
var Bucket = []byte("DB")
|
||||||
|
|
||||||
|
@ -25,7 +21,7 @@ type BoltDBStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBoltDBStore returns a new ready to use BoltDB storage with created bucket.
|
// NewBoltDBStore returns a new ready to use BoltDB storage with created bucket.
|
||||||
func NewBoltDBStore(cfg BoltDBOptions) (*BoltDBStore, error) {
|
func NewBoltDBStore(cfg dbconfig.BoltDBOptions) (*BoltDBStore, error) {
|
||||||
var opts *bbolt.Options // should be exposed via BoltDBOptions if anything needed
|
var opts *bbolt.Options // should be exposed via BoltDBOptions if anything needed
|
||||||
fileMode := os.FileMode(0600) // should be exposed via BoltDBOptions if anything needed
|
fileMode := os.FileMode(0600) // should be exposed via BoltDBOptions if anything needed
|
||||||
fileName := cfg.FilePath
|
fileName := cfg.FilePath
|
||||||
|
|
|
@ -4,13 +4,14 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newBoltStoreForTesting(t testing.TB) Store {
|
func newBoltStoreForTesting(t testing.TB) Store {
|
||||||
d := t.TempDir()
|
d := t.TempDir()
|
||||||
testFileName := filepath.Join(d, "test_bolt_db")
|
testFileName := filepath.Join(d, "test_bolt_db")
|
||||||
boltDBStore, err := NewBoltDBStore(BoltDBOptions{FilePath: testFileName})
|
boltDBStore, err := NewBoltDBStore(dbconfig.BoltDBOptions{FilePath: testFileName})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return boltDBStore
|
return boltDBStore
|
||||||
}
|
}
|
||||||
|
|
21
pkg/core/storage/dbconfig/store_config.go
Normal file
21
pkg/core/storage/dbconfig/store_config.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
Package dbconfig is a micropackage that contains storage DB configuration options.
|
||||||
|
*/
|
||||||
|
package dbconfig
|
||||||
|
|
||||||
|
type (
|
||||||
|
// DBConfiguration describes configuration for DB. Supported: 'levelDB', 'boltDB'.
|
||||||
|
DBConfiguration struct {
|
||||||
|
Type string `yaml:"Type"`
|
||||||
|
LevelDBOptions LevelDBOptions `yaml:"LevelDBOptions"`
|
||||||
|
BoltDBOptions BoltDBOptions `yaml:"BoltDBOptions"`
|
||||||
|
}
|
||||||
|
// LevelDBOptions configuration for LevelDB.
|
||||||
|
LevelDBOptions struct {
|
||||||
|
DataDirectoryPath string `yaml:"DataDirectoryPath"`
|
||||||
|
}
|
||||||
|
// BoltDBOptions configuration for BoltDB.
|
||||||
|
BoltDBOptions struct {
|
||||||
|
FilePath string `yaml:"FilePath"`
|
||||||
|
}
|
||||||
|
)
|
13
pkg/core/storage/dboper/operation.go
Normal file
13
pkg/core/storage/dboper/operation.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
Package dboper contains a type used to represent single DB operation.
|
||||||
|
*/
|
||||||
|
package dboper
|
||||||
|
|
||||||
|
// Operation represents a single KV operation (add/del/change) performed
|
||||||
|
// in the DB.
|
||||||
|
type Operation struct {
|
||||||
|
// State can be Added, Changed or Deleted.
|
||||||
|
State string `json:"state"`
|
||||||
|
Key []byte `json:"key"`
|
||||||
|
Value []byte `json:"value,omitempty"`
|
||||||
|
}
|
|
@ -1,17 +1,13 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
"github.com/syndtr/goleveldb/leveldb/filter"
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LevelDBOptions configuration for LevelDB.
|
|
||||||
type LevelDBOptions struct {
|
|
||||||
DataDirectoryPath string `yaml:"DataDirectoryPath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LevelDBStore is the official storage implementation for storing and retrieving
|
// LevelDBStore is the official storage implementation for storing and retrieving
|
||||||
// blockchain data.
|
// blockchain data.
|
||||||
type LevelDBStore struct {
|
type LevelDBStore struct {
|
||||||
|
@ -21,7 +17,7 @@ type LevelDBStore struct {
|
||||||
|
|
||||||
// NewLevelDBStore returns a new LevelDBStore object that will
|
// NewLevelDBStore returns a new LevelDBStore object that will
|
||||||
// initialize the database found at the given path.
|
// initialize the database found at the given path.
|
||||||
func NewLevelDBStore(cfg LevelDBOptions) (*LevelDBStore, error) {
|
func NewLevelDBStore(cfg dbconfig.LevelDBOptions) (*LevelDBStore, error) {
|
||||||
var opts = new(opt.Options) // should be exposed via LevelDBOptions if anything needed
|
var opts = new(opt.Options) // should be exposed via LevelDBOptions if anything needed
|
||||||
|
|
||||||
opts.Filter = filter.NewBloomFilter(10)
|
opts.Filter = filter.NewBloomFilter(10)
|
||||||
|
|
|
@ -3,14 +3,15 @@ package storage
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLevelDBForTesting(t testing.TB) Store {
|
func newLevelDBForTesting(t testing.TB) Store {
|
||||||
ldbDir := t.TempDir()
|
ldbDir := t.TempDir()
|
||||||
dbConfig := DBConfiguration{
|
dbConfig := dbconfig.DBConfiguration{
|
||||||
Type: "leveldb",
|
Type: "leveldb",
|
||||||
LevelDBOptions: LevelDBOptions{
|
LevelDBOptions: dbconfig.LevelDBOptions{
|
||||||
DataDirectoryPath: ldbDir,
|
DataDirectoryPath: ldbDir,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,13 +68,14 @@ func (s *MemoryStore) putChangeSet(puts map[string][]byte, stores map[string][]b
|
||||||
|
|
||||||
// Seek implements the Store interface.
|
// Seek implements the Store interface.
|
||||||
func (s *MemoryStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
func (s *MemoryStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
||||||
s.mut.RLock()
|
s.seek(rng, f, s.mut.RLock, s.mut.RUnlock)
|
||||||
s.seek(rng, f)
|
|
||||||
s.mut.RUnlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekGC implements the Store interface.
|
// SeekGC implements the Store interface.
|
||||||
func (s *MemoryStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
|
func (s *MemoryStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
|
||||||
|
noop := func() {}
|
||||||
|
// Keep RW lock for the whole Seek time, state must be consistent across whole
|
||||||
|
// operation and we call delete in the handler.
|
||||||
s.mut.Lock()
|
s.mut.Lock()
|
||||||
// We still need to perform normal seek, some GC operations can be
|
// We still need to perform normal seek, some GC operations can be
|
||||||
// sensitive to the order of KV pairs.
|
// sensitive to the order of KV pairs.
|
||||||
|
@ -83,7 +84,7 @@ func (s *MemoryStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
|
||||||
delete(s.chooseMap(k), string(k))
|
delete(s.chooseMap(k), string(k))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
}, noop, noop)
|
||||||
s.mut.Unlock()
|
s.mut.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -91,7 +92,7 @@ func (s *MemoryStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
|
||||||
// seek is an internal unlocked implementation of Seek. `start` denotes whether
|
// seek is an internal unlocked implementation of Seek. `start` denotes whether
|
||||||
// seeking starting from the provided prefix should be performed. Backwards
|
// seeking starting from the provided prefix should be performed. Backwards
|
||||||
// seeking from some point is supported with corresponding SeekRange field set.
|
// seeking from some point is supported with corresponding SeekRange field set.
|
||||||
func (s *MemoryStore) seek(rng SeekRange, f func(k, v []byte) bool) {
|
func (s *MemoryStore) seek(rng SeekRange, f func(k, v []byte) bool, lock func(), unlock func()) {
|
||||||
sPrefix := string(rng.Prefix)
|
sPrefix := string(rng.Prefix)
|
||||||
lPrefix := len(sPrefix)
|
lPrefix := len(sPrefix)
|
||||||
sStart := string(rng.Start)
|
sStart := string(rng.Start)
|
||||||
|
@ -111,6 +112,7 @@ func (s *MemoryStore) seek(rng SeekRange, f func(k, v []byte) bool) {
|
||||||
return res != 0 && rng.Backwards == (res > 0)
|
return res != 0 && rng.Backwards == (res > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock()
|
||||||
m := s.chooseMap(rng.Prefix)
|
m := s.chooseMap(rng.Prefix)
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
if v != nil && isKeyOK(k) {
|
if v != nil && isKeyOK(k) {
|
||||||
|
@ -120,6 +122,7 @@ func (s *MemoryStore) seek(rng SeekRange, f func(k, v []byte) bool) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unlock()
|
||||||
sort.Slice(memList, func(i, j int) bool {
|
sort.Slice(memList, func(i, j int) bool {
|
||||||
return less(memList[i].Key, memList[j].Key)
|
return less(memList[i].Key, memList[j].Key)
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,23 +42,6 @@ const (
|
||||||
ExecTransaction byte = 2
|
ExecTransaction byte = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxStorageKeyLen is the maximum length of a key for storage items.
|
|
||||||
MaxStorageKeyLen = 64
|
|
||||||
// MaxStorageValueLen is the maximum length of a value for storage items.
|
|
||||||
// It is set to be the maximum value for uint16.
|
|
||||||
MaxStorageValueLen = 65535
|
|
||||||
)
|
|
||||||
|
|
||||||
// Operation represents a single KV operation (add/del/change) performed
|
|
||||||
// in the DB.
|
|
||||||
type Operation struct {
|
|
||||||
// State can be Added, Changed or Deleted.
|
|
||||||
State string `json:"state"`
|
|
||||||
Key []byte `json:"key"`
|
|
||||||
Value []byte `json:"value,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SeekRange represents options for Store.Seek operation.
|
// SeekRange represents options for Store.Seek operation.
|
||||||
type SeekRange struct {
|
type SeekRange struct {
|
||||||
// Prefix denotes the Seek's lookup key.
|
// Prefix denotes the Seek's lookup key.
|
||||||
|
@ -125,7 +110,7 @@ func seekRangeToPrefixes(sr SeekRange) *util.Range {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStore creates storage with preselected in configuration database type.
|
// NewStore creates storage with preselected in configuration database type.
|
||||||
func NewStore(cfg DBConfiguration) (Store, error) {
|
func NewStore(cfg dbconfig.DBConfiguration) (Store, error) {
|
||||||
var store Store
|
var store Store
|
||||||
var err error
|
var err error
|
||||||
switch cfg.Type {
|
switch cfg.Type {
|
||||||
|
@ -141,10 +126,10 @@ func NewStore(cfg DBConfiguration) (Store, error) {
|
||||||
return store, err
|
return store, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchToOperations converts a batch of changes into array of Operations.
|
// BatchToOperations converts a batch of changes into array of dboper.Operation.
|
||||||
func BatchToOperations(batch *MemBatch) []Operation {
|
func BatchToOperations(batch *MemBatch) []dboper.Operation {
|
||||||
size := len(batch.Put) + len(batch.Deleted)
|
size := len(batch.Put) + len(batch.Deleted)
|
||||||
ops := make([]Operation, 0, size)
|
ops := make([]dboper.Operation, 0, size)
|
||||||
for i := range batch.Put {
|
for i := range batch.Put {
|
||||||
key := batch.Put[i].Key
|
key := batch.Put[i].Key
|
||||||
if len(key) == 0 || key[0] != byte(STStorage) && key[0] != byte(STTempStorage) {
|
if len(key) == 0 || key[0] != byte(STStorage) && key[0] != byte(STTempStorage) {
|
||||||
|
@ -156,7 +141,7 @@ func BatchToOperations(batch *MemBatch) []Operation {
|
||||||
op = "Changed"
|
op = "Changed"
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = append(ops, Operation{
|
ops = append(ops, dboper.Operation{
|
||||||
State: op,
|
State: op,
|
||||||
Key: key[1:],
|
Key: key[1:],
|
||||||
Value: batch.Put[i].Value,
|
Value: batch.Put[i].Value,
|
||||||
|
@ -170,7 +155,7 @@ func BatchToOperations(batch *MemBatch) []Operation {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = append(ops, Operation{
|
ops = append(ops, dboper.Operation{
|
||||||
State: "Deleted",
|
State: "Deleted",
|
||||||
Key: key[1:],
|
Key: key[1:],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
type (
|
|
||||||
// DBConfiguration describes configuration for DB. Supported: 'levelDB', 'boltDB'.
|
|
||||||
DBConfiguration struct {
|
|
||||||
Type string `yaml:"Type"`
|
|
||||||
LevelDBOptions LevelDBOptions `yaml:"LevelDBOptions"`
|
|
||||||
BoltDBOptions BoltDBOptions `yaml:"BoltDBOptions"`
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -3,6 +3,7 @@ package storage
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ func TestBatchToOperations(t *testing.T) {
|
||||||
{KeyValue: KeyValue{Key: []byte{byte(STStorage), 0x06}, Value: []byte{0x06}}, Exists: true},
|
{KeyValue: KeyValue{Key: []byte{byte(STStorage), 0x06}, Value: []byte{0x06}}, Exists: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
o := []Operation{
|
o := []dboper.Operation{
|
||||||
{State: "Added", Key: []byte{0x01}, Value: []byte{0x01}},
|
{State: "Added", Key: []byte{0x01}, Value: []byte{0x01}},
|
||||||
{State: "Changed", Key: []byte{0x03}, Value: []byte{0x03}},
|
{State: "Changed", Key: []byte{0x03}, Value: []byte{0x03}},
|
||||||
{State: "Deleted", Key: []byte{0x06}},
|
{State: "Deleted", Key: []byte{0x06}},
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -210,7 +211,7 @@ func (e *Executor) InvokeScriptCheckFAULT(t testing.TB, script []byte, signers [
|
||||||
func (e *Executor) CheckHalt(t testing.TB, h util.Uint256, stack ...stackitem.Item) *state.AppExecResult {
|
func (e *Executor) CheckHalt(t testing.TB, h util.Uint256, stack ...stackitem.Item) *state.AppExecResult {
|
||||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
require.Equal(t, vmstate.Halt, aer[0].VMState, aer[0].FaultException)
|
||||||
if len(stack) != 0 {
|
if len(stack) != 0 {
|
||||||
require.Equal(t, stack, aer[0].Stack)
|
require.Equal(t, stack, aer[0].Stack)
|
||||||
}
|
}
|
||||||
|
@ -222,7 +223,7 @@ func (e *Executor) CheckHalt(t testing.TB, h util.Uint256, stack ...stackitem.It
|
||||||
func (e *Executor) CheckFault(t testing.TB, h util.Uint256, s string) {
|
func (e *Executor) CheckFault(t testing.TB, h util.Uint256, s string) {
|
||||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.FaultState, aer[0].VMState)
|
require.Equal(t, vmstate.Fault, aer[0].VMState)
|
||||||
require.True(t, strings.Contains(aer[0].FaultException, s),
|
require.True(t, strings.Contains(aer[0].FaultException, s),
|
||||||
"expected: %s, got: %s", s, aer[0].FaultException)
|
"expected: %s, got: %s", s, aer[0].FaultException)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testin
|
||||||
c.AddNewBlock(t, tx)
|
c.AddNewBlock(t, tx)
|
||||||
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
require.Equal(t, vmstate.Halt, aer[0].VMState, aer[0].FaultException)
|
||||||
if checkResult != nil {
|
if checkResult != nil {
|
||||||
checkResult(t, aer[0].Stack)
|
checkResult(t, aer[0].Stack)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,18 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service serves metrics.
|
// Service serves metrics.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
*http.Server
|
*http.Server
|
||||||
config Config
|
config config.BasicService
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
serviceType string
|
serviceType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config config used for monitoring.
|
|
||||||
type Config struct {
|
|
||||||
Enabled bool `yaml:"Enabled"`
|
|
||||||
Address string `yaml:"Address"`
|
|
||||||
Port string `yaml:"Port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start runs http service with the exposed endpoint on the configured port.
|
// Start runs http service with the exposed endpoint on the configured port.
|
||||||
func (ms *Service) Start() {
|
func (ms *Service) Start() {
|
||||||
if ms.config.Enabled {
|
if ms.config.Enabled {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ import (
|
||||||
type PprofService Service
|
type PprofService Service
|
||||||
|
|
||||||
// NewPprofService creates a new service for gathering pprof metrics.
|
// NewPprofService creates a new service for gathering pprof metrics.
|
||||||
func NewPprofService(cfg Config, log *zap.Logger) *Service {
|
func NewPprofService(cfg config.BasicService, log *zap.Logger) *Service {
|
||||||
if log == nil {
|
if log == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package metrics
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -11,7 +12,7 @@ import (
|
||||||
type PrometheusService Service
|
type PrometheusService Service
|
||||||
|
|
||||||
// NewPrometheusService creates a new service for gathering prometheus metrics.
|
// NewPrometheusService creates a new service for gathering prometheus metrics.
|
||||||
func NewPrometheusService(cfg Config, log *zap.Logger) *Service {
|
func NewPrometheusService(cfg config.BasicService, log *zap.Logger) *Service {
|
||||||
if log == nil {
|
if log == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"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/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -129,7 +129,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
Executions: []state.Execution{
|
Executions: []state.Execution{
|
||||||
{
|
{
|
||||||
Trigger: trigger.Application,
|
Trigger: trigger.Application,
|
||||||
VMState: vm.HaltState,
|
VMState: vmstate.Halt,
|
||||||
GasConsumed: 1,
|
GasConsumed: 1,
|
||||||
Stack: []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1))},
|
Stack: []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1))},
|
||||||
Events: []state.NotificationEvent{},
|
Events: []state.NotificationEvent{},
|
||||||
|
|
|
@ -5,16 +5,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// LedgerAux is a set of methods needed to construct some outputs.
|
|
||||||
LedgerAux interface {
|
|
||||||
BlockHeight() uint32
|
|
||||||
GetHeaderHash(int) util.Uint256
|
|
||||||
}
|
|
||||||
// Block wrapper used for the representation of
|
// Block wrapper used for the representation of
|
||||||
// block.Block / block.Base on the RPC Server.
|
// block.Block / block.Base on the RPC Server.
|
||||||
Block struct {
|
Block struct {
|
||||||
|
@ -31,24 +25,6 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewBlock creates a new Block wrapper.
|
|
||||||
func NewBlock(b *block.Block, chain LedgerAux) Block {
|
|
||||||
res := Block{
|
|
||||||
Block: *b,
|
|
||||||
BlockMetadata: BlockMetadata{
|
|
||||||
Size: io.GetVarSize(b),
|
|
||||||
Confirmations: chain.BlockHeight() - b.Index + 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := chain.GetHeaderHash(int(b.Index) + 1)
|
|
||||||
if !hash.Equals(util.Uint256{}) {
|
|
||||||
res.NextBlockHash = &hash
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
func (b Block) MarshalJSON() ([]byte, error) {
|
func (b Block) MarshalJSON() ([]byte, error) {
|
||||||
output, err := json.Marshal(b.BlockMetadata)
|
output, err := json.Marshal(b.BlockMetadata)
|
||||||
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -18,23 +16,6 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewHeader creates a new Header wrapper.
|
|
||||||
func NewHeader(h *block.Header, chain LedgerAux) Header {
|
|
||||||
res := Header{
|
|
||||||
Header: *h,
|
|
||||||
BlockMetadata: BlockMetadata{
|
|
||||||
Size: io.GetVarSize(h),
|
|
||||||
Confirmations: chain.BlockHeight() - h.Index + 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := chain.GetHeaderHash(int(h.Index) + 1)
|
|
||||||
if !hash.Equals(util.Uint256{}) {
|
|
||||||
res.NextBlockHash = &hash
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
func (h Header) MarshalJSON() ([]byte, error) {
|
func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
output, err := json.Marshal(h.BlockMetadata)
|
output, err := json.Marshal(h.BlockMetadata)
|
||||||
|
|
|
@ -5,67 +5,31 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
|
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Invoke represents a code invocation result and is used by several RPC calls
|
// Invoke represents a code invocation result and is used by several RPC calls
|
||||||
// that invoke functions, scripts and generic bytecode.
|
// that invoke functions, scripts and generic bytecode.
|
||||||
type Invoke struct {
|
type Invoke struct {
|
||||||
State string
|
State string
|
||||||
GasConsumed int64
|
GasConsumed int64
|
||||||
Script []byte
|
Script []byte
|
||||||
Stack []stackitem.Item
|
Stack []stackitem.Item
|
||||||
FaultException string
|
FaultException string
|
||||||
Notifications []state.NotificationEvent
|
Notifications []state.NotificationEvent
|
||||||
Transaction *transaction.Transaction
|
Transaction *transaction.Transaction
|
||||||
Diagnostics *InvokeDiag
|
Diagnostics *InvokeDiag
|
||||||
maxIteratorResultItems int
|
Session uuid.UUID
|
||||||
Session uuid.UUID
|
|
||||||
finalize func()
|
|
||||||
registerIterator RegisterIterator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterIterator is a callback used to register new iterator on the server side.
|
|
||||||
type RegisterIterator func(sessionID string, item stackitem.Item, id int, finalize func()) (uuid.UUID, error)
|
|
||||||
|
|
||||||
// InvokeDiag is an additional diagnostic data for invocation.
|
// InvokeDiag is an additional diagnostic data for invocation.
|
||||||
type InvokeDiag struct {
|
type InvokeDiag struct {
|
||||||
Changes []storage.Operation `json:"storagechanges"`
|
Changes []dboper.Operation `json:"storagechanges"`
|
||||||
Invocations []*vm.InvocationTree `json:"invokedcontracts"`
|
Invocations []*invocations.Tree `json:"invokedcontracts"`
|
||||||
}
|
|
||||||
|
|
||||||
// NewInvoke returns a new Invoke structure with the given fields set.
|
|
||||||
func NewInvoke(ic *interop.Context, script []byte, faultException string, registerIterator RegisterIterator, maxIteratorResultItems int) *Invoke {
|
|
||||||
var diag *InvokeDiag
|
|
||||||
tree := ic.VM.GetInvocationTree()
|
|
||||||
if tree != nil {
|
|
||||||
diag = &InvokeDiag{
|
|
||||||
Invocations: tree.Calls,
|
|
||||||
Changes: storage.BatchToOperations(ic.DAO.GetBatch()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifications := ic.Notifications
|
|
||||||
if notifications == nil {
|
|
||||||
notifications = make([]state.NotificationEvent, 0)
|
|
||||||
}
|
|
||||||
return &Invoke{
|
|
||||||
State: ic.VM.State().String(),
|
|
||||||
GasConsumed: ic.VM.GasConsumed(),
|
|
||||||
Script: script,
|
|
||||||
Stack: ic.VM.Estack().ToArray(),
|
|
||||||
FaultException: faultException,
|
|
||||||
Notifications: notifications,
|
|
||||||
Diagnostics: diag,
|
|
||||||
finalize: ic.Finalize,
|
|
||||||
maxIteratorResultItems: maxIteratorResultItems,
|
|
||||||
registerIterator: registerIterator,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type invokeAux struct {
|
type invokeAux struct {
|
||||||
|
@ -106,85 +70,85 @@ type Iterator struct {
|
||||||
Truncated bool
|
Truncated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize releases resources occupied by Iterators created at the script invocation.
|
// MarshalJSON implements the json.Marshaler.
|
||||||
// This method will be called automatically on Invoke marshalling or by the Server's
|
func (r Iterator) MarshalJSON() ([]byte, error) {
|
||||||
// sessions handler.
|
var iaux iteratorAux
|
||||||
func (r *Invoke) Finalize() {
|
iaux.Type = stackitem.InteropT.String()
|
||||||
if r.finalize != nil {
|
if r.ID != nil {
|
||||||
r.finalize()
|
iaux.Interface = iteratorInterfaceName
|
||||||
|
iaux.ID = r.ID.String()
|
||||||
|
} else {
|
||||||
|
value := make([]json.RawMessage, len(r.Values))
|
||||||
|
for i := range r.Values {
|
||||||
|
var err error
|
||||||
|
value[i], err = stackitem.ToJSONWithTypes(r.Values[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iaux.Value = value
|
||||||
|
iaux.Truncated = r.Truncated
|
||||||
}
|
}
|
||||||
|
return json.Marshal(iaux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler.
|
||||||
|
func (r *Iterator) UnmarshalJSON(data []byte) error {
|
||||||
|
iteratorAux := new(iteratorAux)
|
||||||
|
err := json.Unmarshal(data, iteratorAux)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(iteratorAux.Interface) != 0 {
|
||||||
|
if iteratorAux.Interface != iteratorInterfaceName {
|
||||||
|
return fmt.Errorf("unknown InteropInterface: %s", iteratorAux.Interface)
|
||||||
|
}
|
||||||
|
var iID uuid.UUID
|
||||||
|
iID, err = uuid.Parse(iteratorAux.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal iterator ID: %w", err)
|
||||||
|
}
|
||||||
|
r.ID = &iID
|
||||||
|
} else {
|
||||||
|
r.Values = make([]stackitem.Item, len(iteratorAux.Value))
|
||||||
|
for j := range r.Values {
|
||||||
|
r.Values[j], err = stackitem.FromJSONWithTypes(iteratorAux.Value[j])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal iterator values: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Truncated = iteratorAux.Truncated
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler.
|
// MarshalJSON implements the json.Marshaler.
|
||||||
func (r Invoke) MarshalJSON() ([]byte, error) {
|
func (r Invoke) MarshalJSON() ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
st json.RawMessage
|
st json.RawMessage
|
||||||
err error
|
err error
|
||||||
faultSep string
|
faultSep string
|
||||||
arr = make([]json.RawMessage, len(r.Stack))
|
arr = make([]json.RawMessage, len(r.Stack))
|
||||||
sessionsEnabled = r.registerIterator != nil
|
|
||||||
sessionID string
|
|
||||||
)
|
)
|
||||||
if len(r.FaultException) != 0 {
|
if len(r.FaultException) != 0 {
|
||||||
faultSep = " / "
|
faultSep = " / "
|
||||||
}
|
}
|
||||||
arrloop:
|
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
var data []byte
|
var data []byte
|
||||||
if (r.Stack[i].Type() == stackitem.InteropT) && iterator.IsIterator(r.Stack[i]) {
|
|
||||||
if sessionsEnabled {
|
iter, ok := r.Stack[i].Value().(Iterator)
|
||||||
if sessionID == "" {
|
if (r.Stack[i].Type() == stackitem.InteropT) && ok {
|
||||||
sessionID = uuid.NewString()
|
data, err = json.Marshal(iter)
|
||||||
}
|
|
||||||
iteratorID, err := r.registerIterator(sessionID, r.Stack[i], i, r.finalize)
|
|
||||||
if err != nil {
|
|
||||||
// Call finalizer immediately, there can't be race between server and marshaller because session wasn't added to server's session pool.
|
|
||||||
r.Finalize()
|
|
||||||
return nil, fmt.Errorf("failed to register iterator session: %w", err)
|
|
||||||
}
|
|
||||||
data, err = json.Marshal(iteratorAux{
|
|
||||||
Type: stackitem.InteropT.String(),
|
|
||||||
Interface: iteratorInterfaceName,
|
|
||||||
ID: iteratorID.String(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
r.FaultException += fmt.Sprintf("%sjson error: failed to marshal iterator: %v", faultSep, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iteratorValues, truncated := iterator.ValuesTruncated(r.Stack[i], r.maxIteratorResultItems)
|
|
||||||
value := make([]json.RawMessage, len(iteratorValues))
|
|
||||||
for j := range iteratorValues {
|
|
||||||
value[j], err = stackitem.ToJSONWithTypes(iteratorValues[j])
|
|
||||||
if err != nil {
|
|
||||||
r.FaultException += fmt.Sprintf("%sjson error: %v", faultSep, err)
|
|
||||||
break arrloop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data, err = json.Marshal(iteratorAux{
|
|
||||||
Type: stackitem.InteropT.String(),
|
|
||||||
Value: value,
|
|
||||||
Truncated: truncated,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
r.FaultException += fmt.Sprintf("%sjson error: %v", faultSep, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
data, err = stackitem.ToJSONWithTypes(r.Stack[i])
|
data, err = stackitem.ToJSONWithTypes(r.Stack[i])
|
||||||
if err != nil {
|
}
|
||||||
r.FaultException += fmt.Sprintf("%sjson error: %v", faultSep, err)
|
if err != nil {
|
||||||
break
|
r.FaultException += fmt.Sprintf("%sjson error: %v", faultSep, err)
|
||||||
}
|
break
|
||||||
}
|
}
|
||||||
arr[i] = data
|
arr[i] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sessionsEnabled || sessionID == "" {
|
|
||||||
// Call finalizer manually if iterators are disabled or there's no unnested iterators on estack.
|
|
||||||
defer r.Finalize()
|
|
||||||
}
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
st, err = json.Marshal(arr)
|
st, err = json.Marshal(arr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -195,6 +159,10 @@ arrloop:
|
||||||
if r.Transaction != nil {
|
if r.Transaction != nil {
|
||||||
txbytes = r.Transaction.Bytes()
|
txbytes = r.Transaction.Bytes()
|
||||||
}
|
}
|
||||||
|
var sessionID string
|
||||||
|
if r.Session != (uuid.UUID{}) {
|
||||||
|
sessionID = r.Session.String()
|
||||||
|
}
|
||||||
aux := &invokeAux{
|
aux := &invokeAux{
|
||||||
GasConsumed: r.GasConsumed,
|
GasConsumed: r.GasConsumed,
|
||||||
Script: r.Script,
|
Script: r.Script,
|
||||||
|
@ -233,41 +201,12 @@ func (r *Invoke) UnmarshalJSON(data []byte) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if st[i].Type() == stackitem.InteropT {
|
if st[i].Type() == stackitem.InteropT {
|
||||||
iteratorAux := new(iteratorAux)
|
var iter = Iterator{}
|
||||||
if json.Unmarshal(arr[i], iteratorAux) == nil {
|
err = json.Unmarshal(arr[i], &iter)
|
||||||
if len(iteratorAux.Interface) != 0 {
|
if err != nil {
|
||||||
if iteratorAux.Interface != iteratorInterfaceName {
|
break
|
||||||
err = fmt.Errorf("unknown InteropInterface: %s", iteratorAux.Interface)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var iID uuid.UUID
|
|
||||||
iID, err = uuid.Parse(iteratorAux.ID) // iteratorAux.ID is always non-empty, see https://github.com/neo-project/neo-modules/pull/715#discussion_r897635424.
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("failed to unmarshal iterator ID: %w", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// It's impossible to restore initial iterator type; also iterator is almost
|
|
||||||
// useless outside the VM, thus let's replace it with a special structure.
|
|
||||||
st[i] = stackitem.NewInterop(Iterator{
|
|
||||||
ID: &iID,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
iteratorValues := make([]stackitem.Item, len(iteratorAux.Value))
|
|
||||||
for j := range iteratorValues {
|
|
||||||
iteratorValues[j], err = stackitem.FromJSONWithTypes(iteratorAux.Value[j])
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("failed to unmarshal iterator values: %w", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// It's impossible to restore initial iterator type; also iterator is almost
|
|
||||||
// useless outside the VM, thus let's replace it with a special structure.
|
|
||||||
st[i] = stackitem.NewInterop(Iterator{
|
|
||||||
Values: iteratorValues,
|
|
||||||
Truncated: iteratorAux.Truncated,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
st[i] = stackitem.NewInterop(iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -25,25 +23,6 @@ type TransactionMetadata struct {
|
||||||
VMState string `json:"vmstate,omitempty"`
|
VMState string `json:"vmstate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTransactionOutputRaw returns a new ransactionOutputRaw object.
|
|
||||||
func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, appExecResult *state.AppExecResult, chain LedgerAux) TransactionOutputRaw {
|
|
||||||
result := TransactionOutputRaw{
|
|
||||||
Transaction: *tx,
|
|
||||||
}
|
|
||||||
if header == nil {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
// confirmations formula
|
|
||||||
confirmations := int(chain.BlockHeight() - header.Index + 1)
|
|
||||||
result.TransactionMetadata = TransactionMetadata{
|
|
||||||
Blockhash: header.Hash(),
|
|
||||||
Confirmations: confirmations,
|
|
||||||
Timestamp: header.Timestamp,
|
|
||||||
VMState: appExecResult.VMState.String(),
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) {
|
func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) {
|
||||||
output, err := json.Marshal(t.TransactionMetadata)
|
output, err := json.Marshal(t.TransactionMetadata)
|
||||||
|
|
|
@ -35,10 +35,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"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/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -648,7 +648,7 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(appLogs))
|
require.Equal(t, 1, len(appLogs))
|
||||||
appLog := appLogs[0]
|
appLog := appLogs[0]
|
||||||
require.Equal(t, vm.HaltState, appLog.VMState)
|
require.Equal(t, vmstate.Halt, appLog.VMState)
|
||||||
require.Equal(t, appLog.GasConsumed, req.FallbackTransaction.SystemFee)
|
require.Equal(t, appLog.GasConsumed, req.FallbackTransaction.SystemFee)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1282,7 +1282,7 @@ func TestClient_InvokeAndPackIteratorResults(t *testing.T) {
|
||||||
t.Run("default max items constraint", func(t *testing.T) {
|
t.Run("default max items constraint", func(t *testing.T) {
|
||||||
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil)
|
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState.String(), res.State)
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
require.Equal(t, stackitem.ArrayT, res.Stack[0].Type())
|
require.Equal(t, stackitem.ArrayT, res.Stack[0].Type())
|
||||||
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||||
|
@ -1298,7 +1298,7 @@ func TestClient_InvokeAndPackIteratorResults(t *testing.T) {
|
||||||
max := 123
|
max := 123
|
||||||
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil, max)
|
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil, max)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState.String(), res.State)
|
require.Equal(t, vmstate.Halt.String(), res.State)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
require.Equal(t, 1, len(res.Stack))
|
||||||
require.Equal(t, stackitem.ArrayT, res.Stack[0].Type())
|
require.Equal(t, stackitem.ArrayT, res.Stack[0].Type())
|
||||||
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
@ -101,29 +102,14 @@ type (
|
||||||
// or from historic MPT-based invocation. In the second case, iteratorIdentifiers are supposed
|
// or from historic MPT-based invocation. In the second case, iteratorIdentifiers are supposed
|
||||||
// to be filled during the first `traverseiterator` call using corresponding params.
|
// to be filled during the first `traverseiterator` call using corresponding params.
|
||||||
iteratorIdentifiers []*iteratorIdentifier
|
iteratorIdentifiers []*iteratorIdentifier
|
||||||
// params stores invocation params for historic MPT-based iterator traversing. It is nil in case
|
timer *time.Timer
|
||||||
// of default non-MPT-based sessions mechanism enabled.
|
finalize func()
|
||||||
params *invocationParams
|
|
||||||
timer *time.Timer
|
|
||||||
finalize func()
|
|
||||||
}
|
}
|
||||||
// iteratorIdentifier represents Iterator on the server side, holding iterator ID, Iterator stackitem
|
// iteratorIdentifier represents Iterator on the server side, holding iterator ID and Iterator stackitem.
|
||||||
// and iterator index on stack.
|
|
||||||
iteratorIdentifier struct {
|
iteratorIdentifier struct {
|
||||||
ID string
|
ID string
|
||||||
// Item represents Iterator stackitem. It is nil if SessionBackedByMPT is set to true and no `traverseiterator`
|
// Item represents Iterator stackitem.
|
||||||
// call was called for the corresponding session.
|
|
||||||
Item stackitem.Item
|
Item stackitem.Item
|
||||||
// StackIndex represents Iterator stackitem index on the stack. It can be used only for SessionBackedByMPT configuration.
|
|
||||||
StackIndex int
|
|
||||||
}
|
|
||||||
// invocationParams is a set of parameters used for invoke* calls.
|
|
||||||
invocationParams struct {
|
|
||||||
Trigger trigger.Type
|
|
||||||
Script []byte
|
|
||||||
ContractScriptHash util.Uint160
|
|
||||||
Transaction *transaction.Transaction
|
|
||||||
NextBlockHeight uint32
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -350,9 +336,7 @@ func (s *Server) Shutdown() {
|
||||||
for _, session := range s.sessions {
|
for _, session := range s.sessions {
|
||||||
// Concurrent iterator traversal may still be in process, thus need to protect iteratorIdentifiers access.
|
// Concurrent iterator traversal may still be in process, thus need to protect iteratorIdentifiers access.
|
||||||
session.iteratorsLock.Lock()
|
session.iteratorsLock.Lock()
|
||||||
if session.finalize != nil {
|
session.finalize()
|
||||||
session.finalize()
|
|
||||||
}
|
|
||||||
if !session.timer.Stop() {
|
if !session.timer.Stop() {
|
||||||
<-session.timer.C
|
<-session.timer.C
|
||||||
}
|
}
|
||||||
|
@ -582,6 +566,19 @@ func (s *Server) blockHashFromParam(param *params.Param) (util.Uint256, *respons
|
||||||
return hash, nil
|
return hash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) fillBlockMetadata(obj io.Serializable, h *block.Header) result.BlockMetadata {
|
||||||
|
res := result.BlockMetadata{
|
||||||
|
Size: io.GetVarSize(obj), // obj can be a Block or a Header.
|
||||||
|
Confirmations: s.chain.BlockHeight() - h.Index + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := s.chain.GetHeaderHash(int(h.Index) + 1)
|
||||||
|
if !hash.Equals(util.Uint256{}) {
|
||||||
|
res.NextBlockHash = &hash
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) getBlock(reqParams params.Params) (interface{}, *response.Error) {
|
func (s *Server) getBlock(reqParams params.Params) (interface{}, *response.Error) {
|
||||||
param := reqParams.Value(0)
|
param := reqParams.Value(0)
|
||||||
hash, respErr := s.blockHashFromParam(param)
|
hash, respErr := s.blockHashFromParam(param)
|
||||||
|
@ -595,7 +592,11 @@ func (s *Server) getBlock(reqParams params.Params) (interface{}, *response.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, _ := reqParams.Value(1).GetBoolean(); v {
|
if v, _ := reqParams.Value(1).GetBoolean(); v {
|
||||||
return result.NewBlock(block, s.chain), nil
|
res := result.Block{
|
||||||
|
Block: *block,
|
||||||
|
BlockMetadata: s.fillBlockMetadata(block, &block.Header),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
writer := io.NewBufBinWriter()
|
writer := io.NewBufBinWriter()
|
||||||
block.EncodeBinary(writer.BinWriter)
|
block.EncodeBinary(writer.BinWriter)
|
||||||
|
@ -829,7 +830,7 @@ contract_loop:
|
||||||
curAsset := &bs.Balances[len(bs.Balances)-1]
|
curAsset := &bs.Balances[len(bs.Balances)-1]
|
||||||
for i := range toks {
|
for i := range toks {
|
||||||
id, err := toks[i].TryBytes()
|
id, err := toks[i].TryBytes()
|
||||||
if err != nil || len(id) > storage.MaxStorageKeyLen {
|
if err != nil || len(id) > limits.MaxStorageKeyLen {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var amount = "1"
|
var amount = "1"
|
||||||
|
@ -1542,8 +1543,11 @@ func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *respo
|
||||||
return nil, response.ErrUnknownTransaction
|
return nil, response.ErrUnknownTransaction
|
||||||
}
|
}
|
||||||
if v, _ := reqParams.Value(1).GetBoolean(); v {
|
if v, _ := reqParams.Value(1).GetBoolean(); v {
|
||||||
if height == math.MaxUint32 {
|
res := result.TransactionOutputRaw{
|
||||||
return result.NewTransactionOutputRaw(tx, nil, nil, s.chain), nil
|
Transaction: *tx,
|
||||||
|
}
|
||||||
|
if height == math.MaxUint32 { // Mempooled transaction.
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
_header := s.chain.GetHeaderHash(int(height))
|
_header := s.chain.GetHeaderHash(int(height))
|
||||||
header, err := s.chain.GetHeader(_header)
|
header, err := s.chain.GetHeader(_header)
|
||||||
|
@ -1557,7 +1561,13 @@ func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *respo
|
||||||
if len(aers) == 0 {
|
if len(aers) == 0 {
|
||||||
return nil, response.NewRPCError("Inconsistent application log", "application log for the transaction is empty")
|
return nil, response.NewRPCError("Inconsistent application log", "application log for the transaction is empty")
|
||||||
}
|
}
|
||||||
return result.NewTransactionOutputRaw(tx, header, &aers[0], s.chain), nil
|
res.TransactionMetadata = result.TransactionMetadata{
|
||||||
|
Blockhash: header.Hash(),
|
||||||
|
Confirmations: int(s.chain.BlockHeight() - header.Index + 1),
|
||||||
|
Timestamp: header.Timestamp,
|
||||||
|
VMState: aers[0].VMState.String(),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
return tx.Bytes(), nil
|
return tx.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
@ -1630,7 +1640,11 @@ func (s *Server) getBlockHeader(reqParams params.Params) (interface{}, *response
|
||||||
}
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
return result.NewHeader(h, s.chain), nil
|
res := result.Header{
|
||||||
|
Header: *h,
|
||||||
|
BlockMetadata: s.fillBlockMetadata(h, h),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := io.NewBufBinWriter()
|
buf := io.NewBufBinWriter()
|
||||||
|
@ -2006,64 +2020,117 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
|
||||||
if err != nil {
|
if err != nil {
|
||||||
faultException = err.Error()
|
faultException = err.Error()
|
||||||
}
|
}
|
||||||
var registerIterator result.RegisterIterator
|
items := ic.VM.Estack().ToArray()
|
||||||
if s.config.SessionEnabled {
|
sess := s.postProcessExecStack(items)
|
||||||
registerIterator = func(sessionID string, item stackitem.Item, stackIndex int, finalize func()) (uuid.UUID, error) {
|
var id uuid.UUID
|
||||||
iterID := uuid.New()
|
|
||||||
|
if sess != nil {
|
||||||
|
// b == nil only when we're not using MPT-backed storage, therefore
|
||||||
|
// the second attempt won't stop here.
|
||||||
|
if s.config.SessionBackedByMPT && b == nil {
|
||||||
|
ic.Finalize()
|
||||||
|
b, err = s.getFakeNextBlock(ic.Block.Index)
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.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)
|
||||||
|
}
|
||||||
|
id = uuid.New()
|
||||||
|
sessionID := id.String()
|
||||||
|
sess.finalize = ic.Finalize
|
||||||
|
sess.timer = time.AfterFunc(time.Second*time.Duration(s.config.SessionExpirationTime), func() {
|
||||||
s.sessionsLock.Lock()
|
s.sessionsLock.Lock()
|
||||||
|
defer s.sessionsLock.Unlock()
|
||||||
|
if len(s.sessions) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
sess, ok := s.sessions[sessionID]
|
sess, ok := s.sessions[sessionID]
|
||||||
if !ok {
|
if !ok {
|
||||||
if len(s.sessions) >= s.config.SessionPoolSize {
|
return
|
||||||
return uuid.UUID{}, errors.New("max capacity reached")
|
|
||||||
}
|
|
||||||
timer := time.AfterFunc(time.Second*time.Duration(s.config.SessionExpirationTime), func() {
|
|
||||||
s.sessionsLock.Lock()
|
|
||||||
defer s.sessionsLock.Unlock()
|
|
||||||
if len(s.sessions) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sess, ok := s.sessions[sessionID]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sess.iteratorsLock.Lock()
|
|
||||||
if sess.finalize != nil {
|
|
||||||
sess.finalize()
|
|
||||||
}
|
|
||||||
delete(s.sessions, sessionID)
|
|
||||||
sess.iteratorsLock.Unlock()
|
|
||||||
})
|
|
||||||
sess = &session{
|
|
||||||
finalize: finalize,
|
|
||||||
timer: timer,
|
|
||||||
}
|
|
||||||
if s.config.SessionBackedByMPT {
|
|
||||||
sess.params = &invocationParams{
|
|
||||||
Trigger: t,
|
|
||||||
Script: script,
|
|
||||||
ContractScriptHash: contractScriptHash,
|
|
||||||
Transaction: tx,
|
|
||||||
NextBlockHeight: ic.Block.Index,
|
|
||||||
}
|
|
||||||
// Call finalizer manually if MPT-based iterator sessions are enabled. If disabled, then register finalizator.
|
|
||||||
if finalize != nil {
|
|
||||||
finalize()
|
|
||||||
sess.finalize = nil
|
|
||||||
}
|
|
||||||
item = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sess.iteratorIdentifiers = append(sess.iteratorIdentifiers, &iteratorIdentifier{
|
sess.iteratorsLock.Lock()
|
||||||
ID: iterID.String(),
|
sess.finalize()
|
||||||
Item: item,
|
delete(s.sessions, sessionID)
|
||||||
StackIndex: stackIndex,
|
sess.iteratorsLock.Unlock()
|
||||||
})
|
})
|
||||||
s.sessions[sessionID] = sess
|
s.sessionsLock.Lock()
|
||||||
|
if len(s.sessions) >= s.config.SessionPoolSize {
|
||||||
|
ic.Finalize()
|
||||||
s.sessionsLock.Unlock()
|
s.sessionsLock.Unlock()
|
||||||
return iterID, nil
|
return nil, response.NewInternalServerError("max session capacity reached")
|
||||||
|
}
|
||||||
|
s.sessions[sessionID] = sess
|
||||||
|
s.sessionsLock.Unlock()
|
||||||
|
} else {
|
||||||
|
ic.Finalize()
|
||||||
|
}
|
||||||
|
var diag *result.InvokeDiag
|
||||||
|
tree := ic.VM.GetInvocationTree()
|
||||||
|
if tree != nil {
|
||||||
|
diag = &result.InvokeDiag{
|
||||||
|
Invocations: tree.Calls,
|
||||||
|
Changes: storage.BatchToOperations(ic.DAO.GetBatch()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.NewInvoke(ic, script, faultException, registerIterator, s.config.MaxIteratorResultItems), nil
|
notifications := ic.Notifications
|
||||||
|
if notifications == nil {
|
||||||
|
notifications = make([]state.NotificationEvent, 0)
|
||||||
|
}
|
||||||
|
res := &result.Invoke{
|
||||||
|
State: ic.VM.State().String(),
|
||||||
|
GasConsumed: ic.VM.GasConsumed(),
|
||||||
|
Script: script,
|
||||||
|
Stack: items,
|
||||||
|
FaultException: faultException,
|
||||||
|
Notifications: notifications,
|
||||||
|
Diagnostics: diag,
|
||||||
|
Session: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// postProcessExecStack changes iterator interop items according to the server configuration.
|
||||||
|
// It does modifications in-place, but it returns a session if any iterator was registered.
|
||||||
|
func (s *Server) postProcessExecStack(stack []stackitem.Item) *session {
|
||||||
|
var sess session
|
||||||
|
|
||||||
|
for i, v := range stack {
|
||||||
|
var id uuid.UUID
|
||||||
|
|
||||||
|
stack[i], id = s.registerOrDumpIterator(v)
|
||||||
|
if id != (uuid.UUID{}) {
|
||||||
|
sess.iteratorIdentifiers = append(sess.iteratorIdentifiers, &iteratorIdentifier{
|
||||||
|
ID: id.String(),
|
||||||
|
Item: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sess.iteratorIdentifiers) != 0 {
|
||||||
|
return &sess
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerOrDumpIterator changes iterator interop stack items into result.Iterator
|
||||||
|
// interop stack items and returns a uuid for it if sessions are enabled. All the other stack
|
||||||
|
// items are not changed.
|
||||||
|
func (s *Server) registerOrDumpIterator(item stackitem.Item) (stackitem.Item, uuid.UUID) {
|
||||||
|
var iterID uuid.UUID
|
||||||
|
|
||||||
|
if (item.Type() != stackitem.InteropT) || !iterator.IsIterator(item) {
|
||||||
|
return item, iterID
|
||||||
|
}
|
||||||
|
var resIterator result.Iterator
|
||||||
|
|
||||||
|
if s.config.SessionEnabled {
|
||||||
|
iterID = uuid.New()
|
||||||
|
resIterator.ID = &iterID
|
||||||
|
} else {
|
||||||
|
resIterator.Values, resIterator.Truncated = iterator.ValuesTruncated(item, s.config.MaxIteratorResultItems)
|
||||||
|
}
|
||||||
|
return stackitem.NewInterop(resIterator), iterID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) traverseIterator(reqParams params.Params) (interface{}, *response.Error) {
|
func (s *Server) traverseIterator(reqParams params.Params) (interface{}, *response.Error) {
|
||||||
|
@ -2102,43 +2169,11 @@ func (s *Server) traverseIterator(reqParams params.Params) (interface{}, *respon
|
||||||
s.sessionsLock.Unlock()
|
s.sessionsLock.Unlock()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
iIDStr = iID.String()
|
iIDStr = iID.String()
|
||||||
iVals []stackitem.Item
|
iVals []stackitem.Item
|
||||||
respErr *response.Error
|
|
||||||
)
|
)
|
||||||
for _, it := range session.iteratorIdentifiers {
|
for _, it := range session.iteratorIdentifiers {
|
||||||
if iIDStr == it.ID {
|
if iIDStr == it.ID {
|
||||||
// If SessionBackedByMPT is enabled, then use MPT-backed historic call to retrieve and traverse iterator.
|
|
||||||
// Otherwise, iterator stackitem is ready and can be used.
|
|
||||||
if s.config.SessionBackedByMPT && it.Item == nil {
|
|
||||||
var (
|
|
||||||
b *block.Block
|
|
||||||
ic *interop.Context
|
|
||||||
)
|
|
||||||
b, err = s.getFakeNextBlock(session.params.NextBlockHeight)
|
|
||||||
if err != nil {
|
|
||||||
session.iteratorsLock.Unlock()
|
|
||||||
return nil, response.NewInternalServerError(fmt.Sprintf("unable to prepare block for historic call: %s", err))
|
|
||||||
}
|
|
||||||
ic, respErr = s.prepareInvocationContext(session.params.Trigger, session.params.Script, session.params.ContractScriptHash, session.params.Transaction, b, false)
|
|
||||||
if respErr != nil {
|
|
||||||
session.iteratorsLock.Unlock()
|
|
||||||
return nil, respErr
|
|
||||||
}
|
|
||||||
_ = ic.VM.Run() // No error check because FAULTed invocations could also contain iterator on stack.
|
|
||||||
stack := ic.VM.Estack().ToArray()
|
|
||||||
|
|
||||||
// Fill in the whole set of iterators for the current session in order not to repeat test invocation one more time for other session iterators.
|
|
||||||
for _, itID := range session.iteratorIdentifiers {
|
|
||||||
j := itID.StackIndex
|
|
||||||
if (stack[j].Type() != stackitem.InteropT) || !iterator.IsIterator(stack[j]) {
|
|
||||||
session.iteratorsLock.Unlock()
|
|
||||||
return nil, response.NewInternalServerError(fmt.Sprintf("inconsistent historic call result: expected %s, got %s at stack position #%d", stackitem.InteropT, stack[j].Type(), j))
|
|
||||||
}
|
|
||||||
session.iteratorIdentifiers[j].Item = stack[j]
|
|
||||||
}
|
|
||||||
session.finalize = ic.Finalize
|
|
||||||
}
|
|
||||||
iVals = iterator.Values(it.Item, count)
|
iVals = iterator.Values(it.Item, count)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -2171,9 +2206,7 @@ func (s *Server) terminateSession(reqParams params.Params) (interface{}, *respon
|
||||||
// Iterators access Seek channel under the hood; finalizer closes this channel, thus,
|
// Iterators access Seek channel under the hood; finalizer closes this channel, thus,
|
||||||
// we need to perform finalisation under iteratorsLock.
|
// we need to perform finalisation under iteratorsLock.
|
||||||
session.iteratorsLock.Lock()
|
session.iteratorsLock.Lock()
|
||||||
if session.finalize != nil {
|
session.finalize()
|
||||||
session.finalize()
|
|
||||||
}
|
|
||||||
if !session.timer.Stop() {
|
if !session.timer.Stop() {
|
||||||
<-session.timer.C
|
<-session.timer.C
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -41,10 +41,11 @@ import (
|
||||||
rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
|
rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
|
||||||
"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/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -102,7 +103,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
assert.Equal(t, expectedTxHash, res.Container)
|
assert.Equal(t, expectedTxHash, res.Container)
|
||||||
assert.Equal(t, trigger.Application, res.Executions[0].Trigger)
|
assert.Equal(t, trigger.Application, res.Executions[0].Trigger)
|
||||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -116,7 +117,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
assert.Equal(t, 2, len(res.Executions))
|
assert.Equal(t, 2, len(res.Executions))
|
||||||
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||||
assert.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
|
assert.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
|
||||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -129,7 +130,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger)
|
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger)
|
||||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -142,7 +143,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -925,7 +926,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
assert.Equal(t, "HALT", res.State)
|
assert.Equal(t, "HALT", res.State)
|
||||||
assert.Equal(t, []stackitem.Item{stackitem.Make(true)}, res.Stack)
|
assert.Equal(t, []stackitem.Item{stackitem.Make(true)}, res.Stack)
|
||||||
assert.NotEqual(t, 0, res.GasConsumed)
|
assert.NotEqual(t, 0, res.GasConsumed)
|
||||||
chg := []storage.Operation{{
|
chg := []dboper.Operation{{
|
||||||
State: "Changed",
|
State: "Changed",
|
||||||
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
|
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
|
||||||
Value: []byte{0x70, 0xd9, 0x59, 0x9d, 0x51, 0x79, 0x12},
|
Value: []byte{0x70, 0xd9, 0x59, 0x9d, 0x51, 0x79, 0x12},
|
||||||
|
@ -960,13 +961,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
|
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
|
||||||
Notifications: []state.NotificationEvent{},
|
Notifications: []state.NotificationEvent{},
|
||||||
Diagnostics: &result.InvokeDiag{
|
Diagnostics: &result.InvokeDiag{
|
||||||
Changes: []storage.Operation{},
|
Changes: []dboper.Operation{},
|
||||||
Invocations: []*vm.InvocationTree{{
|
Invocations: []*invocations.Tree{{
|
||||||
Current: hash.Hash160(script),
|
Current: hash.Hash160(script),
|
||||||
Calls: []*vm.InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: nnsHash,
|
Current: nnsHash,
|
||||||
Calls: []*vm.InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: stdHash,
|
Current: stdHash,
|
||||||
},
|
},
|
||||||
|
@ -1073,13 +1074,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
|
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
|
||||||
Notifications: []state.NotificationEvent{},
|
Notifications: []state.NotificationEvent{},
|
||||||
Diagnostics: &result.InvokeDiag{
|
Diagnostics: &result.InvokeDiag{
|
||||||
Changes: []storage.Operation{},
|
Changes: []dboper.Operation{},
|
||||||
Invocations: []*vm.InvocationTree{{
|
Invocations: []*invocations.Tree{{
|
||||||
Current: hash.Hash160(script),
|
Current: hash.Hash160(script),
|
||||||
Calls: []*vm.InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: nnsHash,
|
Current: nnsHash,
|
||||||
Calls: []*vm.InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: stdHash,
|
Current: stdHash,
|
||||||
},
|
},
|
||||||
|
@ -1165,8 +1166,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
FaultException: "at instruction 0 (ROT): too big index",
|
FaultException: "at instruction 0 (ROT): too big index",
|
||||||
Notifications: []state.NotificationEvent{},
|
Notifications: []state.NotificationEvent{},
|
||||||
Diagnostics: &result.InvokeDiag{
|
Diagnostics: &result.InvokeDiag{
|
||||||
Changes: []storage.Operation{},
|
Changes: []dboper.Operation{},
|
||||||
Invocations: []*vm.InvocationTree{{
|
Invocations: []*invocations.Tree{{
|
||||||
Current: hash.Hash160(script),
|
Current: hash.Hash160(script),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
@ -1276,8 +1277,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
FaultException: "at instruction 0 (ROT): too big index",
|
FaultException: "at instruction 0 (ROT): too big index",
|
||||||
Notifications: []state.NotificationEvent{},
|
Notifications: []state.NotificationEvent{},
|
||||||
Diagnostics: &result.InvokeDiag{
|
Diagnostics: &result.InvokeDiag{
|
||||||
Changes: []storage.Operation{},
|
Changes: []dboper.Operation{},
|
||||||
Invocations: []*vm.InvocationTree{{
|
Invocations: []*invocations.Tree{{
|
||||||
Current: hash.Hash160(script),
|
Current: hash.Hash160(script),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
@ -1966,9 +1967,9 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
require.NoError(t, json.Unmarshal(data, &res))
|
require.NoError(t, json.Unmarshal(data, &res))
|
||||||
require.Equal(t, 2, len(res.Executions))
|
require.Equal(t, 2, len(res.Executions))
|
||||||
require.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
require.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||||
require.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
require.Equal(t, vmstate.Halt, res.Executions[0].VMState)
|
||||||
require.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
|
require.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
|
||||||
require.Equal(t, vm.HaltState, res.Executions[1].VMState)
|
require.Equal(t, vmstate.Halt, res.Executions[1].VMState)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("submit", func(t *testing.T) {
|
t.Run("submit", func(t *testing.T) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
@ -53,7 +54,7 @@ type Context struct {
|
||||||
// NEF represents a NEF file for the current contract.
|
// NEF represents a NEF file for the current contract.
|
||||||
NEF *nef.File
|
NEF *nef.File
|
||||||
// invTree is an invocation tree (or branch of it) for this context.
|
// invTree is an invocation tree (or branch of it) for this context.
|
||||||
invTree *InvocationTree
|
invTree *invocations.Tree
|
||||||
// onUnload is a callback that should be called after current context unloading
|
// onUnload is a callback that should be called after current context unloading
|
||||||
// if no exception occurs.
|
// if no exception occurs.
|
||||||
onUnload ContextUnloadCallback
|
onUnload ContextUnloadCallback
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InvocationTree represents a tree with script hashes; when traversing it,
|
|
||||||
// you can see how contracts called each other.
|
|
||||||
type InvocationTree struct {
|
|
||||||
Current util.Uint160 `json:"hash"`
|
|
||||||
Calls []*InvocationTree `json:"call,omitempty"`
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -36,13 +37,13 @@ func TestInvocationTree(t *testing.T) {
|
||||||
topHash := v.Context().ScriptHash()
|
topHash := v.Context().ScriptHash()
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
|
|
||||||
res := &InvocationTree{
|
res := &invocations.Tree{
|
||||||
Calls: []*InvocationTree{{
|
Calls: []*invocations.Tree{{
|
||||||
Current: topHash,
|
Current: topHash,
|
||||||
Calls: []*InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: util.Uint160{1},
|
Current: util.Uint160{1},
|
||||||
Calls: []*InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: util.Uint160{2},
|
Current: util.Uint160{2},
|
||||||
},
|
},
|
||||||
|
@ -53,7 +54,7 @@ func TestInvocationTree(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Current: util.Uint160{4},
|
Current: util.Uint160{4},
|
||||||
Calls: []*InvocationTree{
|
Calls: []*invocations.Tree{
|
||||||
{
|
{
|
||||||
Current: util.Uint160{5},
|
Current: util.Uint160{5},
|
||||||
},
|
},
|
||||||
|
|
12
pkg/vm/invocations/invocation_tree.go
Normal file
12
pkg/vm/invocations/invocation_tree.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package invocations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tree represents a tree with script hashes; when traversing it,
|
||||||
|
// you can see how contracts called each other.
|
||||||
|
type Tree struct {
|
||||||
|
Current util.Uint160 `json:"hash"`
|
||||||
|
Calls []*Tree `json:"call,omitempty"`
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
vmUTExecutionEngineState struct {
|
vmUTExecutionEngineState struct {
|
||||||
State State `json:"state"`
|
State vmstate.State `json:"state"`
|
||||||
ResultStack []vmUTStackItem `json:"resultStack"`
|
ResultStack []vmUTStackItem `json:"resultStack"`
|
||||||
InvocationStack []vmUTExecutionContextState `json:"invocationStack"`
|
InvocationStack []vmUTExecutionContextState `json:"invocationStack"`
|
||||||
}
|
}
|
||||||
|
@ -152,14 +153,14 @@ func testFile(t *testing.T, filename string) {
|
||||||
t.Run(ut.Tests[i].Name, func(t *testing.T) {
|
t.Run(ut.Tests[i].Name, func(t *testing.T) {
|
||||||
prog := []byte(test.Script)
|
prog := []byte(test.Script)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.state = BreakState
|
vm.state = vmstate.Break
|
||||||
vm.SyscallHandler = testSyscallHandler
|
vm.SyscallHandler = testSyscallHandler
|
||||||
|
|
||||||
for i := range test.Steps {
|
for i := range test.Steps {
|
||||||
execStep(t, vm, test.Steps[i])
|
execStep(t, vm, test.Steps[i])
|
||||||
result := test.Steps[i].Result
|
result := test.Steps[i].Result
|
||||||
require.Equal(t, result.State, vm.state)
|
require.Equal(t, result.State, vm.state)
|
||||||
if result.State == FaultState { // do not compare stacks on fault
|
if result.State == vmstate.Fault { // do not compare stacks on fault
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStateFromString(t *testing.T) {
|
|
||||||
var (
|
|
||||||
s State
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
s, err = StateFromString("HALT")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, HaltState, s)
|
|
||||||
|
|
||||||
s, err = StateFromString("BREAK")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, BreakState, s)
|
|
||||||
|
|
||||||
s, err = StateFromString("FAULT")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, FaultState, s)
|
|
||||||
|
|
||||||
s, err = StateFromString("NONE")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, NoneState, s)
|
|
||||||
|
|
||||||
s, err = StateFromString("HALT, BREAK")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, HaltState|BreakState, s)
|
|
||||||
|
|
||||||
s, err = StateFromString("FAULT, BREAK")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, FaultState|BreakState, s)
|
|
||||||
|
|
||||||
_, err = StateFromString("HALT, KEK")
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestState_HasFlag(t *testing.T) {
|
|
||||||
assert.True(t, HaltState.HasFlag(HaltState))
|
|
||||||
assert.True(t, BreakState.HasFlag(BreakState))
|
|
||||||
assert.True(t, FaultState.HasFlag(FaultState))
|
|
||||||
assert.True(t, (HaltState | BreakState).HasFlag(HaltState))
|
|
||||||
assert.True(t, (HaltState | BreakState).HasFlag(BreakState))
|
|
||||||
|
|
||||||
assert.False(t, HaltState.HasFlag(BreakState))
|
|
||||||
assert.False(t, NoneState.HasFlag(HaltState))
|
|
||||||
assert.False(t, (FaultState | BreakState).HasFlag(HaltState))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestState_MarshalJSON(t *testing.T) {
|
|
||||||
var (
|
|
||||||
data []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
data, err = json.Marshal(HaltState | BreakState)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, data, []byte(`"HALT, BREAK"`))
|
|
||||||
|
|
||||||
data, err = json.Marshal(FaultState)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, data, []byte(`"FAULT"`))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestState_UnmarshalJSON(t *testing.T) {
|
|
||||||
var (
|
|
||||||
s State
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, HaltState|BreakState, s)
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, FaultState|BreakState, s)
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`"NONE"`), &s)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, NoneState, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestState_EnumCompat tests that byte value of State matches the C#'s one got from
|
|
||||||
// https://github.com/neo-project/neo-vm/blob/0028d862e253bda3c12eb8bb007a2d95822d3922/src/neo-vm/VMState.cs#L16.
|
|
||||||
func TestState_EnumCompat(t *testing.T) {
|
|
||||||
assert.Equal(t, byte(0), byte(NoneState))
|
|
||||||
assert.Equal(t, byte(1<<0), byte(HaltState))
|
|
||||||
assert.Equal(t, byte(1<<1), byte(FaultState))
|
|
||||||
assert.Equal(t, byte(1<<2), byte(BreakState))
|
|
||||||
}
|
|
80
pkg/vm/vm.go
80
pkg/vm/vm.go
|
@ -21,8 +21,10 @@ import (
|
||||||
"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/util/slice"
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type errorAtInstruct struct {
|
type errorAtInstruct struct {
|
||||||
|
@ -62,7 +64,7 @@ type SyscallHandler = func(*VM, uint32) error
|
||||||
|
|
||||||
// VM represents the virtual machine.
|
// VM represents the virtual machine.
|
||||||
type VM struct {
|
type VM struct {
|
||||||
state State
|
state vmstate.State
|
||||||
|
|
||||||
// callback to get interop price
|
// callback to get interop price
|
||||||
getPrice func(opcode.Opcode, []byte) int64
|
getPrice func(opcode.Opcode, []byte) int64
|
||||||
|
@ -86,7 +88,7 @@ type VM struct {
|
||||||
trigger trigger.Type
|
trigger trigger.Type
|
||||||
|
|
||||||
// invTree is a top-level invocation tree (if enabled).
|
// invTree is a top-level invocation tree (if enabled).
|
||||||
invTree *InvocationTree
|
invTree *invocations.Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -104,7 +106,7 @@ func New() *VM {
|
||||||
// NewWithTrigger returns a new VM for executions triggered by t.
|
// NewWithTrigger returns a new VM for executions triggered by t.
|
||||||
func NewWithTrigger(t trigger.Type) *VM {
|
func NewWithTrigger(t trigger.Type) *VM {
|
||||||
vm := &VM{
|
vm := &VM{
|
||||||
state: NoneState,
|
state: vmstate.None,
|
||||||
trigger: t,
|
trigger: t,
|
||||||
|
|
||||||
SyscallHandler: defaultSyscallHandler,
|
SyscallHandler: defaultSyscallHandler,
|
||||||
|
@ -126,7 +128,7 @@ func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
|
||||||
// more efficient. It reuses invocation and evaluation stacks as well as VM structure
|
// more efficient. It reuses invocation and evaluation stacks as well as VM structure
|
||||||
// itself.
|
// itself.
|
||||||
func (v *VM) Reset(t trigger.Type) {
|
func (v *VM) Reset(t trigger.Type) {
|
||||||
v.state = NoneState
|
v.state = vmstate.None
|
||||||
v.getPrice = nil
|
v.getPrice = nil
|
||||||
v.istack.elems = v.istack.elems[:0]
|
v.istack.elems = v.istack.elems[:0]
|
||||||
v.estack.elems = v.estack.elems[:0]
|
v.estack.elems = v.estack.elems[:0]
|
||||||
|
@ -270,11 +272,11 @@ func (v *VM) LoadFileWithFlags(path string, f callflag.CallFlag) error {
|
||||||
|
|
||||||
// CollectInvocationTree enables collecting invocation tree data.
|
// CollectInvocationTree enables collecting invocation tree data.
|
||||||
func (v *VM) EnableInvocationTree() {
|
func (v *VM) EnableInvocationTree() {
|
||||||
v.invTree = &InvocationTree{}
|
v.invTree = &invocations.Tree{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInvocationTree returns the current invocation tree structure.
|
// GetInvocationTree returns the current invocation tree structure.
|
||||||
func (v *VM) GetInvocationTree() *InvocationTree {
|
func (v *VM) GetInvocationTree() *invocations.Tree {
|
||||||
return v.invTree
|
return v.invTree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +290,7 @@ func (v *VM) LoadWithFlags(prog []byte, f callflag.CallFlag) {
|
||||||
// Clear all stacks and state, it could be a reload.
|
// Clear all stacks and state, it could be a reload.
|
||||||
v.istack.Clear()
|
v.istack.Clear()
|
||||||
v.estack.Clear()
|
v.estack.Clear()
|
||||||
v.state = NoneState
|
v.state = vmstate.None
|
||||||
v.gasConsumed = 0
|
v.gasConsumed = 0
|
||||||
v.invTree = nil
|
v.invTree = nil
|
||||||
v.LoadScriptWithFlags(prog, f)
|
v.LoadScriptWithFlags(prog, f)
|
||||||
|
@ -355,7 +357,7 @@ func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
curTree = parent.invTree
|
curTree = parent.invTree
|
||||||
}
|
}
|
||||||
newTree := &InvocationTree{Current: ctx.ScriptHash()}
|
newTree := &invocations.Tree{Current: ctx.ScriptHash()}
|
||||||
curTree.Calls = append(curTree.Calls, newTree)
|
curTree.Calls = append(curTree.Calls, newTree)
|
||||||
ctx.invTree = newTree
|
ctx.invTree = newTree
|
||||||
}
|
}
|
||||||
|
@ -398,7 +400,7 @@ func dumpStack(s *Stack) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns the state for the VM.
|
// State returns the state for the VM.
|
||||||
func (v *VM) State() State {
|
func (v *VM) State() vmstate.State {
|
||||||
return v.state
|
return v.state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,39 +415,39 @@ func (v *VM) Run() error {
|
||||||
var ctx *Context
|
var ctx *Context
|
||||||
|
|
||||||
if !v.Ready() {
|
if !v.Ready() {
|
||||||
v.state = FaultState
|
v.state = vmstate.Fault
|
||||||
return errors.New("no program loaded")
|
return errors.New("no program loaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.state.HasFlag(FaultState) {
|
if v.state.HasFlag(vmstate.Fault) {
|
||||||
// VM already ran something and failed, in general its state is
|
// VM already ran something and failed, in general its state is
|
||||||
// undefined in this case so we can't run anything.
|
// undefined in this case so we can't run anything.
|
||||||
return errors.New("VM has failed")
|
return errors.New("VM has failed")
|
||||||
}
|
}
|
||||||
// HaltState (the default) or BreakState are safe to continue.
|
// vmstate.Halt (the default) or vmstate.Break are safe to continue.
|
||||||
v.state = NoneState
|
v.state = vmstate.None
|
||||||
ctx = v.Context()
|
ctx = v.Context()
|
||||||
for {
|
for {
|
||||||
switch {
|
switch {
|
||||||
case v.state.HasFlag(FaultState):
|
case v.state.HasFlag(vmstate.Fault):
|
||||||
// Should be caught and reported already by the v.Step(),
|
// Should be caught and reported already by the v.Step(),
|
||||||
// but we're checking here anyway just in case.
|
// but we're checking here anyway just in case.
|
||||||
return errors.New("VM has failed")
|
return errors.New("VM has failed")
|
||||||
case v.state.HasFlag(HaltState), v.state.HasFlag(BreakState):
|
case v.state.HasFlag(vmstate.Halt), v.state.HasFlag(vmstate.Break):
|
||||||
// Normal exit from this loop.
|
// Normal exit from this loop.
|
||||||
return nil
|
return nil
|
||||||
case v.state == NoneState:
|
case v.state == vmstate.None:
|
||||||
if err := v.step(ctx); err != nil {
|
if err := v.step(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
v.state = FaultState
|
v.state = vmstate.Fault
|
||||||
return errors.New("unknown state")
|
return errors.New("unknown state")
|
||||||
}
|
}
|
||||||
// check for breakpoint before executing the next instruction
|
// check for breakpoint before executing the next instruction
|
||||||
ctx = v.Context()
|
ctx = v.Context()
|
||||||
if ctx != nil && ctx.atBreakPoint() {
|
if ctx != nil && ctx.atBreakPoint() {
|
||||||
v.state = BreakState
|
v.state = vmstate.Break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,7 +462,7 @@ func (v *VM) Step() error {
|
||||||
func (v *VM) step(ctx *Context) error {
|
func (v *VM) step(ctx *Context) error {
|
||||||
op, param, err := ctx.Next()
|
op, param, err := ctx.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.state = FaultState
|
v.state = vmstate.Fault
|
||||||
return newError(ctx.ip, op, err)
|
return newError(ctx.ip, op, err)
|
||||||
}
|
}
|
||||||
return v.execute(ctx, op, param)
|
return v.execute(ctx, op, param)
|
||||||
|
@ -472,7 +474,7 @@ func (v *VM) StepInto() error {
|
||||||
ctx := v.Context()
|
ctx := v.Context()
|
||||||
|
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
v.state = HaltState
|
v.state = vmstate.Halt
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.HasStopped() {
|
if v.HasStopped() {
|
||||||
|
@ -482,7 +484,7 @@ func (v *VM) StepInto() error {
|
||||||
if ctx != nil && ctx.prog != nil {
|
if ctx != nil && ctx.prog != nil {
|
||||||
op, param, err := ctx.Next()
|
op, param, err := ctx.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.state = FaultState
|
v.state = vmstate.Fault
|
||||||
return newError(ctx.ip, op, err)
|
return newError(ctx.ip, op, err)
|
||||||
}
|
}
|
||||||
vErr := v.execute(ctx, op, param)
|
vErr := v.execute(ctx, op, param)
|
||||||
|
@ -493,7 +495,7 @@ func (v *VM) StepInto() error {
|
||||||
|
|
||||||
cctx := v.Context()
|
cctx := v.Context()
|
||||||
if cctx != nil && cctx.atBreakPoint() {
|
if cctx != nil && cctx.atBreakPoint() {
|
||||||
v.state = BreakState
|
v.state = vmstate.Break
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -501,16 +503,16 @@ func (v *VM) StepInto() error {
|
||||||
// StepOut takes the debugger to the line where the current function was called.
|
// StepOut takes the debugger to the line where the current function was called.
|
||||||
func (v *VM) StepOut() error {
|
func (v *VM) StepOut() error {
|
||||||
var err error
|
var err error
|
||||||
if v.state == BreakState {
|
if v.state == vmstate.Break {
|
||||||
v.state = NoneState
|
v.state = vmstate.None
|
||||||
}
|
}
|
||||||
|
|
||||||
expSize := v.istack.Len()
|
expSize := v.istack.Len()
|
||||||
for v.state == NoneState && v.istack.Len() >= expSize {
|
for v.state == vmstate.None && v.istack.Len() >= expSize {
|
||||||
err = v.StepInto()
|
err = v.StepInto()
|
||||||
}
|
}
|
||||||
if v.state == NoneState {
|
if v.state == vmstate.None {
|
||||||
v.state = BreakState
|
v.state = vmstate.Break
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -523,20 +525,20 @@ func (v *VM) StepOver() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.state == BreakState {
|
if v.state == vmstate.Break {
|
||||||
v.state = NoneState
|
v.state = vmstate.None
|
||||||
}
|
}
|
||||||
|
|
||||||
expSize := v.istack.Len()
|
expSize := v.istack.Len()
|
||||||
for {
|
for {
|
||||||
err = v.StepInto()
|
err = v.StepInto()
|
||||||
if !(v.state == NoneState && v.istack.Len() > expSize) {
|
if !(v.state == vmstate.None && v.istack.Len() > expSize) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.state == NoneState {
|
if v.state == vmstate.None {
|
||||||
v.state = BreakState
|
v.state = vmstate.Break
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -545,22 +547,22 @@ func (v *VM) StepOver() error {
|
||||||
// HasFailed returns whether the VM is in the failed state now. Usually, it's used to
|
// HasFailed returns whether the VM is in the failed state now. Usually, it's used to
|
||||||
// check status after Run.
|
// check status after Run.
|
||||||
func (v *VM) HasFailed() bool {
|
func (v *VM) HasFailed() bool {
|
||||||
return v.state.HasFlag(FaultState)
|
return v.state.HasFlag(vmstate.Fault)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasStopped returns whether the VM is in the Halt or Failed state.
|
// HasStopped returns whether the VM is in the Halt or Failed state.
|
||||||
func (v *VM) HasStopped() bool {
|
func (v *VM) HasStopped() bool {
|
||||||
return v.state.HasFlag(HaltState) || v.state.HasFlag(FaultState)
|
return v.state.HasFlag(vmstate.Halt) || v.state.HasFlag(vmstate.Fault)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasHalted returns whether the VM is in the Halt state.
|
// HasHalted returns whether the VM is in the Halt state.
|
||||||
func (v *VM) HasHalted() bool {
|
func (v *VM) HasHalted() bool {
|
||||||
return v.state.HasFlag(HaltState)
|
return v.state.HasFlag(vmstate.Halt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AtBreakpoint returns whether the VM is at breakpoint.
|
// AtBreakpoint returns whether the VM is at breakpoint.
|
||||||
func (v *VM) AtBreakpoint() bool {
|
func (v *VM) AtBreakpoint() bool {
|
||||||
return v.state.HasFlag(BreakState)
|
return v.state.HasFlag(vmstate.Break)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInteropID converts instruction parameter to an interop ID.
|
// GetInteropID converts instruction parameter to an interop ID.
|
||||||
|
@ -574,10 +576,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
// each panic at a central point, putting the VM in a fault state and setting error.
|
// each panic at a central point, putting the VM in a fault state and setting error.
|
||||||
defer func() {
|
defer func() {
|
||||||
if errRecover := recover(); errRecover != nil {
|
if errRecover := recover(); errRecover != nil {
|
||||||
v.state = FaultState
|
v.state = vmstate.Fault
|
||||||
err = newError(ctx.ip, op, errRecover)
|
err = newError(ctx.ip, op, errRecover)
|
||||||
} else if v.refs > MaxStackSize {
|
} else if v.refs > MaxStackSize {
|
||||||
v.state = FaultState
|
v.state = vmstate.Fault
|
||||||
err = newError(ctx.ip, op, "stack is too big")
|
err = newError(ctx.ip, op, "stack is too big")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -1469,7 +1471,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
|
|
||||||
v.unloadContext(oldCtx)
|
v.unloadContext(oldCtx)
|
||||||
if v.istack.Len() == 0 {
|
if v.istack.Len() == 0 {
|
||||||
v.state = HaltState
|
v.state = vmstate.Halt
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
package vm
|
/*
|
||||||
|
Package vmstate contains a set of VM state flags along with appropriate type.
|
||||||
|
It provides a set of conversion/marshaling functions/methods for this type as
|
||||||
|
well. This package is made to make VM state reusable across all of the other
|
||||||
|
components that need it without importing whole VM package.
|
||||||
|
*/
|
||||||
|
package vmstate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// State of the VM.
|
// State of the VM. It's a set of flags stored in the integer number.
|
||||||
type State uint8
|
type State uint8
|
||||||
|
|
||||||
// Available States.
|
// Available States.
|
||||||
const (
|
const (
|
||||||
// HaltState represents HALT VM state.
|
// Halt represents HALT VM state (finished normally).
|
||||||
HaltState State = 1 << iota
|
Halt State = 1 << iota
|
||||||
// FaultState represents FAULT VM state.
|
// Fault represents FAULT VM state (finished with an error).
|
||||||
FaultState
|
Fault
|
||||||
// BreakState represents BREAK VM state.
|
// Break represents BREAK VM state (running, debug mode).
|
||||||
BreakState
|
Break
|
||||||
// NoneState represents NONE VM state.
|
// None represents NONE VM state (not started yet).
|
||||||
NoneState State = 0
|
None State = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// HasFlag checks for State flag presence.
|
// HasFlag checks for State flag presence.
|
||||||
|
@ -25,40 +31,40 @@ func (s State) HasFlag(f State) bool {
|
||||||
return s&f != 0
|
return s&f != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements the stringer interface.
|
// String implements the fmt.Stringer interface.
|
||||||
func (s State) String() string {
|
func (s State) String() string {
|
||||||
if s == NoneState {
|
if s == None {
|
||||||
return "NONE"
|
return "NONE"
|
||||||
}
|
}
|
||||||
|
|
||||||
ss := make([]string, 0, 3)
|
ss := make([]string, 0, 3)
|
||||||
if s.HasFlag(HaltState) {
|
if s.HasFlag(Halt) {
|
||||||
ss = append(ss, "HALT")
|
ss = append(ss, "HALT")
|
||||||
}
|
}
|
||||||
if s.HasFlag(FaultState) {
|
if s.HasFlag(Fault) {
|
||||||
ss = append(ss, "FAULT")
|
ss = append(ss, "FAULT")
|
||||||
}
|
}
|
||||||
if s.HasFlag(BreakState) {
|
if s.HasFlag(Break) {
|
||||||
ss = append(ss, "BREAK")
|
ss = append(ss, "BREAK")
|
||||||
}
|
}
|
||||||
return strings.Join(ss, ", ")
|
return strings.Join(ss, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateFromString converts a string into the VM State.
|
// FromString converts a string into the State.
|
||||||
func StateFromString(s string) (st State, err error) {
|
func FromString(s string) (st State, err error) {
|
||||||
if s = strings.TrimSpace(s); s == "NONE" {
|
if s = strings.TrimSpace(s); s == "NONE" {
|
||||||
return NoneState, nil
|
return None, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ss := strings.Split(s, ",")
|
ss := strings.Split(s, ",")
|
||||||
for _, state := range ss {
|
for _, state := range ss {
|
||||||
switch state = strings.TrimSpace(state); state {
|
switch state = strings.TrimSpace(state); state {
|
||||||
case "HALT":
|
case "HALT":
|
||||||
st |= HaltState
|
st |= Halt
|
||||||
case "FAULT":
|
case "FAULT":
|
||||||
st |= FaultState
|
st |= Fault
|
||||||
case "BREAK":
|
case "BREAK":
|
||||||
st |= BreakState
|
st |= Break
|
||||||
default:
|
default:
|
||||||
return 0, errors.New("unknown state")
|
return 0, errors.New("unknown state")
|
||||||
}
|
}
|
||||||
|
@ -78,6 +84,6 @@ func (s *State) UnmarshalJSON(data []byte) (err error) {
|
||||||
return errors.New("wrong format")
|
return errors.New("wrong format")
|
||||||
}
|
}
|
||||||
|
|
||||||
*s, err = StateFromString(string(data[1 : l-1]))
|
*s, err = FromString(string(data[1 : l-1]))
|
||||||
return
|
return
|
||||||
}
|
}
|
97
pkg/vm/vmstate/state_test.go
Normal file
97
pkg/vm/vmstate/state_test.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package vmstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFromString(t *testing.T) {
|
||||||
|
var (
|
||||||
|
s State
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
s, err = FromString("HALT")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Halt, s)
|
||||||
|
|
||||||
|
s, err = FromString("BREAK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Break, s)
|
||||||
|
|
||||||
|
s, err = FromString("FAULT")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Fault, s)
|
||||||
|
|
||||||
|
s, err = FromString("NONE")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, None, s)
|
||||||
|
|
||||||
|
s, err = FromString("HALT, BREAK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Halt|Break, s)
|
||||||
|
|
||||||
|
s, err = FromString("FAULT, BREAK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Fault|Break, s)
|
||||||
|
|
||||||
|
_, err = FromString("HALT, KEK")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestState_HasFlag(t *testing.T) {
|
||||||
|
assert.True(t, Halt.HasFlag(Halt))
|
||||||
|
assert.True(t, Break.HasFlag(Break))
|
||||||
|
assert.True(t, Fault.HasFlag(Fault))
|
||||||
|
assert.True(t, (Halt | Break).HasFlag(Halt))
|
||||||
|
assert.True(t, (Halt | Break).HasFlag(Break))
|
||||||
|
|
||||||
|
assert.False(t, Halt.HasFlag(Break))
|
||||||
|
assert.False(t, None.HasFlag(Halt))
|
||||||
|
assert.False(t, (Fault | Break).HasFlag(Halt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestState_MarshalJSON(t *testing.T) {
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
data, err = json.Marshal(Halt | Break)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, data, []byte(`"HALT, BREAK"`))
|
||||||
|
|
||||||
|
data, err = json.Marshal(Fault)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, data, []byte(`"FAULT"`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestState_UnmarshalJSON(t *testing.T) {
|
||||||
|
var (
|
||||||
|
s State
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Halt|Break, s)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Fault|Break, s)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`"NONE"`), &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, None, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestState_EnumCompat tests that byte value of State matches the C#'s one got from
|
||||||
|
// https://github.com/neo-project/neo-vm/blob/0028d862e253bda3c12eb8bb007a2d95822d3922/src/neo-vm/VMState.cs#L16.
|
||||||
|
func TestState_EnumCompat(t *testing.T) {
|
||||||
|
assert.Equal(t, byte(0), byte(None))
|
||||||
|
assert.Equal(t, byte(1<<0), byte(Halt))
|
||||||
|
assert.Equal(t, byte(1<<1), byte(Fault))
|
||||||
|
assert.Equal(t, byte(1<<2), byte(Break))
|
||||||
|
}
|
Loading…
Reference in a new issue