Merge pull request #2586 from nspcc-dev/move-res-rpc-code
Move response-related RPC code
This commit is contained in:
commit
953f291836
58 changed files with 652 additions and 695 deletions
|
@ -23,8 +23,8 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"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/vmstate"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
@ -354,7 +354,7 @@ func TestContractDeployWithData(t *testing.T) {
|
|||
|
||||
res := new(result.Invoke)
|
||||
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.Equal(t, []byte{12}, res.Stack[0].Value())
|
||||
|
||||
|
@ -366,7 +366,7 @@ func TestContractDeployWithData(t *testing.T) {
|
|||
|
||||
res = new(result.Invoke)
|
||||
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.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value())
|
||||
}
|
||||
|
@ -672,7 +672,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
|
||||
res := new(result.Invoke)
|
||||
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.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))...)
|
||||
res := new(result.Invoke)
|
||||
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.Equal(t, []stackitem.Item{
|
||||
stackitem.Make("findkey1"),
|
||||
|
@ -832,7 +832,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
e.Run(t, append(cmd, strconv.FormatInt(storage.FindDefault, 10))...)
|
||||
res := new(result.Invoke)
|
||||
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)
|
||||
|
||||
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||
|
@ -883,7 +883,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
|
||||
res := new(result.Invoke)
|
||||
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.Equal(t, []byte("on update|sub update"), res.Stack[0].Value())
|
||||
})
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/rpc/server"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"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/urfave/cli"
|
||||
"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)
|
||||
require.NoError(t, err)
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"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/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -154,7 +154,7 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
e.checkTxTestInvokeOutput(t, 11)
|
||||
res := new(result.Invoke)
|
||||
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")
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"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/vmstate"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -129,7 +130,7 @@ func DumpApplicationLog(
|
|||
if len(res.Executions) != 1 {
|
||||
_, _ = tw.Write([]byte("Success:\tunknown (no execution data)\n"))
|
||||
} 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 {
|
||||
|
@ -146,7 +147,7 @@ func DumpApplicationLog(
|
|||
v.PrintOps(tw)
|
||||
if res != nil {
|
||||
for _, e := range res.Executions {
|
||||
if e.VMState != vm.HaltState {
|
||||
if e.VMState != vmstate.Halt {
|
||||
_, _ = tw.Write([]byte("Exception:\t" + e.FaultException + "\n"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"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/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())
|
||||
|
||||
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 {
|
||||
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)
|
||||
|
||||
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.checkEOF(t)
|
||||
|
|
|
@ -7,14 +7,15 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||
)
|
||||
|
||||
type dump []blockDump
|
||||
|
||||
type blockDump struct {
|
||||
Block uint32 `json:"block"`
|
||||
Size int `json:"size"`
|
||||
Storage []storage.Operation `json:"storage"`
|
||||
Block uint32 `json:"block"`
|
||||
Size int `json:"size"`
|
||||
Storage []dboper.Operation `json:"storage"`
|
||||
}
|
||||
|
||||
func newDump() *dump {
|
||||
|
|
|
@ -9,8 +9,7 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"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/network/metrics"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -317,7 +316,7 @@ func TestConfigureAddresses(t *testing.T) {
|
|||
t.Run("custom Pprof address", func(t *testing.T) {
|
||||
cfg := &config.ApplicationConfiguration{
|
||||
Address: defaultAddress,
|
||||
Pprof: metrics.Config{
|
||||
Pprof: config.BasicService{
|
||||
Address: customAddress,
|
||||
},
|
||||
}
|
||||
|
@ -330,7 +329,7 @@ func TestConfigureAddresses(t *testing.T) {
|
|||
t.Run("custom Prometheus address", func(t *testing.T) {
|
||||
cfg := &config.ApplicationConfiguration{
|
||||
Address: defaultAddress,
|
||||
Prometheus: metrics.Config{
|
||||
Prometheus: config.BasicService{
|
||||
Address: customAddress,
|
||||
},
|
||||
}
|
||||
|
@ -350,7 +349,7 @@ func TestInitBlockChain(t *testing.T) {
|
|||
t.Run("empty logger", func(t *testing.T) {
|
||||
_, err := initBlockChain(config.Config{
|
||||
ApplicationConfiguration: config.ApplicationConfiguration{
|
||||
DBConfiguration: storage.DBConfiguration{
|
||||
DBConfiguration: dbconfig.DBConfiguration{
|
||||
Type: "inmemory",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"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/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -116,10 +117,10 @@ func TestLedgerTransactionWitnessCondition(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLedgerVMStates(t *testing.T) {
|
||||
require.EqualValues(t, ledger.NoneState, vm.NoneState)
|
||||
require.EqualValues(t, ledger.HaltState, vm.HaltState)
|
||||
require.EqualValues(t, ledger.FaultState, vm.FaultState)
|
||||
require.EqualValues(t, ledger.BreakState, vm.BreakState)
|
||||
require.EqualValues(t, ledger.NoneState, vmstate.None)
|
||||
require.EqualValues(t, ledger.HaltState, vmstate.Halt)
|
||||
require.EqualValues(t, ledger.FaultState, vmstate.Fault)
|
||||
require.EqualValues(t, ledger.BreakState, vmstate.Break)
|
||||
}
|
||||
|
||||
type nativeTestCase struct {
|
||||
|
|
|
@ -1,33 +1,32 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/network/metrics"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
||||
)
|
||||
|
||||
// ApplicationConfiguration config specific to the node.
|
||||
type ApplicationConfiguration struct {
|
||||
Address string `yaml:"Address"`
|
||||
AnnouncedNodePort uint16 `yaml:"AnnouncedPort"`
|
||||
AttemptConnPeers int `yaml:"AttemptConnPeers"`
|
||||
DBConfiguration storage.DBConfiguration `yaml:"DBConfiguration"`
|
||||
DialTimeout int64 `yaml:"DialTimeout"`
|
||||
LogPath string `yaml:"LogPath"`
|
||||
MaxPeers int `yaml:"MaxPeers"`
|
||||
MinPeers int `yaml:"MinPeers"`
|
||||
NodePort uint16 `yaml:"NodePort"`
|
||||
PingInterval int64 `yaml:"PingInterval"`
|
||||
PingTimeout int64 `yaml:"PingTimeout"`
|
||||
Pprof metrics.Config `yaml:"Pprof"`
|
||||
Prometheus metrics.Config `yaml:"Prometheus"`
|
||||
ProtoTickInterval int64 `yaml:"ProtoTickInterval"`
|
||||
Relay bool `yaml:"Relay"`
|
||||
RPC rpc.Config `yaml:"RPC"`
|
||||
UnlockWallet Wallet `yaml:"UnlockWallet"`
|
||||
Oracle OracleConfiguration `yaml:"Oracle"`
|
||||
P2PNotary P2PNotary `yaml:"P2PNotary"`
|
||||
StateRoot StateRoot `yaml:"StateRoot"`
|
||||
Address string `yaml:"Address"`
|
||||
AnnouncedNodePort uint16 `yaml:"AnnouncedPort"`
|
||||
AttemptConnPeers int `yaml:"AttemptConnPeers"`
|
||||
DBConfiguration dbconfig.DBConfiguration `yaml:"DBConfiguration"`
|
||||
DialTimeout int64 `yaml:"DialTimeout"`
|
||||
LogPath string `yaml:"LogPath"`
|
||||
MaxPeers int `yaml:"MaxPeers"`
|
||||
MinPeers int `yaml:"MinPeers"`
|
||||
NodePort uint16 `yaml:"NodePort"`
|
||||
PingInterval int64 `yaml:"PingInterval"`
|
||||
PingTimeout int64 `yaml:"PingTimeout"`
|
||||
Pprof BasicService `yaml:"Pprof"`
|
||||
Prometheus BasicService `yaml:"Prometheus"`
|
||||
ProtoTickInterval int64 `yaml:"ProtoTickInterval"`
|
||||
Relay bool `yaml:"Relay"`
|
||||
RPC rpc.Config `yaml:"RPC"`
|
||||
UnlockWallet Wallet `yaml:"UnlockWallet"`
|
||||
Oracle OracleConfiguration `yaml:"Oracle"`
|
||||
P2PNotary P2PNotary `yaml:"P2PNotary"`
|
||||
StateRoot StateRoot `yaml:"StateRoot"`
|
||||
// ExtensiblePoolSize is the maximum amount of the extensible payloads from a single sender.
|
||||
ExtensiblePoolSize int `yaml:"ExtensiblePoolSize"`
|
||||
}
|
||||
|
|
8
pkg/config/basic_service.go
Normal file
8
pkg/config/basic_service.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package config
|
||||
|
||||
// BasicService is used for simple services like Pprof or Prometheus monitoring.
|
||||
type BasicService struct {
|
||||
Enabled bool `yaml:"Enabled"`
|
||||
Address string `yaml:"Address"`
|
||||
Port string `yaml:"Port"`
|
||||
}
|
17
pkg/config/limits/limits.go
Normal file
17
pkg/config/limits/limits.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
Package limits contains a number of system-wide hardcoded constants.
|
||||
Many of the Neo protocol parameters can be adjusted by the configuration, but
|
||||
some can not and this package contains hardcoded limits that are relevant for
|
||||
many applications.
|
||||
*/
|
||||
package limits
|
||||
|
||||
const (
|
||||
// MaxStorageKeyLen is the maximum length of a key for storage items.
|
||||
// Contracts can't use keys longer than that in their requests to the DB.
|
||||
MaxStorageKeyLen = 64
|
||||
// MaxStorageValueLen is the maximum length of a value for storage items.
|
||||
// It is set to be the maximum value for uint16, contracts can't put
|
||||
// values longer than that into the DB.
|
||||
MaxStorageValueLen = 65535
|
||||
)
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"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/crypto/keys"
|
||||
"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 {
|
||||
dbPath := t.TempDir()
|
||||
dbOptions := storage.LevelDBOptions{
|
||||
dbOptions := dbconfig.LevelDBOptions{
|
||||
DataDirectoryPath: dbPath,
|
||||
}
|
||||
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
||||
|
@ -147,7 +148,7 @@ func newLevelDBForTesting(t testing.TB) storage.Store {
|
|||
func newBoltStoreForTesting(t testing.TB) storage.Store {
|
||||
d := t.TempDir()
|
||||
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)
|
||||
return boltDBStore
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"time"
|
||||
|
||||
"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/blockchainer"
|
||||
"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/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -816,7 +818,7 @@ func (bc *Blockchain) notificationDispatcher() {
|
|||
for ch := range executionFeed {
|
||||
ch <- aer
|
||||
}
|
||||
if aer.VMState == vm.HaltState {
|
||||
if aer.VMState == vmstate.Halt {
|
||||
for i := range aer.Events {
|
||||
for ch := range notificationFeed {
|
||||
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)
|
||||
break
|
||||
}
|
||||
if aer.Execution.VMState == vm.HaltState {
|
||||
if aer.Execution.VMState == vmstate.Halt {
|
||||
for j := range aer.Execution.Events {
|
||||
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
|
||||
if len(arr) == 4 {
|
||||
id, err = arr[3].TryBytes()
|
||||
if err != nil || len(id) > storage.MaxStorageKeyLen {
|
||||
if err != nil || len(id) > limits.MaxStorageKeyLen {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"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/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/crypto/hash"
|
||||
"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/trigger"
|
||||
"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/opcode"
|
||||
"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/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -52,7 +53,7 @@ func newLevelDBForTestingWithPath(t testing.TB, dbPath string) (storage.Store, s
|
|||
if dbPath == "" {
|
||||
dbPath = t.TempDir()
|
||||
}
|
||||
dbOptions := storage.LevelDBOptions{
|
||||
dbOptions := dbconfig.LevelDBOptions{
|
||||
DataDirectoryPath: dbPath,
|
||||
}
|
||||
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
||||
|
@ -826,7 +827,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
|
|||
|
||||
exec := <-executionCh
|
||||
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
|
||||
require.True(t, len(notificationCh) >= 4)
|
||||
|
@ -841,7 +842,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
|
|||
require.Equal(t, txExpected, tx)
|
||||
exec := <-executionCh
|
||||
require.Equal(t, tx.Hash(), exec.Container)
|
||||
if exec.VMState == vm.HaltState {
|
||||
if exec.VMState == vmstate.Halt {
|
||||
notif := <-notificationCh
|
||||
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
|
||||
}
|
||||
|
@ -855,7 +856,7 @@ func TestBlockchain_Subscriptions(t *testing.T) {
|
|||
|
||||
exec = <-executionCh
|
||||
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.UnsubscribeFromTransactions(txCh)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"math/big"
|
||||
"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/state"
|
||||
"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 {
|
||||
if dao.private {
|
||||
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.
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"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/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
if ic.VM.State() == vm.FaultState {
|
||||
if ic.VM.HasFailed() {
|
||||
return ErrNativeCall
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"errors"
|
||||
"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/storage"
|
||||
"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 {
|
||||
if len(key) > storage.MaxStorageKeyLen {
|
||||
if len(key) > limits.MaxStorageKeyLen {
|
||||
return errors.New("key is too big")
|
||||
}
|
||||
if len(value) > storage.MaxStorageValueLen {
|
||||
if len(value) > limits.MaxStorageValueLen {
|
||||
return errors.New("value is too big")
|
||||
}
|
||||
if stc.ReadOnly {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/big"
|
||||
"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/block"
|
||||
"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"
|
||||
"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/storage"
|
||||
"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/neotest/chain"
|
||||
|
@ -61,7 +61,7 @@ func TestPut(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))
|
||||
})
|
||||
|
||||
|
@ -72,11 +72,11 @@ func TestPut(t *testing.T) {
|
|||
require.Error(t, istorage.Put(ic))
|
||||
})
|
||||
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))
|
||||
})
|
||||
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))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -6,14 +6,14 @@ import (
|
|||
"errors"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 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
|
||||
// before transforming to nibbles.
|
||||
|
|
|
@ -5,13 +5,13 @@ import (
|
|||
"errors"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type LeafNode struct {
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"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/vmstate"
|
||||
)
|
||||
|
||||
// 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)
|
||||
if err != nil || !isTraceableBlock(ic, h) {
|
||||
return stackitem.Make(vm.NoneState)
|
||||
return stackitem.Make(vmstate.None)
|
||||
}
|
||||
return stackitem.Make(aer.VMState)
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||
"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/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"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})
|
||||
|
||||
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) {
|
||||
ledgerInvoker.InvokeFail(t, "expected []byte of size 32", "getTransactionVMState", []byte{1, 2, 3})
|
||||
})
|
||||
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) {
|
||||
// Add more blocks so that tx becomes untraceable.
|
||||
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) {
|
||||
faultedH := e.InvokeScript(t, []byte{byte(opcode.ABORT)}, []neotest.Signer{c.Committee})
|
||||
ledgerInvoker.Invoke(t, vm.FaultState, "getTransactionVMState", faultedH)
|
||||
ledgerInvoker.Invoke(t, vmstate.Fault, "getTransactionVMState", faultedH)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"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/crypto/keys"
|
||||
"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/nef"
|
||||
"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/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -69,7 +70,7 @@ func TestManagement_ContractCache(t *testing.T) {
|
|||
managementInvoker.CheckHalt(t, tx1.Hash())
|
||||
aer, err := managementInvoker.Chain.GetAppExecResults(tx2.Hash(), trigger.Application)
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -281,9 +282,9 @@ func TestManagement_ContractDeploy(t *testing.T) {
|
|||
func TestManagement_StartFromHeight(t *testing.T) {
|
||||
// Create database to be able to start another chain from the same height later.
|
||||
ldbDir := t.TempDir()
|
||||
dbConfig := storage.DBConfiguration{
|
||||
dbConfig := dbconfig.DBConfiguration{
|
||||
Type: "leveldb",
|
||||
LevelDBOptions: storage.LevelDBOptions{
|
||||
LevelDBOptions: dbconfig.LevelDBOptions{
|
||||
DataDirectoryPath: ldbDir,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"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/vmstate"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
r.ReadBytes(aer.Container[:])
|
||||
aer.Trigger = trigger.Type(r.ReadB())
|
||||
aer.VMState = vm.State(r.ReadB())
|
||||
aer.VMState = vmstate.State(r.ReadB())
|
||||
aer.GasConsumed = int64(r.ReadU64LE())
|
||||
sz := r.ReadVarUint()
|
||||
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.
|
||||
type Execution struct {
|
||||
Trigger trigger.Type
|
||||
VMState vm.State
|
||||
VMState vmstate.State
|
||||
GasConsumed int64
|
||||
Stack []stackitem.Item
|
||||
Events []NotificationEvent
|
||||
|
@ -266,7 +266,7 @@ func (e *Execution) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
e.Trigger = trigger
|
||||
state, err := vm.StateFromString(aux.VMState)
|
||||
state, err := vmstate.FromString(aux.VMState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"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/vmstate"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -18,7 +18,7 @@ func BenchmarkAppExecResult_EncodeBinary(b *testing.B) {
|
|||
Container: random.Uint256(),
|
||||
Execution: Execution{
|
||||
Trigger: trigger.Application,
|
||||
VMState: vm.HaltState,
|
||||
VMState: vmstate.Halt,
|
||||
GasConsumed: 12345,
|
||||
Stack: []stackitem.Item{},
|
||||
Events: []NotificationEvent{{
|
||||
|
@ -54,7 +54,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
|
|||
Container: random.Uint256(),
|
||||
Execution: Execution{
|
||||
Trigger: 1,
|
||||
VMState: vm.HaltState,
|
||||
VMState: vmstate.Halt,
|
||||
GasConsumed: 10,
|
||||
Stack: []stackitem.Item{stackitem.NewBool(true)},
|
||||
Events: []NotificationEvent{},
|
||||
|
@ -63,12 +63,12 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
|
|||
}
|
||||
t.Run("halt", func(t *testing.T) {
|
||||
appExecResult := newAer()
|
||||
appExecResult.VMState = vm.HaltState
|
||||
appExecResult.VMState = vmstate.Halt
|
||||
testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
|
||||
})
|
||||
t.Run("fault", func(t *testing.T) {
|
||||
appExecResult := newAer()
|
||||
appExecResult.VMState = vm.FaultState
|
||||
appExecResult.VMState = vmstate.Fault
|
||||
testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
|
||||
})
|
||||
t.Run("with interop", func(t *testing.T) {
|
||||
|
@ -150,7 +150,7 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
|||
Container: random.Uint256(),
|
||||
Execution: Execution{
|
||||
Trigger: trigger.Application,
|
||||
VMState: vm.HaltState,
|
||||
VMState: vmstate.Halt,
|
||||
GasConsumed: 10,
|
||||
Stack: []stackitem.Item{},
|
||||
Events: []NotificationEvent{},
|
||||
|
@ -164,7 +164,7 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
|||
Container: random.Uint256(),
|
||||
Execution: Execution{
|
||||
Trigger: trigger.Application,
|
||||
VMState: vm.FaultState,
|
||||
VMState: vmstate.Fault,
|
||||
GasConsumed: 10,
|
||||
Stack: []stackitem.Item{stackitem.NewBool(true)},
|
||||
Events: []NotificationEvent{},
|
||||
|
@ -178,7 +178,7 @@ func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
|
|||
Container: random.Uint256(),
|
||||
Execution: Execution{
|
||||
Trigger: trigger.OnPersist,
|
||||
VMState: vm.HaltState,
|
||||
VMState: vmstate.Halt,
|
||||
GasConsumed: 10,
|
||||
Stack: []stackitem.Item{},
|
||||
Events: []NotificationEvent{},
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bytes"
|
||||
"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/io"
|
||||
"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.
|
||||
func (t *NEP11Transfer) DecodeBinary(r *io.BinReader) {
|
||||
t.NEP17Transfer.DecodeBinary(r)
|
||||
t.ID = r.ReadVarBytes(storage.MaxStorageKeyLen)
|
||||
t.ID = r.ReadVarBytes(limits.MaxStorageKeyLen)
|
||||
}
|
||||
|
|
|
@ -5,16 +5,12 @@ import (
|
|||
"fmt"
|
||||
"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/util/slice"
|
||||
"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.
|
||||
var Bucket = []byte("DB")
|
||||
|
||||
|
@ -25,7 +21,7 @@ type BoltDBStore struct {
|
|||
}
|
||||
|
||||
// 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
|
||||
fileMode := os.FileMode(0600) // should be exposed via BoltDBOptions if anything needed
|
||||
fileName := cfg.FilePath
|
||||
|
|
|
@ -4,13 +4,14 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newBoltStoreForTesting(t testing.TB) Store {
|
||||
d := t.TempDir()
|
||||
testFileName := filepath.Join(d, "test_bolt_db")
|
||||
boltDBStore, err := NewBoltDBStore(BoltDBOptions{FilePath: testFileName})
|
||||
boltDBStore, err := NewBoltDBStore(dbconfig.BoltDBOptions{FilePath: testFileName})
|
||||
require.NoError(t, err)
|
||||
return boltDBStore
|
||||
}
|
||||
|
|
21
pkg/core/storage/dbconfig/store_config.go
Normal file
21
pkg/core/storage/dbconfig/store_config.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
Package dbconfig is a micropackage that contains storage DB configuration options.
|
||||
*/
|
||||
package dbconfig
|
||||
|
||||
type (
|
||||
// DBConfiguration describes configuration for DB. Supported: 'levelDB', 'boltDB'.
|
||||
DBConfiguration struct {
|
||||
Type string `yaml:"Type"`
|
||||
LevelDBOptions LevelDBOptions `yaml:"LevelDBOptions"`
|
||||
BoltDBOptions BoltDBOptions `yaml:"BoltDBOptions"`
|
||||
}
|
||||
// LevelDBOptions configuration for LevelDB.
|
||||
LevelDBOptions struct {
|
||||
DataDirectoryPath string `yaml:"DataDirectoryPath"`
|
||||
}
|
||||
// BoltDBOptions configuration for BoltDB.
|
||||
BoltDBOptions struct {
|
||||
FilePath string `yaml:"FilePath"`
|
||||
}
|
||||
)
|
13
pkg/core/storage/dboper/operation.go
Normal file
13
pkg/core/storage/dboper/operation.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
Package dboper contains a type used to represent single DB operation.
|
||||
*/
|
||||
package dboper
|
||||
|
||||
// Operation represents a single KV operation (add/del/change) performed
|
||||
// in the DB.
|
||||
type Operation struct {
|
||||
// State can be Added, Changed or Deleted.
|
||||
State string `json:"state"`
|
||||
Key []byte `json:"key"`
|
||||
Value []byte `json:"value,omitempty"`
|
||||
}
|
|
@ -1,17 +1,13 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
"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
|
||||
// blockchain data.
|
||||
type LevelDBStore struct {
|
||||
|
@ -21,7 +17,7 @@ type LevelDBStore struct {
|
|||
|
||||
// NewLevelDBStore returns a new LevelDBStore object that will
|
||||
// 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
|
||||
|
||||
opts.Filter = filter.NewBloomFilter(10)
|
||||
|
|
|
@ -3,14 +3,15 @@ package storage
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newLevelDBForTesting(t testing.TB) Store {
|
||||
ldbDir := t.TempDir()
|
||||
dbConfig := DBConfiguration{
|
||||
dbConfig := dbconfig.DBConfiguration{
|
||||
Type: "leveldb",
|
||||
LevelDBOptions: LevelDBOptions{
|
||||
LevelDBOptions: dbconfig.LevelDBOptions{
|
||||
DataDirectoryPath: ldbDir,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -68,13 +68,14 @@ func (s *MemoryStore) putChangeSet(puts map[string][]byte, stores map[string][]b
|
|||
|
||||
// Seek implements the Store interface.
|
||||
func (s *MemoryStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
||||
s.mut.RLock()
|
||||
s.seek(rng, f)
|
||||
s.mut.RUnlock()
|
||||
s.seek(rng, f, s.mut.RLock, s.mut.RUnlock)
|
||||
}
|
||||
|
||||
// SeekGC implements the Store interface.
|
||||
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()
|
||||
// We still need to perform normal seek, some GC operations can be
|
||||
// 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))
|
||||
}
|
||||
return true
|
||||
})
|
||||
}, noop, noop)
|
||||
s.mut.Unlock()
|
||||
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
|
||||
// seeking starting from the provided prefix should be performed. Backwards
|
||||
// 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)
|
||||
lPrefix := len(sPrefix)
|
||||
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)
|
||||
}
|
||||
|
||||
lock()
|
||||
m := s.chooseMap(rng.Prefix)
|
||||
for k, v := range m {
|
||||
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 {
|
||||
return less(memList[i].Key, memList[j].Key)
|
||||
})
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -40,23 +42,6 @@ const (
|
|||
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.
|
||||
type SeekRange struct {
|
||||
// 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.
|
||||
func NewStore(cfg DBConfiguration) (Store, error) {
|
||||
func NewStore(cfg dbconfig.DBConfiguration) (Store, error) {
|
||||
var store Store
|
||||
var err error
|
||||
switch cfg.Type {
|
||||
|
@ -141,10 +126,10 @@ func NewStore(cfg DBConfiguration) (Store, error) {
|
|||
return store, err
|
||||
}
|
||||
|
||||
// BatchToOperations converts a batch of changes into array of Operations.
|
||||
func BatchToOperations(batch *MemBatch) []Operation {
|
||||
// BatchToOperations converts a batch of changes into array of dboper.Operation.
|
||||
func BatchToOperations(batch *MemBatch) []dboper.Operation {
|
||||
size := len(batch.Put) + len(batch.Deleted)
|
||||
ops := make([]Operation, 0, size)
|
||||
ops := make([]dboper.Operation, 0, size)
|
||||
for i := range batch.Put {
|
||||
key := batch.Put[i].Key
|
||||
if len(key) == 0 || key[0] != byte(STStorage) && key[0] != byte(STTempStorage) {
|
||||
|
@ -156,7 +141,7 @@ func BatchToOperations(batch *MemBatch) []Operation {
|
|||
op = "Changed"
|
||||
}
|
||||
|
||||
ops = append(ops, Operation{
|
||||
ops = append(ops, dboper.Operation{
|
||||
State: op,
|
||||
Key: key[1:],
|
||||
Value: batch.Put[i].Value,
|
||||
|
@ -170,7 +155,7 @@ func BatchToOperations(batch *MemBatch) []Operation {
|
|||
continue
|
||||
}
|
||||
|
||||
ops = append(ops, Operation{
|
||||
ops = append(ops, dboper.Operation{
|
||||
State: "Deleted",
|
||||
Key: key[1:],
|
||||
})
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package storage
|
||||
|
||||
type (
|
||||
// DBConfiguration describes configuration for DB. Supported: 'levelDB', 'boltDB'.
|
||||
DBConfiguration struct {
|
||||
Type string `yaml:"Type"`
|
||||
LevelDBOptions LevelDBOptions `yaml:"LevelDBOptions"`
|
||||
BoltDBOptions BoltDBOptions `yaml:"BoltDBOptions"`
|
||||
}
|
||||
)
|
|
@ -3,6 +3,7 @@ package storage
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
||||
"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},
|
||||
},
|
||||
}
|
||||
o := []Operation{
|
||||
o := []dboper.Operation{
|
||||
{State: "Added", Key: []byte{0x01}, Value: []byte{0x01}},
|
||||
{State: "Changed", Key: []byte{0x03}, Value: []byte{0x03}},
|
||||
{State: "Deleted", Key: []byte{0x06}},
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"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/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 {
|
||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||
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 {
|
||||
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) {
|
||||
aer, err := e.Chain.GetAppExecResults(h, trigger.Application)
|
||||
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),
|
||||
"expected: %s, got: %s", s, aer[0].FaultException)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -91,7 +92,7 @@ func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testin
|
|||
c.AddNewBlock(t, tx)
|
||||
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||
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 {
|
||||
checkResult(t, aer[0].Stack)
|
||||
}
|
||||
|
|
|
@ -4,24 +4,18 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Service serves metrics.
|
||||
type Service struct {
|
||||
*http.Server
|
||||
config Config
|
||||
config config.BasicService
|
||||
log *zap.Logger
|
||||
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.
|
||||
func (ms *Service) Start() {
|
||||
if ms.config.Enabled {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/pprof"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
type PprofService Service
|
||||
|
||||
// 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 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package metrics
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
type PrometheusService Service
|
||||
|
||||
// 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 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -34,9 +34,9 @@ import (
|
|||
"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/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/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -129,7 +129,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
Executions: []state.Execution{
|
||||
{
|
||||
Trigger: trigger.Application,
|
||||
VMState: vm.HaltState,
|
||||
VMState: vmstate.Halt,
|
||||
GasConsumed: 1,
|
||||
Stack: []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1))},
|
||||
Events: []state.NotificationEvent{},
|
||||
|
|
|
@ -5,16 +5,10 @@ import (
|
|||
"errors"
|
||||
|
||||
"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 (
|
||||
// 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.Block / block.Base on the RPC Server.
|
||||
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.
|
||||
func (b Block) MarshalJSON() ([]byte, error) {
|
||||
output, err := json.Marshal(b.BlockMetadata)
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"errors"
|
||||
|
||||
"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 (
|
||||
|
@ -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.
|
||||
func (h Header) MarshalJSON() ([]byte, error) {
|
||||
output, err := json.Marshal(h.BlockMetadata)
|
||||
|
|
|
@ -5,67 +5,31 @@ import (
|
|||
"fmt"
|
||||
|
||||
"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/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/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// Invoke represents a code invocation result and is used by several RPC calls
|
||||
// that invoke functions, scripts and generic bytecode.
|
||||
type Invoke struct {
|
||||
State string
|
||||
GasConsumed int64
|
||||
Script []byte
|
||||
Stack []stackitem.Item
|
||||
FaultException string
|
||||
Notifications []state.NotificationEvent
|
||||
Transaction *transaction.Transaction
|
||||
Diagnostics *InvokeDiag
|
||||
maxIteratorResultItems int
|
||||
Session uuid.UUID
|
||||
finalize func()
|
||||
registerIterator RegisterIterator
|
||||
State string
|
||||
GasConsumed int64
|
||||
Script []byte
|
||||
Stack []stackitem.Item
|
||||
FaultException string
|
||||
Notifications []state.NotificationEvent
|
||||
Transaction *transaction.Transaction
|
||||
Diagnostics *InvokeDiag
|
||||
Session uuid.UUID
|
||||
}
|
||||
|
||||
// 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.
|
||||
type InvokeDiag struct {
|
||||
Changes []storage.Operation `json:"storagechanges"`
|
||||
Invocations []*vm.InvocationTree `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,
|
||||
}
|
||||
Changes []dboper.Operation `json:"storagechanges"`
|
||||
Invocations []*invocations.Tree `json:"invokedcontracts"`
|
||||
}
|
||||
|
||||
type invokeAux struct {
|
||||
|
@ -106,85 +70,85 @@ type Iterator struct {
|
|||
Truncated bool
|
||||
}
|
||||
|
||||
// Finalize releases resources occupied by Iterators created at the script invocation.
|
||||
// This method will be called automatically on Invoke marshalling or by the Server's
|
||||
// sessions handler.
|
||||
func (r *Invoke) Finalize() {
|
||||
if r.finalize != nil {
|
||||
r.finalize()
|
||||
// MarshalJSON implements the json.Marshaler.
|
||||
func (r Iterator) MarshalJSON() ([]byte, error) {
|
||||
var iaux iteratorAux
|
||||
iaux.Type = stackitem.InteropT.String()
|
||||
if r.ID != nil {
|
||||
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.
|
||||
func (r Invoke) MarshalJSON() ([]byte, error) {
|
||||
var (
|
||||
st json.RawMessage
|
||||
err error
|
||||
faultSep string
|
||||
arr = make([]json.RawMessage, len(r.Stack))
|
||||
sessionsEnabled = r.registerIterator != nil
|
||||
sessionID string
|
||||
st json.RawMessage
|
||||
err error
|
||||
faultSep string
|
||||
arr = make([]json.RawMessage, len(r.Stack))
|
||||
)
|
||||
if len(r.FaultException) != 0 {
|
||||
faultSep = " / "
|
||||
}
|
||||
arrloop:
|
||||
for i := range arr {
|
||||
var data []byte
|
||||
if (r.Stack[i].Type() == stackitem.InteropT) && iterator.IsIterator(r.Stack[i]) {
|
||||
if sessionsEnabled {
|
||||
if sessionID == "" {
|
||||
sessionID = uuid.NewString()
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
iter, ok := r.Stack[i].Value().(Iterator)
|
||||
if (r.Stack[i].Type() == stackitem.InteropT) && ok {
|
||||
data, err = json.Marshal(iter)
|
||||
} else {
|
||||
data, err = stackitem.ToJSONWithTypes(r.Stack[i])
|
||||
if err != nil {
|
||||
r.FaultException += fmt.Sprintf("%sjson error: %v", faultSep, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
r.FaultException += fmt.Sprintf("%sjson error: %v", faultSep, err)
|
||||
break
|
||||
}
|
||||
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 {
|
||||
st, err = json.Marshal(arr)
|
||||
if err != nil {
|
||||
|
@ -195,6 +159,10 @@ arrloop:
|
|||
if r.Transaction != nil {
|
||||
txbytes = r.Transaction.Bytes()
|
||||
}
|
||||
var sessionID string
|
||||
if r.Session != (uuid.UUID{}) {
|
||||
sessionID = r.Session.String()
|
||||
}
|
||||
aux := &invokeAux{
|
||||
GasConsumed: r.GasConsumed,
|
||||
Script: r.Script,
|
||||
|
@ -233,41 +201,12 @@ func (r *Invoke) UnmarshalJSON(data []byte) error {
|
|||
break
|
||||
}
|
||||
if st[i].Type() == stackitem.InteropT {
|
||||
iteratorAux := new(iteratorAux)
|
||||
if json.Unmarshal(arr[i], iteratorAux) == nil {
|
||||
if len(iteratorAux.Interface) != 0 {
|
||||
if iteratorAux.Interface != iteratorInterfaceName {
|
||||
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,
|
||||
})
|
||||
}
|
||||
var iter = Iterator{}
|
||||
err = json.Unmarshal(arr[i], &iter)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
st[i] = stackitem.NewInterop(iter)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
|
|
@ -4,8 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"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/util"
|
||||
)
|
||||
|
@ -25,25 +23,6 @@ type TransactionMetadata struct {
|
|||
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.
|
||||
func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) {
|
||||
output, err := json.Marshal(t.TransactionMetadata)
|
||||
|
|
|
@ -35,10 +35,10 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"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/opcode"
|
||||
"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/stretchr/testify/require"
|
||||
)
|
||||
|
@ -648,7 +648,7 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(appLogs))
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@ -1282,7 +1282,7 @@ func TestClient_InvokeAndPackIteratorResults(t *testing.T) {
|
|||
t.Run("default max items constraint", func(t *testing.T) {
|
||||
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil)
|
||||
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, stackitem.ArrayT, res.Stack[0].Type())
|
||||
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||
|
@ -1298,7 +1298,7 @@ func TestClient_InvokeAndPackIteratorResults(t *testing.T) {
|
|||
max := 123
|
||||
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil, max)
|
||||
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, stackitem.ArrayT, res.Stack[0].Type())
|
||||
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
|
||||
"github.com/google/uuid"
|
||||
"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/core"
|
||||
"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
|
||||
// to be filled during the first `traverseiterator` call using corresponding params.
|
||||
iteratorIdentifiers []*iteratorIdentifier
|
||||
// params stores invocation params for historic MPT-based iterator traversing. It is nil in case
|
||||
// of default non-MPT-based sessions mechanism enabled.
|
||||
params *invocationParams
|
||||
timer *time.Timer
|
||||
finalize func()
|
||||
timer *time.Timer
|
||||
finalize func()
|
||||
}
|
||||
// iteratorIdentifier represents Iterator on the server side, holding iterator ID, Iterator stackitem
|
||||
// and iterator index on stack.
|
||||
// iteratorIdentifier represents Iterator on the server side, holding iterator ID and Iterator stackitem.
|
||||
iteratorIdentifier struct {
|
||||
ID string
|
||||
// Item represents Iterator stackitem. It is nil if SessionBackedByMPT is set to true and no `traverseiterator`
|
||||
// call was called for the corresponding session.
|
||||
// Item represents Iterator stackitem.
|
||||
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 {
|
||||
// Concurrent iterator traversal may still be in process, thus need to protect iteratorIdentifiers access.
|
||||
session.iteratorsLock.Lock()
|
||||
if session.finalize != nil {
|
||||
session.finalize()
|
||||
}
|
||||
session.finalize()
|
||||
if !session.timer.Stop() {
|
||||
<-session.timer.C
|
||||
}
|
||||
|
@ -582,6 +566,19 @@ func (s *Server) blockHashFromParam(param *params.Param) (util.Uint256, *respons
|
|||
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) {
|
||||
param := reqParams.Value(0)
|
||||
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 {
|
||||
return result.NewBlock(block, s.chain), nil
|
||||
res := result.Block{
|
||||
Block: *block,
|
||||
BlockMetadata: s.fillBlockMetadata(block, &block.Header),
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
writer := io.NewBufBinWriter()
|
||||
block.EncodeBinary(writer.BinWriter)
|
||||
|
@ -829,7 +830,7 @@ contract_loop:
|
|||
curAsset := &bs.Balances[len(bs.Balances)-1]
|
||||
for i := range toks {
|
||||
id, err := toks[i].TryBytes()
|
||||
if err != nil || len(id) > storage.MaxStorageKeyLen {
|
||||
if err != nil || len(id) > limits.MaxStorageKeyLen {
|
||||
continue
|
||||
}
|
||||
var amount = "1"
|
||||
|
@ -1542,8 +1543,11 @@ func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *respo
|
|||
return nil, response.ErrUnknownTransaction
|
||||
}
|
||||
if v, _ := reqParams.Value(1).GetBoolean(); v {
|
||||
if height == math.MaxUint32 {
|
||||
return result.NewTransactionOutputRaw(tx, nil, nil, s.chain), nil
|
||||
res := result.TransactionOutputRaw{
|
||||
Transaction: *tx,
|
||||
}
|
||||
if height == math.MaxUint32 { // Mempooled transaction.
|
||||
return res, nil
|
||||
}
|
||||
_header := s.chain.GetHeaderHash(int(height))
|
||||
header, err := s.chain.GetHeader(_header)
|
||||
|
@ -1557,7 +1561,13 @@ func (s *Server) getrawtransaction(reqParams params.Params) (interface{}, *respo
|
|||
if len(aers) == 0 {
|
||||
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
|
||||
}
|
||||
|
@ -1630,7 +1640,11 @@ func (s *Server) getBlockHeader(reqParams params.Params) (interface{}, *response
|
|||
}
|
||||
|
||||
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()
|
||||
|
@ -2006,64 +2020,117 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
|
|||
if err != nil {
|
||||
faultException = err.Error()
|
||||
}
|
||||
var registerIterator result.RegisterIterator
|
||||
if s.config.SessionEnabled {
|
||||
registerIterator = func(sessionID string, item stackitem.Item, stackIndex int, finalize func()) (uuid.UUID, error) {
|
||||
iterID := uuid.New()
|
||||
items := ic.VM.Estack().ToArray()
|
||||
sess := s.postProcessExecStack(items)
|
||||
var id uuid.UUID
|
||||
|
||||
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()
|
||||
defer s.sessionsLock.Unlock()
|
||||
if len(s.sessions) == 0 {
|
||||
return
|
||||
}
|
||||
sess, ok := s.sessions[sessionID]
|
||||
if !ok {
|
||||
if len(s.sessions) >= s.config.SessionPoolSize {
|
||||
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
|
||||
}
|
||||
return
|
||||
}
|
||||
sess.iteratorIdentifiers = append(sess.iteratorIdentifiers, &iteratorIdentifier{
|
||||
ID: iterID.String(),
|
||||
Item: item,
|
||||
StackIndex: stackIndex,
|
||||
})
|
||||
s.sessions[sessionID] = sess
|
||||
sess.iteratorsLock.Lock()
|
||||
sess.finalize()
|
||||
delete(s.sessions, sessionID)
|
||||
sess.iteratorsLock.Unlock()
|
||||
})
|
||||
s.sessionsLock.Lock()
|
||||
if len(s.sessions) >= s.config.SessionPoolSize {
|
||||
ic.Finalize()
|
||||
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) {
|
||||
|
@ -2102,43 +2169,11 @@ func (s *Server) traverseIterator(reqParams params.Params) (interface{}, *respon
|
|||
s.sessionsLock.Unlock()
|
||||
|
||||
var (
|
||||
iIDStr = iID.String()
|
||||
iVals []stackitem.Item
|
||||
respErr *response.Error
|
||||
iIDStr = iID.String()
|
||||
iVals []stackitem.Item
|
||||
)
|
||||
for _, it := range session.iteratorIdentifiers {
|
||||
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)
|
||||
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,
|
||||
// we need to perform finalisation under iteratorsLock.
|
||||
session.iteratorsLock.Lock()
|
||||
if session.finalize != nil {
|
||||
session.finalize()
|
||||
}
|
||||
session.finalize()
|
||||
if !session.timer.Stop() {
|
||||
<-session.timer.C
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"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/crypto/hash"
|
||||
"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"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"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/invocations"
|
||||
"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/vmstate"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -102,7 +103,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
assert.Equal(t, 1, len(res.Executions))
|
||||
assert.Equal(t, expectedTxHash, res.Container)
|
||||
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, trigger.OnPersist, res.Executions[0].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, 1, len(res.Executions))
|
||||
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, 1, len(res.Executions))
|
||||
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, []stackitem.Item{stackitem.Make(true)}, res.Stack)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
chg := []storage.Operation{{
|
||||
chg := []dboper.Operation{{
|
||||
State: "Changed",
|
||||
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
|
||||
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")},
|
||||
Notifications: []state.NotificationEvent{},
|
||||
Diagnostics: &result.InvokeDiag{
|
||||
Changes: []storage.Operation{},
|
||||
Invocations: []*vm.InvocationTree{{
|
||||
Changes: []dboper.Operation{},
|
||||
Invocations: []*invocations.Tree{{
|
||||
Current: hash.Hash160(script),
|
||||
Calls: []*vm.InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: nnsHash,
|
||||
Calls: []*vm.InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: stdHash,
|
||||
},
|
||||
|
@ -1073,13 +1074,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
|
||||
Notifications: []state.NotificationEvent{},
|
||||
Diagnostics: &result.InvokeDiag{
|
||||
Changes: []storage.Operation{},
|
||||
Invocations: []*vm.InvocationTree{{
|
||||
Changes: []dboper.Operation{},
|
||||
Invocations: []*invocations.Tree{{
|
||||
Current: hash.Hash160(script),
|
||||
Calls: []*vm.InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: nnsHash,
|
||||
Calls: []*vm.InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: stdHash,
|
||||
},
|
||||
|
@ -1165,8 +1166,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
FaultException: "at instruction 0 (ROT): too big index",
|
||||
Notifications: []state.NotificationEvent{},
|
||||
Diagnostics: &result.InvokeDiag{
|
||||
Changes: []storage.Operation{},
|
||||
Invocations: []*vm.InvocationTree{{
|
||||
Changes: []dboper.Operation{},
|
||||
Invocations: []*invocations.Tree{{
|
||||
Current: hash.Hash160(script),
|
||||
}},
|
||||
},
|
||||
|
@ -1276,8 +1277,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
FaultException: "at instruction 0 (ROT): too big index",
|
||||
Notifications: []state.NotificationEvent{},
|
||||
Diagnostics: &result.InvokeDiag{
|
||||
Changes: []storage.Operation{},
|
||||
Invocations: []*vm.InvocationTree{{
|
||||
Changes: []dboper.Operation{},
|
||||
Invocations: []*invocations.Tree{{
|
||||
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.Equal(t, 2, len(res.Executions))
|
||||
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, vm.HaltState, res.Executions[1].VMState)
|
||||
require.Equal(t, vmstate.Halt, res.Executions[1].VMState)
|
||||
})
|
||||
|
||||
t.Run("submit", func(t *testing.T) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"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/stackitem"
|
||||
)
|
||||
|
@ -53,7 +54,7 @@ type Context struct {
|
|||
// NEF represents a NEF file for the current contract.
|
||||
NEF *nef.File
|
||||
// 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
|
||||
// if no exception occurs.
|
||||
onUnload ContextUnloadCallback
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// InvocationTree represents a tree with script hashes; when traversing it,
|
||||
// you can see how contracts called each other.
|
||||
type InvocationTree struct {
|
||||
Current util.Uint160 `json:"hash"`
|
||||
Calls []*InvocationTree `json:"call,omitempty"`
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"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/stretchr/testify/require"
|
||||
)
|
||||
|
@ -36,13 +37,13 @@ func TestInvocationTree(t *testing.T) {
|
|||
topHash := v.Context().ScriptHash()
|
||||
require.NoError(t, v.Run())
|
||||
|
||||
res := &InvocationTree{
|
||||
Calls: []*InvocationTree{{
|
||||
res := &invocations.Tree{
|
||||
Calls: []*invocations.Tree{{
|
||||
Current: topHash,
|
||||
Calls: []*InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: util.Uint160{1},
|
||||
Calls: []*InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: util.Uint160{2},
|
||||
},
|
||||
|
@ -53,7 +54,7 @@ func TestInvocationTree(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Current: util.Uint160{4},
|
||||
Calls: []*InvocationTree{
|
||||
Calls: []*invocations.Tree{
|
||||
{
|
||||
Current: util.Uint160{5},
|
||||
},
|
||||
|
|
12
pkg/vm/invocations/invocation_tree.go
Normal file
12
pkg/vm/invocations/invocation_tree.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package invocations
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Tree represents a tree with script hashes; when traversing it,
|
||||
// you can see how contracts called each other.
|
||||
type Tree struct {
|
||||
Current util.Uint160 `json:"hash"`
|
||||
Calls []*Tree `json:"call,omitempty"`
|
||||
}
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -44,7 +45,7 @@ type (
|
|||
}
|
||||
|
||||
vmUTExecutionEngineState struct {
|
||||
State State `json:"state"`
|
||||
State vmstate.State `json:"state"`
|
||||
ResultStack []vmUTStackItem `json:"resultStack"`
|
||||
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) {
|
||||
prog := []byte(test.Script)
|
||||
vm := load(prog)
|
||||
vm.state = BreakState
|
||||
vm.state = vmstate.Break
|
||||
vm.SyscallHandler = testSyscallHandler
|
||||
|
||||
for i := range test.Steps {
|
||||
execStep(t, vm, test.Steps[i])
|
||||
result := test.Steps[i].Result
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStateFromString(t *testing.T) {
|
||||
var (
|
||||
s State
|
||||
err error
|
||||
)
|
||||
|
||||
s, err = StateFromString("HALT")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, HaltState, s)
|
||||
|
||||
s, err = StateFromString("BREAK")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, BreakState, s)
|
||||
|
||||
s, err = StateFromString("FAULT")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, FaultState, s)
|
||||
|
||||
s, err = StateFromString("NONE")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, NoneState, s)
|
||||
|
||||
s, err = StateFromString("HALT, BREAK")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, HaltState|BreakState, s)
|
||||
|
||||
s, err = StateFromString("FAULT, BREAK")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, FaultState|BreakState, s)
|
||||
|
||||
_, err = StateFromString("HALT, KEK")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestState_HasFlag(t *testing.T) {
|
||||
assert.True(t, HaltState.HasFlag(HaltState))
|
||||
assert.True(t, BreakState.HasFlag(BreakState))
|
||||
assert.True(t, FaultState.HasFlag(FaultState))
|
||||
assert.True(t, (HaltState | BreakState).HasFlag(HaltState))
|
||||
assert.True(t, (HaltState | BreakState).HasFlag(BreakState))
|
||||
|
||||
assert.False(t, HaltState.HasFlag(BreakState))
|
||||
assert.False(t, NoneState.HasFlag(HaltState))
|
||||
assert.False(t, (FaultState | BreakState).HasFlag(HaltState))
|
||||
}
|
||||
|
||||
func TestState_MarshalJSON(t *testing.T) {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
|
||||
data, err = json.Marshal(HaltState | BreakState)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, []byte(`"HALT, BREAK"`))
|
||||
|
||||
data, err = json.Marshal(FaultState)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, []byte(`"FAULT"`))
|
||||
}
|
||||
|
||||
func TestState_UnmarshalJSON(t *testing.T) {
|
||||
var (
|
||||
s State
|
||||
err error
|
||||
)
|
||||
|
||||
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, HaltState|BreakState, s)
|
||||
|
||||
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, FaultState|BreakState, s)
|
||||
|
||||
err = json.Unmarshal([]byte(`"NONE"`), &s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, NoneState, s)
|
||||
}
|
||||
|
||||
// TestState_EnumCompat tests that byte value of State matches the C#'s one got from
|
||||
// https://github.com/neo-project/neo-vm/blob/0028d862e253bda3c12eb8bb007a2d95822d3922/src/neo-vm/VMState.cs#L16.
|
||||
func TestState_EnumCompat(t *testing.T) {
|
||||
assert.Equal(t, byte(0), byte(NoneState))
|
||||
assert.Equal(t, byte(1<<0), byte(HaltState))
|
||||
assert.Equal(t, byte(1<<1), byte(FaultState))
|
||||
assert.Equal(t, byte(1<<2), byte(BreakState))
|
||||
}
|
80
pkg/vm/vm.go
80
pkg/vm/vm.go
|
@ -21,8 +21,10 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"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/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
)
|
||||
|
||||
type errorAtInstruct struct {
|
||||
|
@ -62,7 +64,7 @@ type SyscallHandler = func(*VM, uint32) error
|
|||
|
||||
// VM represents the virtual machine.
|
||||
type VM struct {
|
||||
state State
|
||||
state vmstate.State
|
||||
|
||||
// callback to get interop price
|
||||
getPrice func(opcode.Opcode, []byte) int64
|
||||
|
@ -86,7 +88,7 @@ type VM struct {
|
|||
trigger trigger.Type
|
||||
|
||||
// invTree is a top-level invocation tree (if enabled).
|
||||
invTree *InvocationTree
|
||||
invTree *invocations.Tree
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -104,7 +106,7 @@ func New() *VM {
|
|||
// NewWithTrigger returns a new VM for executions triggered by t.
|
||||
func NewWithTrigger(t trigger.Type) *VM {
|
||||
vm := &VM{
|
||||
state: NoneState,
|
||||
state: vmstate.None,
|
||||
trigger: t,
|
||||
|
||||
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
|
||||
// itself.
|
||||
func (v *VM) Reset(t trigger.Type) {
|
||||
v.state = NoneState
|
||||
v.state = vmstate.None
|
||||
v.getPrice = nil
|
||||
v.istack.elems = v.istack.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.
|
||||
func (v *VM) EnableInvocationTree() {
|
||||
v.invTree = &InvocationTree{}
|
||||
v.invTree = &invocations.Tree{}
|
||||
}
|
||||
|
||||
// GetInvocationTree returns the current invocation tree structure.
|
||||
func (v *VM) GetInvocationTree() *InvocationTree {
|
||||
func (v *VM) GetInvocationTree() *invocations.Tree {
|
||||
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.
|
||||
v.istack.Clear()
|
||||
v.estack.Clear()
|
||||
v.state = NoneState
|
||||
v.state = vmstate.None
|
||||
v.gasConsumed = 0
|
||||
v.invTree = nil
|
||||
v.LoadScriptWithFlags(prog, f)
|
||||
|
@ -355,7 +357,7 @@ func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint
|
|||
if parent != nil {
|
||||
curTree = parent.invTree
|
||||
}
|
||||
newTree := &InvocationTree{Current: ctx.ScriptHash()}
|
||||
newTree := &invocations.Tree{Current: ctx.ScriptHash()}
|
||||
curTree.Calls = append(curTree.Calls, newTree)
|
||||
ctx.invTree = newTree
|
||||
}
|
||||
|
@ -398,7 +400,7 @@ func dumpStack(s *Stack) string {
|
|||
}
|
||||
|
||||
// State returns the state for the VM.
|
||||
func (v *VM) State() State {
|
||||
func (v *VM) State() vmstate.State {
|
||||
return v.state
|
||||
}
|
||||
|
||||
|
@ -413,39 +415,39 @@ func (v *VM) Run() error {
|
|||
var ctx *Context
|
||||
|
||||
if !v.Ready() {
|
||||
v.state = FaultState
|
||||
v.state = vmstate.Fault
|
||||
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
|
||||
// undefined in this case so we can't run anything.
|
||||
return errors.New("VM has failed")
|
||||
}
|
||||
// HaltState (the default) or BreakState are safe to continue.
|
||||
v.state = NoneState
|
||||
// vmstate.Halt (the default) or vmstate.Break are safe to continue.
|
||||
v.state = vmstate.None
|
||||
ctx = v.Context()
|
||||
for {
|
||||
switch {
|
||||
case v.state.HasFlag(FaultState):
|
||||
case v.state.HasFlag(vmstate.Fault):
|
||||
// Should be caught and reported already by the v.Step(),
|
||||
// but we're checking here anyway just in case.
|
||||
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.
|
||||
return nil
|
||||
case v.state == NoneState:
|
||||
case v.state == vmstate.None:
|
||||
if err := v.step(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
v.state = FaultState
|
||||
v.state = vmstate.Fault
|
||||
return errors.New("unknown state")
|
||||
}
|
||||
// check for breakpoint before executing the next instruction
|
||||
ctx = v.Context()
|
||||
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 {
|
||||
op, param, err := ctx.Next()
|
||||
if err != nil {
|
||||
v.state = FaultState
|
||||
v.state = vmstate.Fault
|
||||
return newError(ctx.ip, op, err)
|
||||
}
|
||||
return v.execute(ctx, op, param)
|
||||
|
@ -472,7 +474,7 @@ func (v *VM) StepInto() error {
|
|||
ctx := v.Context()
|
||||
|
||||
if ctx == nil {
|
||||
v.state = HaltState
|
||||
v.state = vmstate.Halt
|
||||
}
|
||||
|
||||
if v.HasStopped() {
|
||||
|
@ -482,7 +484,7 @@ func (v *VM) StepInto() error {
|
|||
if ctx != nil && ctx.prog != nil {
|
||||
op, param, err := ctx.Next()
|
||||
if err != nil {
|
||||
v.state = FaultState
|
||||
v.state = vmstate.Fault
|
||||
return newError(ctx.ip, op, err)
|
||||
}
|
||||
vErr := v.execute(ctx, op, param)
|
||||
|
@ -493,7 +495,7 @@ func (v *VM) StepInto() error {
|
|||
|
||||
cctx := v.Context()
|
||||
if cctx != nil && cctx.atBreakPoint() {
|
||||
v.state = BreakState
|
||||
v.state = vmstate.Break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -501,16 +503,16 @@ func (v *VM) StepInto() error {
|
|||
// StepOut takes the debugger to the line where the current function was called.
|
||||
func (v *VM) StepOut() error {
|
||||
var err error
|
||||
if v.state == BreakState {
|
||||
v.state = NoneState
|
||||
if v.state == vmstate.Break {
|
||||
v.state = vmstate.None
|
||||
}
|
||||
|
||||
expSize := v.istack.Len()
|
||||
for v.state == NoneState && v.istack.Len() >= expSize {
|
||||
for v.state == vmstate.None && v.istack.Len() >= expSize {
|
||||
err = v.StepInto()
|
||||
}
|
||||
if v.state == NoneState {
|
||||
v.state = BreakState
|
||||
if v.state == vmstate.None {
|
||||
v.state = vmstate.Break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -523,20 +525,20 @@ func (v *VM) StepOver() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if v.state == BreakState {
|
||||
v.state = NoneState
|
||||
if v.state == vmstate.Break {
|
||||
v.state = vmstate.None
|
||||
}
|
||||
|
||||
expSize := v.istack.Len()
|
||||
for {
|
||||
err = v.StepInto()
|
||||
if !(v.state == NoneState && v.istack.Len() > expSize) {
|
||||
if !(v.state == vmstate.None && v.istack.Len() > expSize) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if v.state == NoneState {
|
||||
v.state = BreakState
|
||||
if v.state == vmstate.None {
|
||||
v.state = vmstate.Break
|
||||
}
|
||||
|
||||
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
|
||||
// check status after Run.
|
||||
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.
|
||||
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.
|
||||
func (v *VM) HasHalted() bool {
|
||||
return v.state.HasFlag(HaltState)
|
||||
return v.state.HasFlag(vmstate.Halt)
|
||||
}
|
||||
|
||||
// AtBreakpoint returns whether the VM is at breakpoint.
|
||||
func (v *VM) AtBreakpoint() bool {
|
||||
return v.state.HasFlag(BreakState)
|
||||
return v.state.HasFlag(vmstate.Break)
|
||||
}
|
||||
|
||||
// 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.
|
||||
defer func() {
|
||||
if errRecover := recover(); errRecover != nil {
|
||||
v.state = FaultState
|
||||
v.state = vmstate.Fault
|
||||
err = newError(ctx.ip, op, errRecover)
|
||||
} else if v.refs > MaxStackSize {
|
||||
v.state = FaultState
|
||||
v.state = vmstate.Fault
|
||||
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)
|
||||
if v.istack.Len() == 0 {
|
||||
v.state = HaltState
|
||||
v.state = vmstate.Halt
|
||||
break
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
package vm
|
||||
/*
|
||||
Package vmstate contains a set of VM state flags along with appropriate type.
|
||||
It provides a set of conversion/marshaling functions/methods for this type as
|
||||
well. This package is made to make VM state reusable across all of the other
|
||||
components that need it without importing whole VM package.
|
||||
*/
|
||||
package vmstate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// State of the VM.
|
||||
// State of the VM. It's a set of flags stored in the integer number.
|
||||
type State uint8
|
||||
|
||||
// Available States.
|
||||
const (
|
||||
// HaltState represents HALT VM state.
|
||||
HaltState State = 1 << iota
|
||||
// FaultState represents FAULT VM state.
|
||||
FaultState
|
||||
// BreakState represents BREAK VM state.
|
||||
BreakState
|
||||
// NoneState represents NONE VM state.
|
||||
NoneState State = 0
|
||||
// Halt represents HALT VM state (finished normally).
|
||||
Halt State = 1 << iota
|
||||
// Fault represents FAULT VM state (finished with an error).
|
||||
Fault
|
||||
// Break represents BREAK VM state (running, debug mode).
|
||||
Break
|
||||
// None represents NONE VM state (not started yet).
|
||||
None State = 0
|
||||
)
|
||||
|
||||
// HasFlag checks for State flag presence.
|
||||
|
@ -25,40 +31,40 @@ func (s State) HasFlag(f State) bool {
|
|||
return s&f != 0
|
||||
}
|
||||
|
||||
// String implements the stringer interface.
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (s State) String() string {
|
||||
if s == NoneState {
|
||||
if s == None {
|
||||
return "NONE"
|
||||
}
|
||||
|
||||
ss := make([]string, 0, 3)
|
||||
if s.HasFlag(HaltState) {
|
||||
if s.HasFlag(Halt) {
|
||||
ss = append(ss, "HALT")
|
||||
}
|
||||
if s.HasFlag(FaultState) {
|
||||
if s.HasFlag(Fault) {
|
||||
ss = append(ss, "FAULT")
|
||||
}
|
||||
if s.HasFlag(BreakState) {
|
||||
if s.HasFlag(Break) {
|
||||
ss = append(ss, "BREAK")
|
||||
}
|
||||
return strings.Join(ss, ", ")
|
||||
}
|
||||
|
||||
// StateFromString converts a string into the VM State.
|
||||
func StateFromString(s string) (st State, err error) {
|
||||
// FromString converts a string into the State.
|
||||
func FromString(s string) (st State, err error) {
|
||||
if s = strings.TrimSpace(s); s == "NONE" {
|
||||
return NoneState, nil
|
||||
return None, nil
|
||||
}
|
||||
|
||||
ss := strings.Split(s, ",")
|
||||
for _, state := range ss {
|
||||
switch state = strings.TrimSpace(state); state {
|
||||
case "HALT":
|
||||
st |= HaltState
|
||||
st |= Halt
|
||||
case "FAULT":
|
||||
st |= FaultState
|
||||
st |= Fault
|
||||
case "BREAK":
|
||||
st |= BreakState
|
||||
st |= Break
|
||||
default:
|
||||
return 0, errors.New("unknown state")
|
||||
}
|
||||
|
@ -78,6 +84,6 @@ func (s *State) UnmarshalJSON(data []byte) (err error) {
|
|||
return errors.New("wrong format")
|
||||
}
|
||||
|
||||
*s, err = StateFromString(string(data[1 : l-1]))
|
||||
*s, err = FromString(string(data[1 : l-1]))
|
||||
return
|
||||
}
|
97
pkg/vm/vmstate/state_test.go
Normal file
97
pkg/vm/vmstate/state_test.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package vmstate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFromString(t *testing.T) {
|
||||
var (
|
||||
s State
|
||||
err error
|
||||
)
|
||||
|
||||
s, err = FromString("HALT")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Halt, s)
|
||||
|
||||
s, err = FromString("BREAK")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Break, s)
|
||||
|
||||
s, err = FromString("FAULT")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Fault, s)
|
||||
|
||||
s, err = FromString("NONE")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, None, s)
|
||||
|
||||
s, err = FromString("HALT, BREAK")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Halt|Break, s)
|
||||
|
||||
s, err = FromString("FAULT, BREAK")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Fault|Break, s)
|
||||
|
||||
_, err = FromString("HALT, KEK")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestState_HasFlag(t *testing.T) {
|
||||
assert.True(t, Halt.HasFlag(Halt))
|
||||
assert.True(t, Break.HasFlag(Break))
|
||||
assert.True(t, Fault.HasFlag(Fault))
|
||||
assert.True(t, (Halt | Break).HasFlag(Halt))
|
||||
assert.True(t, (Halt | Break).HasFlag(Break))
|
||||
|
||||
assert.False(t, Halt.HasFlag(Break))
|
||||
assert.False(t, None.HasFlag(Halt))
|
||||
assert.False(t, (Fault | Break).HasFlag(Halt))
|
||||
}
|
||||
|
||||
func TestState_MarshalJSON(t *testing.T) {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
|
||||
data, err = json.Marshal(Halt | Break)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, []byte(`"HALT, BREAK"`))
|
||||
|
||||
data, err = json.Marshal(Fault)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, []byte(`"FAULT"`))
|
||||
}
|
||||
|
||||
func TestState_UnmarshalJSON(t *testing.T) {
|
||||
var (
|
||||
s State
|
||||
err error
|
||||
)
|
||||
|
||||
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Halt|Break, s)
|
||||
|
||||
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, Fault|Break, s)
|
||||
|
||||
err = json.Unmarshal([]byte(`"NONE"`), &s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, None, s)
|
||||
}
|
||||
|
||||
// TestState_EnumCompat tests that byte value of State matches the C#'s one got from
|
||||
// https://github.com/neo-project/neo-vm/blob/0028d862e253bda3c12eb8bb007a2d95822d3922/src/neo-vm/VMState.cs#L16.
|
||||
func TestState_EnumCompat(t *testing.T) {
|
||||
assert.Equal(t, byte(0), byte(None))
|
||||
assert.Equal(t, byte(1<<0), byte(Halt))
|
||||
assert.Equal(t, byte(1<<1), byte(Fault))
|
||||
assert.Equal(t, byte(1<<2), byte(Break))
|
||||
}
|
Loading…
Reference in a new issue