Merge pull request #2586 from nspcc-dev/move-res-rpc-code

Move response-related RPC code
This commit is contained in:
Roman Khimov 2022-07-11 18:23:01 +03:00 committed by GitHub
commit 953f291836
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 652 additions and 695 deletions

View file

@ -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())
}) })

View file

@ -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
} }

View file

@ -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")

View file

@ -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"))
} }
} }

View file

@ -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)

View file

@ -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 {

View file

@ -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",
}, },
}, },

View file

@ -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 {

View file

@ -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"`
} }

View 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"`
}

View 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
)

View file

@ -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
} }

View file

@ -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
} }
} }

View file

@ -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)

View file

@ -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.
} }

View file

@ -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

View file

@ -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 {

View file

@ -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))
}) })
}) })

View file

@ -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.

View file

@ -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 {

View file

@ -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)
} }

View file

@ -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)
}) })
} }

View file

@ -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,
}, },
} }

View file

@ -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
} }

View file

@ -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{},

View file

@ -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)
} }

View file

@ -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

View file

@ -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
} }

View 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"`
}
)

View 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"`
}

View file

@ -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)

View file

@ -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,
}, },
} }

View file

@ -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)
}) })

View file

@ -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:],
}) })

View file

@ -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"`
}
)

View file

@ -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}},

View file

@ -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)
} }

View file

@ -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)
} }

View file

@ -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 {

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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{},

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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)

View file

@ -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
} }

View file

@ -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) {

View file

@ -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

View file

@ -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"`
}

View file

@ -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},
}, },

View 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"`
}

View file

@ -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
} }

View file

@ -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))
}

View file

@ -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
} }

View file

@ -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
} }

View 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))
}