2022-07-21 13:21:44 +00:00
package rpcsrv
2018-03-23 20:36:59 +00:00
import (
2021-08-05 14:56:17 +00:00
"bytes"
2018-03-23 20:36:59 +00:00
"context"
2020-09-28 11:58:04 +00:00
"crypto/elliptic"
2020-06-04 08:59:22 +00:00
"encoding/binary"
2021-11-17 20:04:50 +00:00
"encoding/hex"
2020-01-14 12:02:38 +00:00
"encoding/json"
2020-08-06 14:44:08 +00:00
"errors"
2018-03-23 20:36:59 +00:00
"fmt"
2020-03-05 12:39:53 +00:00
"math"
2020-07-09 09:57:24 +00:00
"math/big"
2020-03-10 11:56:18 +00:00
"net"
2018-03-23 20:36:59 +00:00
"net/http"
2019-01-25 11:20:35 +00:00
"strconv"
2022-03-21 20:36:19 +00:00
"strings"
2020-05-10 22:00:19 +00:00
"sync"
2020-04-29 12:25:58 +00:00
"time"
2018-03-23 20:36:59 +00:00
2022-07-07 19:03:11 +00:00
"github.com/google/uuid"
2020-04-29 12:25:58 +00:00
"github.com/gorilla/websocket"
2022-07-22 16:17:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/config"
2022-07-08 16:51:59 +00:00
"github.com/nspcc-dev/neo-go/pkg/config/limits"
2020-06-18 09:00:51 +00:00
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core"
2020-03-02 17:01:32 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/block"
2022-04-07 15:13:08 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/interop"
2021-11-17 20:04:50 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
2022-07-22 19:52:58 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
2021-05-28 11:55:06 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
2020-06-05 08:51:39 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
2021-10-07 09:03:37 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/native"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/state"
2022-07-08 20:25:22 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/storage"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
2020-09-28 11:58:04 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
2020-03-05 14:48:30 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
2022-07-22 16:09:29 +00:00
"github.com/nspcc-dev/neo-go/pkg/neorpc"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
2022-10-17 10:31:24 +00:00
"github.com/nspcc-dev/neo-go/pkg/neorpc/rpcevent"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/network"
2021-02-05 07:26:23 +00:00
"github.com/nspcc-dev/neo-go/pkg/network/payload"
2021-02-07 19:01:22 +00:00
"github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
2022-07-22 16:09:29 +00:00
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/params"
2022-08-22 15:50:35 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
2020-12-29 10:45:49 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
2022-08-22 15:50:35 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
2022-12-10 08:35:03 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard"
2020-11-11 15:43:28 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2022-08-22 15:50:35 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm"
2021-07-25 12:00:44 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
2021-03-10 14:43:52 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
2021-10-07 09:03:37 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
2022-04-22 07:33:52 +00:00
"go.uber.org/atomic"
2019-12-30 08:44:52 +00:00
"go.uber.org/zap"
2018-03-23 20:36:59 +00:00
)
type (
2022-07-22 19:52:58 +00:00
// Ledger abstracts away the Blockchain as used by the RPC server.
Ledger interface {
AddBlock ( block * block . Block ) error
BlockHeight ( ) uint32
CalculateClaimable ( h util . Uint160 , endHeight uint32 ) ( * big . Int , error )
CurrentBlockHash ( ) util . Uint256
FeePerByte ( ) int64
ForEachNEP11Transfer ( acc util . Uint160 , newestTimestamp uint64 , f func ( * state . NEP11Transfer ) ( bool , error ) ) error
ForEachNEP17Transfer ( acc util . Uint160 , newestTimestamp uint64 , f func ( * state . NEP17Transfer ) ( bool , error ) ) error
GetAppExecResults ( util . Uint256 , trigger . Type ) ( [ ] state . AppExecResult , error )
GetBaseExecFee ( ) int64
GetBlock ( hash util . Uint256 ) ( * block . Block , error )
GetCommittee ( ) ( keys . PublicKeys , error )
2022-12-06 13:34:38 +00:00
GetConfig ( ) config . Blockchain
2022-07-22 19:52:58 +00:00
GetContractScriptHash ( id int32 ) ( util . Uint160 , error )
GetContractState ( hash util . Uint160 ) * state . Contract
GetEnrollments ( ) ( [ ] state . Validator , error )
GetGoverningTokenBalance ( acc util . Uint160 ) ( * big . Int , uint32 )
GetHeader ( hash util . Uint256 ) ( * block . Header , error )
2022-11-18 20:19:50 +00:00
GetHeaderHash ( uint32 ) util . Uint256
2022-07-22 19:52:58 +00:00
GetMaxVerificationGAS ( ) int64
GetMemPool ( ) * mempool . Pool
GetNEP11Contracts ( ) [ ] util . Uint160
GetNEP17Contracts ( ) [ ] util . Uint160
GetNativeContractScriptHash ( string ) ( util . Uint160 , error )
GetNatives ( ) [ ] state . NativeContract
GetNextBlockValidators ( ) ( [ ] * keys . PublicKey , error )
GetNotaryContractScriptHash ( ) util . Uint160
GetNotaryServiceFeePerKey ( ) int64
2022-07-22 20:14:02 +00:00
GetStateModule ( ) core . StateRoot
2022-07-22 19:52:58 +00:00
GetStorageItem ( id int32 , key [ ] byte ) state . StorageItem
2022-10-06 10:24:57 +00:00
GetTestHistoricVM ( t trigger . Type , tx * transaction . Transaction , nextBlockHeight uint32 ) ( * interop . Context , error )
GetTestVM ( t trigger . Type , tx * transaction . Transaction , b * block . Block ) ( * interop . Context , error )
2022-07-22 19:52:58 +00:00
GetTokenLastUpdated ( acc util . Uint160 ) ( map [ int32 ] uint32 , error )
GetTransaction ( util . Uint256 ) ( * transaction . Transaction , uint32 , error )
GetValidators ( ) ( [ ] * keys . PublicKey , error )
HeaderHeight ( ) uint32
InitVerificationContext ( ic * interop . Context , hash util . Uint160 , witness * transaction . Witness ) error
2022-08-19 17:47:55 +00:00
SubscribeForBlocks ( ch chan * block . Block )
SubscribeForExecutions ( ch chan * state . AppExecResult )
SubscribeForNotifications ( ch chan * state . ContainedNotificationEvent )
SubscribeForTransactions ( ch chan * transaction . Transaction )
UnsubscribeFromBlocks ( ch chan * block . Block )
UnsubscribeFromExecutions ( ch chan * state . AppExecResult )
UnsubscribeFromNotifications ( ch chan * state . ContainedNotificationEvent )
UnsubscribeFromTransactions ( ch chan * transaction . Transaction )
2022-07-22 19:52:58 +00:00
VerifyTx ( * transaction . Transaction ) error
VerifyWitness ( util . Uint160 , hash . Hashable , * transaction . Witness , int64 ) ( int64 , error )
mempool . Feer // fee interface
}
2022-07-26 15:27:33 +00:00
// OracleHandler is the interface oracle service needs to provide for the Server.
OracleHandler interface {
AddResponse ( pub * keys . PublicKey , reqID uint64 , txSig [ ] byte )
}
2018-03-23 20:36:59 +00:00
// Server represents the JSON-RPC 2.0 server.
Server struct {
2022-11-25 10:20:53 +00:00
http [ ] * http . Server
https [ ] * http . Server
2022-07-22 19:52:58 +00:00
chain Ledger
2022-07-22 16:17:48 +00:00
config config . RPC
2022-05-18 09:45:44 +00:00
// wsReadLimit represents web-socket message limit for a receiving side.
wsReadLimit int64
2022-11-09 05:55:57 +00:00
upgrader websocket . Upgrader
2020-11-17 12:57:50 +00:00
network netmode . Magic
stateRootEnabled bool
coreServer * network . Server
2022-07-26 18:36:37 +00:00
oracle * atomic . Value
2020-11-17 12:57:50 +00:00
log * zap . Logger
shutdown chan struct { }
2022-04-22 07:33:52 +00:00
started * atomic . Bool
2022-04-22 07:49:06 +00:00
errChan chan error
2020-05-10 22:00:19 +00:00
2022-06-15 18:23:29 +00:00
sessionsLock sync . Mutex
sessions map [ string ] * session
2022-11-17 13:00:22 +00:00
subsLock sync . RWMutex
subscribers map [ * subscriber ] bool
subsCounterLock sync . RWMutex
2021-05-28 11:55:06 +00:00
blockSubs int
executionSubs int
notificationSubs int
transactionSubs int
notaryRequestSubs int
2022-11-17 13:00:22 +00:00
blockCh chan * block . Block
executionCh chan * state . AppExecResult
notificationCh chan * state . ContainedNotificationEvent
transactionCh chan * transaction . Transaction
notaryRequestCh chan mempoolevent . Event
2018-03-23 20:36:59 +00:00
}
2022-06-15 18:23:29 +00:00
// session holds a set of iterators got after invoke* call with corresponding
2022-07-07 19:03:11 +00:00
// finalizer and session expiration timer.
2022-06-15 18:23:29 +00:00
session struct {
2022-07-07 19:03:11 +00:00
// iteratorsLock protects iteratorIdentifiers of the current session.
iteratorsLock sync . Mutex
// iteratorIdentifiers stores the set of Iterator stackitems got either from original invocation
// 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
2022-07-08 20:25:22 +00:00
timer * time . Timer
finalize func ( )
}
// iteratorIdentifier represents Iterator on the server side, holding iterator ID and Iterator stackitem.
2022-07-07 19:03:11 +00:00
iteratorIdentifier struct {
ID string
2022-07-08 20:25:22 +00:00
// Item represents Iterator stackitem.
2022-07-07 19:03:11 +00:00
Item stackitem . Item
2022-06-15 18:23:29 +00:00
}
2018-03-23 20:36:59 +00:00
)
2020-04-29 12:25:58 +00:00
const (
// Disconnection timeout.
wsPongLimit = 60 * time . Second
// Ping period for connection liveness check.
wsPingPeriod = wsPongLimit / 2
// Write deadline.
wsWriteLimit = wsPingPeriod / 2
2020-05-10 22:00:19 +00:00
2022-11-23 09:19:49 +00:00
// Default maximum number of websocket clients per Server.
defaultMaxWebSocketClients = 64
2020-09-14 14:48:17 +00:00
// Maximum number of elements for get*transfers requests.
maxTransfersLimit = 1000
2022-07-08 12:42:27 +00:00
// defaultSessionPoolSize is the number of concurrently running iterator sessions.
defaultSessionPoolSize = 20
2020-04-29 12:25:58 +00:00
)
2022-07-22 16:09:29 +00:00
var rpcHandlers = map [ string ] func ( * Server , params . Params ) ( interface { } , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
"calculatenetworkfee" : ( * Server ) . calculateNetworkFee ,
"findstates" : ( * Server ) . findStates ,
"getapplicationlog" : ( * Server ) . getApplicationLog ,
"getbestblockhash" : ( * Server ) . getBestBlockHash ,
"getblock" : ( * Server ) . getBlock ,
"getblockcount" : ( * Server ) . getBlockCount ,
"getblockhash" : ( * Server ) . getBlockHash ,
"getblockheader" : ( * Server ) . getBlockHeader ,
"getblockheadercount" : ( * Server ) . getBlockHeaderCount ,
"getblocksysfee" : ( * Server ) . getBlockSysFee ,
2022-07-01 13:02:03 +00:00
"getcandidates" : ( * Server ) . getCandidates ,
2022-04-07 15:13:08 +00:00
"getcommittee" : ( * Server ) . getCommittee ,
"getconnectioncount" : ( * Server ) . getConnectionCount ,
"getcontractstate" : ( * Server ) . getContractState ,
"getnativecontracts" : ( * Server ) . getNativeContracts ,
"getnep11balances" : ( * Server ) . getNEP11Balances ,
"getnep11properties" : ( * Server ) . getNEP11Properties ,
"getnep11transfers" : ( * Server ) . getNEP11Transfers ,
"getnep17balances" : ( * Server ) . getNEP17Balances ,
"getnep17transfers" : ( * Server ) . getNEP17Transfers ,
"getpeers" : ( * Server ) . getPeers ,
"getproof" : ( * Server ) . getProof ,
"getrawmempool" : ( * Server ) . getRawMempool ,
"getrawtransaction" : ( * Server ) . getrawtransaction ,
"getstate" : ( * Server ) . getState ,
"getstateheight" : ( * Server ) . getStateHeight ,
"getstateroot" : ( * Server ) . getStateRoot ,
"getstorage" : ( * Server ) . getStorage ,
"gettransactionheight" : ( * Server ) . getTransactionHeight ,
"getunclaimedgas" : ( * Server ) . getUnclaimedGas ,
"getnextblockvalidators" : ( * Server ) . getNextBlockValidators ,
"getversion" : ( * Server ) . getVersion ,
"invokefunction" : ( * Server ) . invokeFunction ,
"invokefunctionhistoric" : ( * Server ) . invokeFunctionHistoric ,
"invokescript" : ( * Server ) . invokescript ,
"invokescripthistoric" : ( * Server ) . invokescripthistoric ,
"invokecontractverify" : ( * Server ) . invokeContractVerify ,
"invokecontractverifyhistoric" : ( * Server ) . invokeContractVerifyHistoric ,
"sendrawtransaction" : ( * Server ) . sendrawtransaction ,
"submitblock" : ( * Server ) . submitBlock ,
"submitnotaryrequest" : ( * Server ) . submitNotaryRequest ,
"submitoracleresponse" : ( * Server ) . submitOracleResponse ,
2022-06-15 18:23:29 +00:00
"terminatesession" : ( * Server ) . terminateSession ,
"traverseiterator" : ( * Server ) . traverseIterator ,
2022-04-07 15:13:08 +00:00
"validateaddress" : ( * Server ) . validateAddress ,
"verifyproof" : ( * Server ) . verifyProof ,
2020-03-13 07:29:49 +00:00
}
2022-07-22 16:09:29 +00:00
var rpcWsHandlers = map [ string ] func ( * Server , params . Params , * subscriber ) ( interface { } , * neorpc . Error ) {
2020-05-10 22:00:19 +00:00
"subscribe" : ( * Server ) . subscribe ,
"unsubscribe" : ( * Server ) . unsubscribe ,
}
2022-07-22 16:09:29 +00:00
var invalidBlockHeightError = func ( index int , height int ) * neorpc . Error {
return neorpc . NewRPCError ( "Invalid block height" , fmt . Sprintf ( "param at index %d should be greater than or equal to 0 and less then or equal to current block height, got: %d" , index , height ) )
2019-11-21 13:42:51 +00:00
}
2018-03-25 10:13:47 +00:00
2020-02-17 12:17:02 +00:00
// New creates a new Server struct.
2022-07-22 19:52:58 +00:00
func New ( chain Ledger , conf config . RPC , coreServer * network . Server ,
2022-07-26 15:27:33 +00:00
orc OracleHandler , log * zap . Logger , errChan chan error ) Server {
2022-11-25 10:20:53 +00:00
addrs := conf . GetAddresses ( )
httpServers := make ( [ ] * http . Server , len ( addrs ) )
for i , addr := range addrs {
httpServers [ i ] = & http . Server {
Addr : addr ,
}
2019-11-01 10:23:46 +00:00
}
2022-11-25 10:20:53 +00:00
var tlsServers [ ] * http . Server
2020-03-10 11:56:18 +00:00
if cfg := conf . TLSConfig ; cfg . Enabled {
2022-11-25 10:20:53 +00:00
addrs := cfg . GetAddresses ( )
tlsServers = make ( [ ] * http . Server , len ( addrs ) )
for i , addr := range addrs {
tlsServers [ i ] = & http . Server {
Addr : addr ,
}
2020-03-10 11:56:18 +00:00
}
}
2022-12-06 13:34:38 +00:00
protoCfg := chain . GetConfig ( ) . ProtocolConfiguration
2022-07-08 12:42:27 +00:00
if conf . SessionEnabled {
if conf . SessionExpirationTime <= 0 {
2022-12-02 16:10:45 +00:00
conf . SessionExpirationTime = int ( protoCfg . TimePerBlock / time . Second )
log . Info ( "SessionExpirationTime is not set or wrong, setting default value" , zap . Int ( "SessionExpirationTime" , conf . SessionExpirationTime ) )
2022-07-08 12:42:27 +00:00
}
if conf . SessionPoolSize <= 0 {
conf . SessionPoolSize = defaultSessionPoolSize
log . Info ( "SessionPoolSize is not set or wrong, setting default value" , zap . Int ( "SessionPoolSize" , defaultSessionPoolSize ) )
}
2022-07-05 16:29:18 +00:00
}
2022-11-23 09:19:49 +00:00
if conf . MaxWebSocketClients == 0 {
conf . MaxWebSocketClients = defaultMaxWebSocketClients
log . Info ( "MaxWebSocketClients is not set or wrong, setting default value" , zap . Int ( "MaxWebSocketClients" , defaultMaxWebSocketClients ) )
}
2022-07-26 18:36:37 +00:00
var oracleWrapped = new ( atomic . Value )
if orc != nil {
oracleWrapped . Store ( & orc )
}
2022-11-09 05:55:57 +00:00
var wsOriginChecker func ( * http . Request ) bool
if conf . EnableCORSWorkaround {
wsOriginChecker = func ( _ * http . Request ) bool { return true }
}
2018-03-23 20:36:59 +00:00
return Server {
2022-11-25 10:20:53 +00:00
http : httpServers ,
https : tlsServers ,
2020-11-17 12:57:50 +00:00
chain : chain ,
config : conf ,
2022-07-05 16:29:18 +00:00
wsReadLimit : int64 ( protoCfg . MaxBlockSize * 4 ) / 3 + 1024 , // Enough for Base64-encoded content of `submitblock` and `submitp2pnotaryrequest`.
2022-11-09 05:55:57 +00:00
upgrader : websocket . Upgrader { CheckOrigin : wsOriginChecker } ,
2022-07-05 16:29:18 +00:00
network : protoCfg . Magic ,
stateRootEnabled : protoCfg . StateRootInHeader ,
2020-11-17 12:57:50 +00:00
coreServer : coreServer ,
log : log ,
2022-07-26 18:36:37 +00:00
oracle : oracleWrapped ,
2020-11-17 12:57:50 +00:00
shutdown : make ( chan struct { } ) ,
2022-04-22 07:33:52 +00:00
started : atomic . NewBool ( false ) ,
2022-04-22 07:49:06 +00:00
errChan : errChan ,
2020-05-10 22:00:19 +00:00
2022-06-15 18:23:29 +00:00
sessions : make ( map [ string ] * session ) ,
2020-05-10 22:00:19 +00:00
subscribers : make ( map [ * subscriber ] bool ) ,
// These are NOT buffered to preserve original order of events.
2021-05-28 11:55:06 +00:00
blockCh : make ( chan * block . Block ) ,
executionCh : make ( chan * state . AppExecResult ) ,
2022-07-22 18:17:23 +00:00
notificationCh : make ( chan * state . ContainedNotificationEvent ) ,
2021-05-28 11:55:06 +00:00
transactionCh : make ( chan * transaction . Transaction ) ,
notaryRequestCh : make ( chan mempoolevent . Event ) ,
2018-03-23 20:36:59 +00:00
}
}
2022-04-22 08:33:56 +00:00
// Name returns service name.
func ( s * Server ) Name ( ) string {
return "rpc"
}
2022-04-22 07:49:06 +00:00
// Start creates a new JSON-RPC server listening on the configured port. It creates
// goroutines needed internally and it returns its errors via errChan passed to New().
2022-07-04 20:03:50 +00:00
// The Server only starts once, subsequent calls to Start are no-op.
2022-04-22 07:49:06 +00:00
func ( s * Server ) Start ( ) {
2019-11-01 10:23:46 +00:00
if ! s . config . Enabled {
2019-12-30 08:44:52 +00:00
s . log . Info ( "RPC server is not enabled" )
2019-11-01 10:23:46 +00:00
return
}
2022-04-22 07:33:52 +00:00
if ! s . started . CAS ( false , true ) {
s . log . Info ( "RPC server already started" )
return
}
2022-11-25 10:20:53 +00:00
for _ , srv := range s . http {
srv . Handler = http . HandlerFunc ( s . handleHTTPRequest )
s . log . Info ( "starting rpc-server" , zap . String ( "endpoint" , srv . Addr ) )
ln , err := net . Listen ( "tcp" , srv . Addr )
if err != nil {
s . errChan <- fmt . Errorf ( "failed to listen on %s: %w" , srv . Addr , err )
return
}
srv . Addr = ln . Addr ( ) . String ( ) // set Addr to the actual address
go func ( server * http . Server ) {
err = server . Serve ( ln )
if ! errors . Is ( err , http . ErrServerClosed ) {
s . log . Error ( "failed to start RPC server" , zap . Error ( err ) )
s . errChan <- err
}
} ( srv )
}
2018-03-23 20:36:59 +00:00
2020-05-10 22:00:19 +00:00
go s . handleSubEvents ( )
2020-03-10 11:56:18 +00:00
if cfg := s . config . TLSConfig ; cfg . Enabled {
2022-11-25 10:20:53 +00:00
for _ , srv := range s . https {
srv . Handler = http . HandlerFunc ( s . handleHTTPRequest )
s . log . Info ( "starting rpc-server (https)" , zap . String ( "endpoint" , srv . Addr ) )
ln , err := net . Listen ( "tcp" , srv . Addr )
2020-08-31 12:35:55 +00:00
if err != nil {
2022-04-22 07:49:06 +00:00
s . errChan <- err
2020-08-31 12:35:55 +00:00
return
}
2022-11-25 10:20:53 +00:00
srv . Addr = ln . Addr ( ) . String ( )
go func ( srv * http . Server ) {
err = srv . ServeTLS ( ln , cfg . CertFile , cfg . KeyFile )
if ! errors . Is ( err , http . ErrServerClosed ) {
s . log . Error ( "failed to start TLS RPC server" ,
zap . String ( "endpoint" , srv . Addr ) , zap . Error ( err ) )
s . errChan <- err
}
} ( srv )
2020-08-31 12:35:55 +00:00
}
2022-11-25 10:20:53 +00:00
}
2018-03-23 20:36:59 +00:00
}
2022-07-04 20:03:50 +00:00
// Shutdown stops the RPC server if it's running. It can only be called once,
// subsequent calls to Shutdown on the same instance are no-op. The instance
// that was stopped can not be started again by calling Start (use a new
// instance if needed).
2022-04-22 07:49:06 +00:00
func ( s * Server ) Shutdown ( ) {
2022-07-01 19:01:04 +00:00
if ! s . started . CAS ( true , false ) {
2022-04-22 08:47:36 +00:00
return
}
2020-05-10 22:00:19 +00:00
// Signal to websocket writer routines and handleSubEvents.
close ( s . shutdown )
2020-03-10 11:56:18 +00:00
if s . config . TLSConfig . Enabled {
2022-11-25 10:20:53 +00:00
for _ , srv := range s . https {
s . log . Info ( "shutting down RPC server (https)" , zap . String ( "endpoint" , srv . Addr ) )
err := srv . Shutdown ( context . Background ( ) )
if err != nil {
s . log . Warn ( "error during RPC (https) server shutdown" ,
zap . String ( "endpoint" , srv . Addr ) , zap . Error ( err ) )
}
2022-04-25 21:13:13 +00:00
}
2020-03-10 11:56:18 +00:00
}
2022-11-25 10:20:53 +00:00
for _ , srv := range s . http {
s . log . Info ( "shutting down RPC server" , zap . String ( "endpoint" , srv . Addr ) )
err := srv . Shutdown ( context . Background ( ) )
if err != nil {
s . log . Warn ( "error during RPC (http) server shutdown" ,
zap . String ( "endpoint" , srv . Addr ) , zap . Error ( err ) )
}
2022-04-25 21:13:13 +00:00
}
2020-05-10 22:00:19 +00:00
2022-06-15 18:23:29 +00:00
// Perform sessions finalisation.
if s . config . SessionEnabled {
s . sessionsLock . Lock ( )
for _ , session := range s . sessions {
// Concurrent iterator traversal may still be in process, thus need to protect iteratorIdentifiers access.
session . iteratorsLock . Lock ( )
2022-07-08 20:25:22 +00:00
session . finalize ( )
2022-06-15 18:23:29 +00:00
if ! session . timer . Stop ( ) {
<- session . timer . C
}
session . iteratorsLock . Unlock ( )
}
s . sessions = nil
s . sessionsLock . Unlock ( )
}
2020-05-10 22:00:19 +00:00
// Wait for handleSubEvents to finish.
<- s . executionCh
2018-03-23 20:36:59 +00:00
}
2022-07-26 18:36:37 +00:00
// SetOracleHandler allows to update oracle handler used by the Server.
func ( s * Server ) SetOracleHandler ( orc OracleHandler ) {
s . oracle . Store ( & orc )
}
2020-04-28 13:56:33 +00:00
func ( s * Server ) handleHTTPRequest ( w http . ResponseWriter , httpRequest * http . Request ) {
2022-07-07 14:41:01 +00:00
req := params . NewRequest ( )
2020-05-10 22:00:19 +00:00
2020-04-29 12:25:58 +00:00
if httpRequest . URL . Path == "/ws" && httpRequest . Method == "GET" {
2020-05-10 22:00:19 +00:00
// Technically there is a race between this check and
// s.subscribers modification 20 lines below, but it's tiny
// and not really critical to bother with it. Some additional
// clients may sneak in, no big deal.
s . subsLock . RLock ( )
numOfSubs := len ( s . subscribers )
s . subsLock . RUnlock ( )
2022-11-23 09:19:49 +00:00
if numOfSubs >= s . config . MaxWebSocketClients {
2020-05-10 22:00:19 +00:00
s . writeHTTPErrorResponse (
2022-07-07 14:41:01 +00:00
params . NewIn ( ) ,
2020-05-10 22:00:19 +00:00
w ,
2022-07-22 16:09:29 +00:00
neorpc . NewInternalServerError ( "websocket users limit reached" ) ,
2020-05-10 22:00:19 +00:00
)
return
}
2022-11-09 05:55:57 +00:00
ws , err := s . upgrader . Upgrade ( w , httpRequest , nil )
2020-04-29 12:25:58 +00:00
if err != nil {
s . log . Info ( "websocket connection upgrade failed" , zap . Error ( err ) )
return
}
2022-06-09 15:19:01 +00:00
resChan := make ( chan abstractResult ) // response.abstract or response.abstractBatch
2020-05-10 22:00:19 +00:00
subChan := make ( chan * websocket . PreparedMessage , notificationBufSize )
subscr := & subscriber { writer : subChan , ws : ws }
s . subsLock . Lock ( )
s . subscribers [ subscr ] = true
s . subsLock . Unlock ( )
go s . handleWsWrites ( ws , resChan , subChan )
s . handleWsReads ( ws , resChan , subscr )
2020-04-29 12:25:58 +00:00
return
}
2022-10-04 18:50:46 +00:00
if httpRequest . Method == "OPTIONS" && s . config . EnableCORSWorkaround { // Preflight CORS.
setCORSOriginHeaders ( w . Header ( ) )
w . Header ( ) . Set ( "Access-Control-Allow-Methods" , "GET, POST" ) // GET for websockets.
w . Header ( ) . Set ( "Access-Control-Max-Age" , "21600" ) // 6 hours.
return
}
2018-03-23 20:36:59 +00:00
if httpRequest . Method != "POST" {
2020-04-28 19:56:19 +00:00
s . writeHTTPErrorResponse (
2022-07-07 14:41:01 +00:00
params . NewIn ( ) ,
2018-03-23 20:36:59 +00:00
w ,
2022-07-22 16:09:29 +00:00
neorpc . NewInvalidParamsError ( fmt . Sprintf ( "invalid method '%s', please retry with 'POST'" , httpRequest . Method ) ) ,
2018-03-23 20:36:59 +00:00
)
return
}
err := req . DecodeData ( httpRequest . Body )
if err != nil {
2022-07-22 16:09:29 +00:00
s . writeHTTPErrorResponse ( params . NewIn ( ) , w , neorpc . NewParseError ( err . Error ( ) ) )
2018-03-23 20:36:59 +00:00
return
}
2020-05-10 22:00:19 +00:00
resp := s . handleRequest ( req , nil )
2020-04-28 19:56:19 +00:00
s . writeHTTPServerResponse ( req , w , resp )
2020-04-28 13:56:33 +00:00
}
2022-07-07 14:41:01 +00:00
func ( s * Server ) handleRequest ( req * params . Request , sub * subscriber ) abstractResult {
2020-10-26 17:22:20 +00:00
if req . In != nil {
2022-03-21 20:36:19 +00:00
req . In . Method = escapeForLog ( req . In . Method ) // No valid method name will be changed by it.
2020-10-26 17:22:20 +00:00
return s . handleIn ( req . In , sub )
}
2022-06-09 15:19:01 +00:00
resp := make ( abstractBatch , len ( req . Batch ) )
2020-10-26 17:22:20 +00:00
for i , in := range req . Batch {
2022-03-21 20:36:19 +00:00
in . Method = escapeForLog ( in . Method ) // No valid method name will be changed by it.
2020-10-26 17:22:20 +00:00
resp [ i ] = s . handleIn ( & in , sub )
}
return resp
}
2022-07-07 14:41:01 +00:00
func ( s * Server ) handleIn ( req * params . In , sub * subscriber ) abstract {
2020-05-10 22:00:19 +00:00
var res interface { }
2022-07-22 16:09:29 +00:00
var resErr * neorpc . Error
if req . JSONRPC != neorpc . JSONRPCVersion {
return s . packResponse ( req , nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "problem parsing JSON: invalid version, expected 2.0 got '%s'" , req . JSONRPC ) ) )
2020-10-26 17:22:20 +00:00
}
2020-05-10 22:00:19 +00:00
2022-07-07 14:41:01 +00:00
reqParams := params . Params ( req . RawParams )
2018-03-23 20:36:59 +00:00
2020-01-13 13:45:36 +00:00
s . log . Debug ( "processing rpc request" ,
2019-12-30 08:44:52 +00:00
zap . String ( "method" , req . Method ) ,
2020-10-08 13:29:30 +00:00
zap . Stringer ( "params" , reqParams ) )
2018-03-23 20:36:59 +00:00
2022-11-09 10:26:45 +00:00
start := time . Now ( )
defer func ( ) { addReqTimeMetric ( req . Method , time . Since ( start ) ) } ( )
2020-03-12 17:36:36 +00:00
2022-07-22 16:09:29 +00:00
resErr = neorpc . NewMethodNotFoundError ( fmt . Sprintf ( "method %q not supported" , req . Method ) )
2020-03-13 07:29:49 +00:00
handler , ok := rpcHandlers [ req . Method ]
2020-05-10 22:00:19 +00:00
if ok {
2021-10-28 11:10:18 +00:00
res , resErr = handler ( s , reqParams )
2020-05-10 22:00:19 +00:00
} else if sub != nil {
handler , ok := rpcWsHandlers [ req . Method ]
if ok {
2021-10-28 11:10:18 +00:00
res , resErr = handler ( s , reqParams , sub )
2020-05-10 22:00:19 +00:00
}
2018-03-23 20:36:59 +00:00
}
2020-08-31 15:27:32 +00:00
return s . packResponse ( req , res , resErr )
2020-03-13 07:01:49 +00:00
}
2022-06-09 15:19:01 +00:00
func ( s * Server ) handleWsWrites ( ws * websocket . Conn , resChan <- chan abstractResult , subChan <- chan * websocket . PreparedMessage ) {
2020-04-29 12:25:58 +00:00
pingTicker := time . NewTicker ( wsPingPeriod )
2020-05-12 19:38:29 +00:00
eventloop :
2020-04-29 12:25:58 +00:00
for {
select {
2020-05-10 22:00:19 +00:00
case <- s . shutdown :
2020-05-12 19:38:29 +00:00
break eventloop
2020-05-10 22:00:19 +00:00
case event , ok := <- subChan :
if ! ok {
2020-05-12 19:38:29 +00:00
break eventloop
2020-05-10 22:00:19 +00:00
}
2021-05-12 18:45:32 +00:00
if err := ws . SetWriteDeadline ( time . Now ( ) . Add ( wsWriteLimit ) ) ; err != nil {
break eventloop
}
2020-05-10 22:00:19 +00:00
if err := ws . WritePreparedMessage ( event ) ; err != nil {
2020-05-12 19:38:29 +00:00
break eventloop
2020-05-10 22:00:19 +00:00
}
2020-04-29 12:25:58 +00:00
case res , ok := <- resChan :
if ! ok {
2020-05-12 19:38:29 +00:00
break eventloop
2020-04-29 12:25:58 +00:00
}
2021-05-12 18:45:32 +00:00
if err := ws . SetWriteDeadline ( time . Now ( ) . Add ( wsWriteLimit ) ) ; err != nil {
break eventloop
}
2020-04-29 12:25:58 +00:00
if err := ws . WriteJSON ( res ) ; err != nil {
2020-05-12 19:38:29 +00:00
break eventloop
2020-04-29 12:25:58 +00:00
}
case <- pingTicker . C :
2021-05-12 18:45:32 +00:00
if err := ws . SetWriteDeadline ( time . Now ( ) . Add ( wsWriteLimit ) ) ; err != nil {
break eventloop
}
2020-04-29 12:25:58 +00:00
if err := ws . WriteMessage ( websocket . PingMessage , [ ] byte { } ) ; err != nil {
2020-05-12 19:38:29 +00:00
break eventloop
2020-04-29 12:25:58 +00:00
}
}
}
2020-05-12 19:38:29 +00:00
ws . Close ( )
pingTicker . Stop ( )
// Drain notification channel as there might be some goroutines blocked
// on it.
drainloop :
for {
select {
case _ , ok := <- subChan :
if ! ok {
break drainloop
}
default :
break drainloop
}
}
2020-04-29 12:25:58 +00:00
}
2022-06-09 15:19:01 +00:00
func ( s * Server ) handleWsReads ( ws * websocket . Conn , resChan chan <- abstractResult , subscr * subscriber ) {
2022-05-18 09:45:44 +00:00
ws . SetReadLimit ( s . wsReadLimit )
2021-05-12 18:45:32 +00:00
err := ws . SetReadDeadline ( time . Now ( ) . Add ( wsPongLimit ) )
ws . SetPongHandler ( func ( string ) error { return ws . SetReadDeadline ( time . Now ( ) . Add ( wsPongLimit ) ) } )
2020-05-10 22:00:19 +00:00
requestloop :
2021-05-12 18:45:32 +00:00
for err == nil {
2022-07-07 14:41:01 +00:00
req := params . NewRequest ( )
2020-04-29 12:25:58 +00:00
err := ws . ReadJSON ( req )
if err != nil {
break
}
2020-05-10 22:00:19 +00:00
res := s . handleRequest ( req , subscr )
2022-07-22 16:09:29 +00:00
res . RunForErrors ( func ( jsonErr * neorpc . Error ) {
2020-10-26 17:22:20 +00:00
s . logRequestError ( req , jsonErr )
} )
2020-05-10 22:00:19 +00:00
select {
case <- s . shutdown :
break requestloop
case resChan <- res :
}
}
2022-11-17 13:00:22 +00:00
2020-05-10 22:00:19 +00:00
s . subsLock . Lock ( )
delete ( s . subscribers , subscr )
2022-11-17 13:00:22 +00:00
s . subsLock . Unlock ( )
s . subsCounterLock . Lock ( )
2020-05-10 22:00:19 +00:00
for _ , e := range subscr . feeds {
2022-07-22 16:09:29 +00:00
if e . event != neorpc . InvalidEventID {
2020-05-13 14:13:33 +00:00
s . unsubscribeFromChannel ( e . event )
2020-05-10 22:00:19 +00:00
}
2020-04-29 12:25:58 +00:00
}
2022-11-17 13:00:22 +00:00
s . subsCounterLock . Unlock ( )
2020-04-29 12:25:58 +00:00
close ( resChan )
ws . Close ( )
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBestBlockHash ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-03-13 07:14:48 +00:00
return "0x" + s . chain . CurrentBlockHash ( ) . StringLE ( ) , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBlockCount ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-03-13 07:14:48 +00:00
return s . chain . BlockHeight ( ) + 1 , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBlockHeaderCount ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2021-02-07 19:01:22 +00:00
return s . chain . HeaderHeight ( ) + 1 , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getConnectionCount ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-03-13 07:14:48 +00:00
return s . coreServer . PeerCount ( ) , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) blockHashFromParam ( param * params . Param ) ( util . Uint256 , * neorpc . Error ) {
2021-10-29 14:06:45 +00:00
var (
hash util . Uint256
err error
)
2020-06-04 11:58:47 +00:00
if param == nil {
2022-07-22 16:09:29 +00:00
return hash , neorpc . ErrInvalidParams
2020-06-04 11:58:47 +00:00
}
2021-10-29 14:06:45 +00:00
if hash , err = param . GetUint256 ( ) ; err != nil {
num , respErr := s . blockHeightFromParam ( param )
if respErr != nil {
return hash , respErr
2020-03-13 07:08:30 +00:00
}
hash = s . chain . GetHeaderHash ( num )
2020-06-05 13:02:55 +00:00
}
return hash , nil
}
2022-07-08 11:50:00 +00:00
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 ,
}
2022-11-18 20:19:50 +00:00
hash := s . chain . GetHeaderHash ( h . Index + 1 )
2022-07-08 11:50:00 +00:00
if ! hash . Equals ( util . Uint256 { } ) {
res . NextBlockHash = & hash
}
return res
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBlock ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2020-06-04 11:58:47 +00:00
param := reqParams . Value ( 0 )
2020-06-05 13:02:55 +00:00
hash , respErr := s . blockHashFromParam ( param )
if respErr != nil {
return nil , respErr
}
2020-03-13 07:08:30 +00:00
block , err := s . chain . GetBlock ( hash )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get block" , err . Error ( ) )
2020-03-13 07:08:30 +00:00
}
2021-10-28 11:10:18 +00:00
if v , _ := reqParams . Value ( 1 ) . GetBoolean ( ) ; v {
2022-07-08 11:50:00 +00:00
res := result . Block {
Block : * block ,
BlockMetadata : s . fillBlockMetadata ( block , & block . Header ) ,
}
return res , nil
2020-03-13 07:08:30 +00:00
}
writer := io . NewBufBinWriter ( )
block . EncodeBinary ( writer . BinWriter )
2020-11-06 14:37:58 +00:00
return writer . Bytes ( ) , nil
2020-03-13 07:08:30 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBlockHash ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-28 11:10:18 +00:00
num , err := s . blockHeightFromParam ( reqParams . Value ( 0 ) )
2020-03-13 07:06:52 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-03-13 07:06:52 +00:00
}
return s . chain . GetHeaderHash ( num ) , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getVersion ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2022-11-29 14:43:08 +00:00
port , err := s . coreServer . Port ( nil ) // any port will suite
2020-06-10 07:01:21 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "cannot fetch tcp port: %s" , err ) )
2020-06-10 07:01:21 +00:00
}
2021-09-07 12:42:04 +00:00
cfg := s . chain . GetConfig ( )
2022-04-26 09:32:06 +00:00
return & result . Version {
2022-11-10 13:32:49 +00:00
TCPPort : port ,
Nonce : s . coreServer . ID ( ) ,
UserAgent : s . coreServer . UserAgent ,
2021-09-07 12:42:04 +00:00
Protocol : result . Protocol {
AddressVersion : address . NEO3Prefix ,
Network : cfg . Magic ,
2022-12-02 16:10:45 +00:00
MillisecondsPerBlock : int ( cfg . TimePerBlock / time . Millisecond ) ,
2021-09-07 12:42:04 +00:00
MaxTraceableBlocks : cfg . MaxTraceableBlocks ,
MaxValidUntilBlockIncrement : cfg . MaxValidUntilBlockIncrement ,
MaxTransactionsPerBlock : cfg . MaxTransactionsPerBlock ,
MemoryPoolMaxTransactions : cfg . MemPoolSize ,
2022-01-21 02:33:06 +00:00
ValidatorsCount : byte ( cfg . GetNumOfCNs ( s . chain . BlockHeight ( ) ) ) ,
2022-04-25 13:01:30 +00:00
InitialGasDistribution : cfg . InitialGASSupply ,
2022-08-08 13:10:02 +00:00
CommitteeHistory : cfg . CommitteeHistory ,
P2PSigExtensions : cfg . P2PSigExtensions ,
StateRootInHeader : cfg . StateRootInHeader ,
ValidatorsHistory : cfg . ValidatorsHistory ,
2021-09-07 12:42:04 +00:00
} ,
2020-03-13 07:04:57 +00:00
} , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getPeers ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-03-13 07:03:58 +00:00
peers := result . NewGetPeers ( )
peers . AddUnconnected ( s . coreServer . UnconnectedPeers ( ) )
peers . AddConnected ( s . coreServer . ConnectedPeers ( ) )
peers . AddBad ( s . coreServer . BadPeers ( ) )
return peers , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getRawMempool ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-28 11:10:18 +00:00
verbose , _ := reqParams . Value ( 0 ) . GetBoolean ( )
2020-03-13 07:03:01 +00:00
mp := s . chain . GetMemPool ( )
hashList := make ( [ ] util . Uint256 , 0 )
for _ , item := range mp . GetVerifiedTransactions ( ) {
2020-06-05 16:01:10 +00:00
hashList = append ( hashList , item . Hash ( ) )
2020-03-13 07:03:01 +00:00
}
2020-07-27 14:27:21 +00:00
if ! verbose {
return hashList , nil
}
return result . RawMempool {
2021-09-09 13:24:22 +00:00
Height : s . chain . BlockHeight ( ) ,
Verified : hashList ,
Unverified : [ ] util . Uint256 { } , // avoid `null` result
2020-07-27 14:27:21 +00:00
} , nil
2020-03-13 07:03:01 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) validateAddress ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-28 11:10:18 +00:00
param , err := reqParams . Value ( 0 ) . GetString ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-03-13 07:01:49 +00:00
}
2021-10-28 11:10:18 +00:00
return result . ValidateAddress {
Address : reqParams . Value ( 0 ) ,
IsValid : validateAddress ( param ) ,
} , nil
2018-03-25 10:13:47 +00:00
}
2021-03-24 17:32:48 +00:00
// calculateNetworkFee calculates network fee for the transaction.
2022-07-22 16:09:29 +00:00
func ( s * Server ) calculateNetworkFee ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2021-03-24 17:32:48 +00:00
if len ( reqParams ) < 1 {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . ErrInvalidParams
2021-03-24 17:32:48 +00:00
}
byteTx , err := reqParams [ 0 ] . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-03-24 17:32:48 +00:00
}
tx , err := transaction . NewTransactionFromBytes ( byteTx )
if err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-03-24 17:32:48 +00:00
}
hashablePart , err := tx . EncodeHashableFields ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "failed to compute tx size: %s" , err ) )
2021-03-24 17:32:48 +00:00
}
size := len ( hashablePart ) + io . GetVarSize ( len ( tx . Signers ) )
2022-08-22 15:50:35 +00:00
var netFee int64
2021-03-24 17:32:48 +00:00
for i , signer := range tx . Signers {
2022-08-22 11:47:30 +00:00
w := tx . Scripts [ i ]
2022-08-22 15:50:35 +00:00
if len ( w . InvocationScript ) == 0 { // No invocation provided, try to infer one.
var paramz [ ] manifest . Parameter
if len ( w . VerificationScript ) == 0 { // Contract-based verification
cs := s . chain . GetContractState ( signer . Account )
if cs == nil {
return 0 , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "signer %d has no verification script and no deployed contract" , i ) )
}
md := cs . Manifest . ABI . GetMethod ( manifest . MethodVerify , - 1 )
if md == nil || md . ReturnType != smartcontract . BoolType {
return 0 , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "signer %d has no verify method in deployed contract" , i ) )
}
paramz = md . Parameters // Might as well have none params and it's OK.
} else { // Regular signature verification.
if vm . IsSignatureContract ( w . VerificationScript ) {
paramz = [ ] manifest . Parameter { { Type : smartcontract . SignatureType } }
} else if nSigs , _ , ok := vm . ParseMultiSigContract ( w . VerificationScript ) ; ok {
paramz = make ( [ ] manifest . Parameter , nSigs )
for j := 0 ; j < nSigs ; j ++ {
paramz [ j ] = manifest . Parameter { Type : smartcontract . SignatureType }
}
}
}
inv := io . NewBufBinWriter ( )
for _ , p := range paramz {
p . Type . EncodeDefaultValue ( inv . BinWriter )
}
if inv . Err != nil {
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to create dummy invocation script (signer %d): %s" , i , inv . Err . Error ( ) ) )
}
w . InvocationScript = inv . Bytes ( )
2021-03-24 17:32:48 +00:00
}
2022-08-22 15:50:35 +00:00
gasConsumed , _ := s . chain . VerifyWitness ( signer . Account , tx , & w , int64 ( s . config . MaxGasInvoke ) )
netFee += gasConsumed
size += io . GetVarSize ( w . VerificationScript ) + io . GetVarSize ( w . InvocationScript )
2021-03-24 17:32:48 +00:00
}
2022-08-22 13:51:58 +00:00
if s . chain . P2PSigExtensionsEnabled ( ) {
attrs := tx . GetAttributes ( transaction . NotaryAssistedT )
if len ( attrs ) != 0 {
na := attrs [ 0 ] . Value . ( * transaction . NotaryAssisted )
netFee += ( int64 ( na . NKeys ) + 1 ) * s . chain . GetNotaryServiceFeePerKey ( )
}
}
2022-01-11 21:58:03 +00:00
fee := s . chain . FeePerByte ( )
2021-03-24 17:32:48 +00:00
netFee += int64 ( size ) * fee
2021-06-04 17:44:58 +00:00
return result . NetworkFee { Value : netFee } , nil
2021-03-24 17:32:48 +00:00
}
2020-09-21 11:08:15 +00:00
// getApplicationLog returns the contract log based on the specified txid or blockid.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getApplicationLog ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2020-09-21 11:08:15 +00:00
hash , err := reqParams . Value ( 0 ) . GetUint256 ( )
2020-02-21 14:56:28 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-02-21 14:56:28 +00:00
}
2020-11-10 11:30:00 +00:00
trig := trigger . All
if len ( reqParams ) > 1 {
2021-10-28 11:10:18 +00:00
trigString , err := reqParams . Value ( 1 ) . GetString ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
rpc: fix `getapplicationlog` RPC handler
Fixes the following panic:
```
2020/12/22 18:16:09 http: panic serving 127.0.0.1:50228: runtime error: invalid memory address or nil pointer dereference
goroutine 4043 [running]:
net/http.(*conn).serve.func1(0xc00094c960)
net/http/server.go:1772 +0x139
panic(0xcd9b40, 0x16a94e0)
runtime/panic.go:973 +0x396
github.com/nspcc-dev/neo-go/pkg/rpc/server.(*Server).getApplicationLog(0xc000094ea0, 0xc000472d20, 0x2, 0x4, 0xc0000be228, 0xc0007ad601, 0x28)
github.com/nspcc-dev/neo-go/pkg/rpc/server/server.go:542 +0xac
github.com/nspcc-dev/neo-go/pkg/rpc/server.(*Server).handleIn(0xc000094ea0, 0xc000089770, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
github.com/nspcc-dev/neo-go/pkg/rpc/server/server.go:326 +0x981
github.com/nspcc-dev/neo-go/pkg/rpc/server.(*Server).handleRequest(0xc000094ea0, 0xc001bccba0, 0x0, 0x0, 0x0)
github.com/nspcc-dev/neo-go/pkg/rpc/server/server.go:296 +0x26a
github.com/nspcc-dev/neo-go/pkg/rpc/server.(*Server).handleHTTPRequest(0xc000094ea0, 0x1071f40, 0xc000b089a0, 0xc00053c200)
github.com/nspcc-dev/neo-go/pkg/rpc/server/server.go:290 +0x91b
net/http.HandlerFunc.ServeHTTP(0xc0004e61b0, 0x1071f40, 0xc000b089a0, 0xc00053c200)
net/http/server.go:2012 +0x44
net/http.serverHandler.ServeHTTP(0xc0000d2ee0, 0x1071f40, 0xc000b089a0, 0xc00053c200)
net/http/server.go:2807 +0xa3
net/http.(*conn).serve(0xc00094c960, 0x10749c0, 0xc0006ae980)
net/http/server.go:1895 +0x86c
created by net/http.(*Server).Serve
net/http/server.go:2933 +0x35c
```
2020-12-22 15:21:04 +00:00
}
2021-10-28 11:10:18 +00:00
trig , err = trigger . FromString ( trigString )
2020-11-10 11:30:00 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-11-10 11:30:00 +00:00
}
}
2020-11-11 15:43:28 +00:00
appExecResults , err := s . chain . GetAppExecResults ( hash , trigger . All )
2020-02-21 14:56:28 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrUnknownScriptContainer , fmt . Sprintf ( "failed to locate application log: %s" , err ) )
2020-02-21 14:56:28 +00:00
}
2020-11-10 11:30:00 +00:00
return result . NewApplicationLog ( hash , appExecResults , trig ) , nil
2020-02-21 14:56:28 +00:00
}
2022-07-04 14:12:42 +00:00
func ( s * Server ) getNEP11Tokens ( h util . Uint160 , acc util . Uint160 , bw * io . BufBinWriter ) ( [ ] stackitem . Item , string , int , error ) {
items , finalize , err := s . invokeReadOnlyMulti ( bw , h , [ ] string { "tokensOf" , "symbol" , "decimals" } , [ ] [ ] interface { } { { acc } , nil , nil } )
2021-11-17 20:04:50 +00:00
if err != nil {
2022-07-04 14:12:42 +00:00
return nil , "" , 0 , err
2021-11-17 20:04:50 +00:00
}
defer finalize ( )
2022-07-04 14:12:42 +00:00
if ( items [ 0 ] . Type ( ) != stackitem . InteropT ) || ! iterator . IsIterator ( items [ 0 ] ) {
return nil , "" , 0 , fmt . Errorf ( "invalid `tokensOf` result type %s" , items [ 0 ] . String ( ) )
}
2022-06-15 18:23:29 +00:00
vals := iterator . Values ( items [ 0 ] , s . config . MaxNEP11Tokens )
2022-07-04 14:12:42 +00:00
sym , err := stackitem . ToString ( items [ 1 ] )
if err != nil {
return nil , "" , 0 , fmt . Errorf ( "`symbol` return value error: %w" , err )
2021-11-17 20:04:50 +00:00
}
2022-07-04 14:12:42 +00:00
dec , err := items [ 2 ] . TryInteger ( )
if err != nil {
return nil , "" , 0 , fmt . Errorf ( "`decimals` return value error: %w" , err )
}
if ! dec . IsInt64 ( ) || dec . Sign ( ) == - 1 || dec . Int64 ( ) > math . MaxInt32 {
return nil , "" , 0 , errors . New ( "`decimals` returned a bad integer" )
}
return vals , sym , int ( dec . Int64 ( ) ) , nil
2021-11-17 20:04:50 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNEP11Balances ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-11-17 20:04:50 +00:00
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2021-11-17 20:04:50 +00:00
}
bs := & result . NEP11Balances {
Address : address . Uint160ToString ( u ) ,
Balances : [ ] result . NEP11AssetBalance { } ,
}
lastUpdated , err := s . chain . GetTokenLastUpdated ( u )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get NEP-11 last updated block" , err . Error ( ) )
2021-11-17 20:04:50 +00:00
}
var count int
stateSyncPoint := lastUpdated [ math . MinInt32 ]
bw := io . NewBufBinWriter ( )
contract_loop :
for _ , h := range s . chain . GetNEP11Contracts ( ) {
2022-07-04 14:12:42 +00:00
toks , sym , dec , err := s . getNEP11Tokens ( h , u , bw )
2021-11-17 20:04:50 +00:00
if err != nil {
continue
}
if len ( toks ) == 0 {
continue
}
cs := s . chain . GetContractState ( h )
if cs == nil {
continue
}
2022-12-10 08:35:03 +00:00
isDivisible := ( standard . ComplyABI ( & cs . Manifest , standard . Nep11Divisible ) == nil )
2021-11-17 20:04:50 +00:00
lub , ok := lastUpdated [ cs . ID ]
if ! ok {
cfg := s . chain . GetConfig ( )
2022-12-06 13:34:38 +00:00
if ! cfg . P2PStateExchangeExtensions && cfg . Ledger . RemoveUntraceableBlocks {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get LastUpdatedBlock for balance of %s token: internal database inconsistency" , cs . Hash . StringLE ( ) ) )
2021-11-17 20:04:50 +00:00
}
lub = stateSyncPoint
}
bs . Balances = append ( bs . Balances , result . NEP11AssetBalance {
2022-07-04 14:12:42 +00:00
Asset : h ,
Decimals : dec ,
Name : cs . Manifest . Name ,
Symbol : sym ,
Tokens : make ( [ ] result . NEP11TokenBalance , 0 , len ( toks ) ) ,
2021-11-17 20:04:50 +00:00
} )
curAsset := & bs . Balances [ len ( bs . Balances ) - 1 ]
for i := range toks {
id , err := toks [ i ] . TryBytes ( )
2022-07-08 16:51:59 +00:00
if err != nil || len ( id ) > limits . MaxStorageKeyLen {
2021-11-17 20:04:50 +00:00
continue
}
var amount = "1"
if isDivisible {
2022-07-04 14:12:42 +00:00
balance , err := s . getNEP11DTokenBalance ( h , u , id , bw )
2021-11-17 20:04:50 +00:00
if err != nil {
continue
}
if balance . Sign ( ) == 0 {
continue
}
amount = balance . String ( )
}
count ++
curAsset . Tokens = append ( curAsset . Tokens , result . NEP11TokenBalance {
ID : hex . EncodeToString ( id ) ,
Amount : amount ,
LastUpdated : lub ,
} )
if count >= s . config . MaxNEP11Tokens {
break contract_loop
}
}
}
return bs , nil
}
func ( s * Server ) invokeNEP11Properties ( h util . Uint160 , id [ ] byte , bw * io . BufBinWriter ) ( [ ] stackitem . MapElement , error ) {
item , finalize , err := s . invokeReadOnly ( bw , h , "properties" , id )
if err != nil {
return nil , err
}
defer finalize ( )
if item . Type ( ) != stackitem . MapT {
return nil , fmt . Errorf ( "invalid `properties` result type %s" , item . String ( ) )
}
return item . Value ( ) . ( [ ] stackitem . MapElement ) , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNEP11Properties ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-11-17 20:04:50 +00:00
asset , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2021-11-17 20:04:50 +00:00
}
token , err := ps . Value ( 1 ) . GetBytesHex ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2021-11-17 20:04:50 +00:00
}
props , err := s . invokeNEP11Properties ( asset , token , nil )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get NEP-11 properties" , err . Error ( ) )
2021-11-17 20:04:50 +00:00
}
res := make ( map [ string ] interface { } )
for _ , kv := range props {
key , err := kv . Key . TryBytes ( )
if err != nil {
continue
}
var val interface { }
2021-11-17 21:08:10 +00:00
if result . KnownNEP11Properties [ string ( key ) ] || kv . Value . Type ( ) != stackitem . AnyT {
2021-11-17 20:04:50 +00:00
v , err := kv . Value . TryBytes ( )
if err != nil {
continue
}
2021-11-17 21:08:10 +00:00
if result . KnownNEP11Properties [ string ( key ) ] {
2021-11-17 20:04:50 +00:00
val = string ( v )
} else {
val = v
}
}
res [ string ( key ) ] = val
}
return res , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNEP17Balances ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2020-07-03 15:10:07 +00:00
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
2020-03-05 11:50:06 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-03-05 11:50:06 +00:00
}
2020-11-24 08:14:25 +00:00
bs := & result . NEP17Balances {
2020-03-11 12:03:20 +00:00
Address : address . Uint160ToString ( u ) ,
2020-11-24 08:14:25 +00:00
Balances : [ ] result . NEP17Balance { } ,
2020-03-11 12:03:20 +00:00
}
2021-11-16 20:09:04 +00:00
lastUpdated , err := s . chain . GetTokenLastUpdated ( u )
2021-07-25 12:00:44 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get NEP-17 last updated block" , err . Error ( ) )
2021-07-25 12:00:44 +00:00
}
2021-08-11 11:29:03 +00:00
stateSyncPoint := lastUpdated [ math . MinInt32 ]
2021-07-25 12:00:44 +00:00
bw := io . NewBufBinWriter ( )
for _ , h := range s . chain . GetNEP17Contracts ( ) {
2022-07-04 14:12:42 +00:00
balance , sym , dec , err := s . getNEP17TokenBalance ( h , u , bw )
2021-07-25 12:00:44 +00:00
if err != nil {
continue
}
if balance . Sign ( ) == 0 {
continue
}
cs := s . chain . GetContractState ( h )
if cs == nil {
continue
2020-03-05 11:50:06 +00:00
}
2021-08-11 11:29:03 +00:00
lub , ok := lastUpdated [ cs . ID ]
if ! ok {
cfg := s . chain . GetConfig ( )
2022-12-06 13:34:38 +00:00
if ! cfg . P2PStateExchangeExtensions && cfg . Ledger . RemoveUntraceableBlocks {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get LastUpdatedBlock for balance of %s token: internal database inconsistency" , cs . Hash . StringLE ( ) ) )
2021-08-11 11:29:03 +00:00
}
lub = stateSyncPoint
}
2021-07-25 12:00:44 +00:00
bs . Balances = append ( bs . Balances , result . NEP17Balance {
Asset : h ,
Amount : balance . String ( ) ,
2022-07-04 14:12:42 +00:00
Decimals : dec ,
2021-08-11 11:29:03 +00:00
LastUpdated : lub ,
2022-07-04 14:12:42 +00:00
Name : cs . Manifest . Name ,
Symbol : sym ,
2021-07-25 12:00:44 +00:00
} )
2020-03-05 11:50:06 +00:00
}
return bs , nil
}
2021-11-17 20:04:50 +00:00
func ( s * Server ) invokeReadOnly ( bw * io . BufBinWriter , h util . Uint160 , method string , params ... interface { } ) ( stackitem . Item , func ( ) , error ) {
2022-07-04 14:12:42 +00:00
r , f , err := s . invokeReadOnlyMulti ( bw , h , [ ] string { method } , [ ] [ ] interface { } { params } )
if err != nil {
return nil , nil , err
}
return r [ 0 ] , f , nil
}
func ( s * Server ) invokeReadOnlyMulti ( bw * io . BufBinWriter , h util . Uint160 , methods [ ] string , params [ ] [ ] interface { } ) ( [ ] stackitem . Item , func ( ) , error ) {
2021-07-25 12:00:44 +00:00
if bw == nil {
bw = io . NewBufBinWriter ( )
} else {
bw . Reset ( )
}
2022-07-04 14:12:42 +00:00
if len ( methods ) != len ( params ) {
return nil , nil , fmt . Errorf ( "asymmetric parameters" )
}
for i := range methods {
emit . AppCall ( bw . BinWriter , h , methods [ i ] , callflag . ReadStates | callflag . AllowCall , params [ i ] ... )
if bw . Err != nil {
return nil , nil , fmt . Errorf ( "failed to create `%s` invocation script: %w" , methods [ i ] , bw . Err )
}
2021-07-25 12:00:44 +00:00
}
script := bw . Bytes ( )
tx := & transaction . Transaction { Script : script }
2022-10-06 10:24:57 +00:00
ic , err := s . chain . GetTestVM ( trigger . Application , tx , nil )
2021-11-17 20:04:50 +00:00
if err != nil {
2022-10-06 10:24:57 +00:00
return nil , nil , fmt . Errorf ( "faile to prepare test VM: %w" , err )
2021-11-17 20:04:50 +00:00
}
2022-01-12 22:20:08 +00:00
ic . VM . GasLimit = core . HeaderVerificationGasLimit
ic . VM . LoadScriptWithFlags ( script , callflag . All )
err = ic . VM . Run ( )
2021-07-25 12:00:44 +00:00
if err != nil {
2022-01-12 22:20:08 +00:00
ic . Finalize ( )
2022-07-04 14:12:42 +00:00
return nil , nil , fmt . Errorf ( "failed to run %d methods of %s: %w" , len ( methods ) , h . StringLE ( ) , err )
2021-07-25 12:00:44 +00:00
}
2022-07-04 14:12:42 +00:00
estack := ic . VM . Estack ( )
if estack . Len ( ) != len ( methods ) {
2022-01-12 22:20:08 +00:00
ic . Finalize ( )
2022-07-04 14:12:42 +00:00
return nil , nil , fmt . Errorf ( "invalid return values count: expected %d, got %d" , len ( methods ) , estack . Len ( ) )
2021-11-17 20:04:50 +00:00
}
2022-07-04 14:12:42 +00:00
return estack . ToArray ( ) , ic . Finalize , nil
2021-11-17 20:04:50 +00:00
}
2022-07-04 14:12:42 +00:00
func ( s * Server ) getNEP17TokenBalance ( h util . Uint160 , acc util . Uint160 , bw * io . BufBinWriter ) ( * big . Int , string , int , error ) {
items , finalize , err := s . invokeReadOnlyMulti ( bw , h , [ ] string { "balanceOf" , "symbol" , "decimals" } , [ ] [ ] interface { } { { acc } , nil , nil } )
if err != nil {
return nil , "" , 0 , err
2021-11-17 20:04:50 +00:00
}
2022-07-04 14:12:42 +00:00
finalize ( )
res , err := items [ 0 ] . TryInteger ( )
if err != nil {
return nil , "" , 0 , fmt . Errorf ( "unexpected `balanceOf` result type: %w" , err )
}
sym , err := stackitem . ToString ( items [ 1 ] )
if err != nil {
return nil , "" , 0 , fmt . Errorf ( "`symbol` return value error: %w" , err )
}
dec , err := items [ 2 ] . TryInteger ( )
if err != nil {
return nil , "" , 0 , fmt . Errorf ( "`decimals` return value error: %w" , err )
}
if ! dec . IsInt64 ( ) || dec . Sign ( ) == - 1 || dec . Int64 ( ) > math . MaxInt32 {
return nil , "" , 0 , errors . New ( "`decimals` returned a bad integer" )
}
return res , sym , int ( dec . Int64 ( ) ) , nil
}
func ( s * Server ) getNEP11DTokenBalance ( h util . Uint160 , acc util . Uint160 , id [ ] byte , bw * io . BufBinWriter ) ( * big . Int , error ) {
item , finalize , err := s . invokeReadOnly ( bw , h , "balanceOf" , acc , id )
2021-11-17 20:04:50 +00:00
if err != nil {
return nil , err
2021-07-25 12:00:44 +00:00
}
2021-11-17 20:04:50 +00:00
finalize ( )
res , err := item . TryInteger ( )
2021-07-25 12:00:44 +00:00
if err != nil {
return nil , fmt . Errorf ( "unexpected `balanceOf` result type: %w" , err )
}
return res , nil
}
2022-07-07 14:41:01 +00:00
func getTimestampsAndLimit ( ps params . Params , index int ) ( uint64 , uint64 , int , int , error ) {
2020-08-07 06:42:44 +00:00
var start , end uint64
2020-09-12 21:12:45 +00:00
var limit , page int
2020-09-14 14:48:17 +00:00
limit = maxTransfersLimit
2020-09-12 21:12:45 +00:00
pStart , pEnd , pLimit , pPage := ps . Value ( index ) , ps . Value ( index + 1 ) , ps . Value ( index + 2 ) , ps . Value ( index + 3 )
if pPage != nil {
p , err := pPage . GetInt ( )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
if p < 0 {
return 0 , 0 , 0 , 0 , errors . New ( "can't use negative page" )
}
page = p
}
2020-09-11 19:33:17 +00:00
if pLimit != nil {
l , err := pLimit . GetInt ( )
2020-08-07 06:42:44 +00:00
if err != nil {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , err
2020-08-07 06:42:44 +00:00
}
2020-09-11 19:33:17 +00:00
if l <= 0 {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , errors . New ( "can't use negative or zero limit" )
2020-09-11 19:33:17 +00:00
}
2020-09-14 14:48:17 +00:00
if l > maxTransfersLimit {
return 0 , 0 , 0 , 0 , errors . New ( "too big limit requested" )
}
2020-09-11 19:33:17 +00:00
limit = l
2020-08-07 06:42:44 +00:00
}
2020-09-11 19:33:17 +00:00
if pEnd != nil {
val , err := pEnd . GetInt ( )
2020-08-07 06:42:44 +00:00
if err != nil {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , err
2020-08-07 06:42:44 +00:00
}
end = uint64 ( val )
2020-09-11 19:33:17 +00:00
} else {
end = uint64 ( time . Now ( ) . Unix ( ) * 1000 )
2020-08-07 06:42:44 +00:00
}
2020-09-11 19:33:17 +00:00
if pStart != nil {
val , err := pStart . GetInt ( )
2020-09-08 09:56:52 +00:00
if err != nil {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , err
2020-09-08 09:56:52 +00:00
}
2020-09-11 19:33:17 +00:00
start = uint64 ( val )
} else {
start = uint64 ( time . Now ( ) . Add ( - time . Hour * 24 * 7 ) . Unix ( ) * 1000 )
2020-09-08 09:56:52 +00:00
}
2020-09-12 21:12:45 +00:00
return start , end , limit , page , nil
2020-08-07 06:42:44 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNEP11Transfers ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-11-17 20:04:50 +00:00
return s . getTokenTransfers ( ps , true )
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNEP17Transfers ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-11-17 20:04:50 +00:00
return s . getTokenTransfers ( ps , false )
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getTokenTransfers ( ps params . Params , isNEP11 bool ) ( interface { } , * neorpc . Error ) {
2020-07-03 15:25:18 +00:00
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
2020-03-05 12:16:03 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-03-05 12:16:03 +00:00
}
2020-09-12 21:12:45 +00:00
start , end , limit , page , err := getTimestampsAndLimit ( ps , 1 )
2020-08-07 06:42:44 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "malformed timestamps/limit: %s" , err ) )
2020-08-07 06:42:44 +00:00
}
2021-11-17 20:04:50 +00:00
bs := & tokenTransfers {
2020-03-11 12:03:20 +00:00
Address : address . Uint160ToString ( u ) ,
2021-11-17 20:04:50 +00:00
Received : [ ] interface { } { } ,
Sent : [ ] interface { } { } ,
2020-03-11 12:03:20 +00:00
}
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
cache := make ( map [ int32 ] util . Uint160 )
2020-09-12 21:12:45 +00:00
var resCount , frameCount int
2022-04-20 18:30:09 +00:00
// handleTransfer returns items to be added into the received and sent arrays
2021-11-17 20:04:50 +00:00
// along with a continue flag and error.
var handleTransfer = func ( tr * state . NEP17Transfer ) ( * result . NEP17Transfer , * result . NEP17Transfer , bool , error ) {
var received , sent * result . NEP17Transfer
2022-04-20 18:30:09 +00:00
// Iterating from the newest to the oldest, not yet reached required
2020-09-12 21:12:45 +00:00
// time frame, continue looping.
2020-09-08 12:29:07 +00:00
if tr . Timestamp > end {
2021-11-17 20:04:50 +00:00
return nil , nil , true , nil
2020-09-08 12:29:07 +00:00
}
2022-04-20 18:30:09 +00:00
// Iterating from the newest to the oldest, moved past required
2020-09-12 21:12:45 +00:00
// time frame, stop looping.
if tr . Timestamp < start {
2021-11-17 20:04:50 +00:00
return nil , nil , false , nil
2020-08-07 06:42:44 +00:00
}
2020-09-12 21:12:45 +00:00
frameCount ++
// Using limits, not yet reached required page.
if limit != 0 && page * limit >= frameCount {
2021-11-17 20:04:50 +00:00
return nil , nil , true , nil
2020-09-12 21:12:45 +00:00
}
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
h , err := s . getHash ( tr . Asset , cache )
2020-07-28 16:05:16 +00:00
if err != nil {
2021-11-17 20:04:50 +00:00
return nil , nil , false , err
2020-07-28 16:05:16 +00:00
}
2020-09-12 21:12:45 +00:00
2020-11-24 08:14:25 +00:00
transfer := result . NEP17Transfer {
2020-03-05 12:16:03 +00:00
Timestamp : tr . Timestamp ,
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
Asset : h ,
2020-03-05 12:16:03 +00:00
Index : tr . Block ,
TxHash : tr . Tx ,
}
2020-07-09 09:57:24 +00:00
if tr . Amount . Sign ( ) > 0 { // token was received
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
transfer . Amount = tr . Amount . String ( )
2020-03-05 12:16:03 +00:00
if ! tr . From . Equals ( util . Uint160 { } ) {
transfer . Address = address . Uint160ToString ( tr . From )
}
2021-11-17 20:04:50 +00:00
received = & result . NEP17Transfer { }
* received = transfer // Make a copy, transfer is to be modified below.
2020-09-12 21:12:45 +00:00
} else {
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
transfer . Amount = new ( big . Int ) . Neg ( & tr . Amount ) . String ( )
2020-09-12 21:12:45 +00:00
if ! tr . To . Equals ( util . Uint160 { } ) {
transfer . Address = address . Uint160ToString ( tr . To )
}
2021-11-17 20:04:50 +00:00
sent = & result . NEP17Transfer { }
* sent = transfer
2020-03-05 12:16:03 +00:00
}
2020-09-12 21:12:45 +00:00
resCount ++
2021-11-17 20:04:50 +00:00
// Check limits for continue flag.
return received , sent , ! ( limit != 0 && resCount >= limit ) , nil
}
if ! isNEP11 {
2022-01-18 15:28:24 +00:00
err = s . chain . ForEachNEP17Transfer ( u , end , func ( tr * state . NEP17Transfer ) ( bool , error ) {
2021-11-17 20:04:50 +00:00
r , s , res , err := handleTransfer ( tr )
if err == nil {
if r != nil {
bs . Received = append ( bs . Received , r )
}
if s != nil {
bs . Sent = append ( bs . Sent , s )
}
}
return res , err
} )
} else {
2022-01-18 15:28:24 +00:00
err = s . chain . ForEachNEP11Transfer ( u , end , func ( tr * state . NEP11Transfer ) ( bool , error ) {
2021-11-17 20:04:50 +00:00
r , s , res , err := handleTransfer ( & tr . NEP17Transfer )
if err == nil {
id := hex . EncodeToString ( tr . ID )
if r != nil {
bs . Received = append ( bs . Received , nep17TransferToNEP11 ( r , id ) )
}
if s != nil {
bs . Sent = append ( bs . Sent , nep17TransferToNEP11 ( s , id ) )
}
}
return res , err
} )
}
2020-03-05 12:16:03 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "invalid transfer log: %s" , err ) )
2020-03-05 12:16:03 +00:00
}
return bs , nil
}
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
// getHash returns the hash of the contract by its ID using cache.
func ( s * Server ) getHash ( contractID int32 , cache map [ int32 ] util . Uint160 ) ( util . Uint160 , error ) {
2020-07-29 12:09:59 +00:00
if d , ok := cache [ contractID ] ; ok {
2020-03-05 12:39:53 +00:00
return d , nil
}
2020-07-29 12:09:59 +00:00
h , err := s . chain . GetContractScriptHash ( contractID )
if err != nil {
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
return util . Uint160 { } , err
2020-03-05 12:39:53 +00:00
}
rpc: adjust NEP5 transfers amount JSON marshalling
This committ fixes the difference between Go and C# nodes:
Go:
```
{
"jsonrpc" : "2.0",
"result" : {
"received" : [
{
"blockindex" : 65,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"transfernotifyindex" : 0,
"timestamp" : 1605535020126,
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"amount" : "29999999",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
],
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : []
},
"id" : 1
}
```
C#:
```
{
"id" : 1,
"result" : {
"address" : "NULwe3UAHckN2fzNdcVg31tDiaYtMDwANt",
"sent" : [],
"received" : [
{
"transferaddress" : "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"timestamp" : 1605535020126,
"txhash" : "0x394f851cf167d664c0dbcf98e2e64f2da23022fd7943dcb914492529de20a945",
"blockindex" : 65,
"transfernotifyindex" : 0,
"amount" : "2999999900000000",
"assethash" : "0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"
}
]
},
"jsonrpc" : "2.0"
}
```
2020-11-17 09:28:28 +00:00
cache [ contractID ] = h
return h , nil
2020-03-05 12:39:53 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) contractIDFromParam ( param * params . Param ) ( int32 , * neorpc . Error ) {
2020-06-18 10:50:30 +00:00
var result int32
2020-06-04 11:58:47 +00:00
if param == nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . ErrInvalidParams
2020-06-04 11:58:47 +00:00
}
2021-10-28 11:10:18 +00:00
if scriptHash , err := param . GetUint160FromHex ( ) ; err == nil {
2020-06-18 10:50:30 +00:00
cs := s . chain . GetContractState ( scriptHash )
if cs == nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . ErrUnknown
2020-06-18 10:50:30 +00:00
}
result = cs . ID
2021-10-28 11:10:18 +00:00
} else {
2020-06-18 10:50:30 +00:00
id , err := param . GetInt ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . ErrInvalidParams
2020-06-18 10:50:30 +00:00
}
2021-04-19 07:48:35 +00:00
if err := checkInt32 ( id ) ; err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-04-19 07:48:35 +00:00
}
2020-06-18 10:50:30 +00:00
result = int32 ( id )
}
return result , nil
}
2020-09-25 09:40:57 +00:00
// getContractScriptHashFromParam returns the contract script hash by hex contract hash, address, id or native contract name.
2022-07-22 16:09:29 +00:00
func ( s * Server ) contractScriptHashFromParam ( param * params . Param ) ( util . Uint160 , * neorpc . Error ) {
2020-09-25 09:40:57 +00:00
var result util . Uint160
if param == nil {
2022-07-22 16:09:29 +00:00
return result , neorpc . ErrInvalidParams
2020-09-25 09:40:57 +00:00
}
2021-10-29 14:06:45 +00:00
nameOrHashOrIndex , err := param . GetString ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return result , neorpc . ErrInvalidParams
2020-09-25 09:40:57 +00:00
}
2021-10-29 14:06:45 +00:00
result , err = param . GetUint160FromAddressOrHex ( )
if err == nil {
return result , nil
}
result , err = s . chain . GetNativeContractScriptHash ( nameOrHashOrIndex )
if err == nil {
return result , nil
}
id , err := strconv . Atoi ( nameOrHashOrIndex )
if err != nil {
2022-07-22 16:09:29 +00:00
return result , neorpc . NewRPCError ( "Invalid contract identifier (name/hash/index is expected)" , err . Error ( ) )
2021-10-29 14:06:45 +00:00
}
if err := checkInt32 ( id ) ; err != nil {
2022-07-22 16:09:29 +00:00
return result , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-10-29 14:06:45 +00:00
}
result , err = s . chain . GetContractScriptHash ( int32 ( id ) )
if err != nil {
2022-07-22 16:09:29 +00:00
return result , neorpc . NewRPCError ( "Unknown contract" , "" )
2020-09-25 09:40:57 +00:00
}
return result , nil
}
2020-06-04 08:59:22 +00:00
func makeStorageKey ( id int32 , key [ ] byte ) [ ] byte {
skey := make ( [ ] byte , 4 + len ( key ) )
binary . LittleEndian . PutUint32 ( skey , uint32 ( id ) )
copy ( skey [ 4 : ] , key )
return skey
}
var errKeepOnlyLatestState = errors . New ( "'KeepOnlyLatestState' setting is enabled" )
2022-07-22 16:09:29 +00:00
func ( s * Server ) getProof ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2022-12-06 13:34:38 +00:00
if s . chain . GetConfig ( ) . Ledger . KeepOnlyLatestState {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidRequestError ( fmt . Sprintf ( "'getproof' is not supported: %s" , errKeepOnlyLatestState ) )
2020-06-04 08:59:22 +00:00
}
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-06-04 08:59:22 +00:00
}
sc , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-06-04 08:59:22 +00:00
}
2021-03-30 10:08:13 +00:00
key , err := ps . Value ( 2 ) . GetBytesBase64 ( )
2020-06-04 08:59:22 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-06-04 08:59:22 +00:00
}
2021-10-15 04:01:47 +00:00
cs , respErr := s . getHistoricalContractState ( root , sc )
if respErr != nil {
return nil , respErr
2020-06-04 08:59:22 +00:00
}
skey := makeStorageKey ( cs . ID , key )
2021-01-29 14:33:24 +00:00
proof , err := s . chain . GetStateModule ( ) . GetStateProof ( root , skey )
2021-04-08 12:52:57 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get proof: %s" , err ) )
2021-04-08 12:52:57 +00:00
}
2021-03-30 10:08:13 +00:00
return & result . ProofWithKey {
Key : skey ,
Proof : proof ,
2020-06-04 08:59:22 +00:00
} , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) verifyProof ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2022-12-06 13:34:38 +00:00
if s . chain . GetConfig ( ) . Ledger . KeepOnlyLatestState {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidRequestError ( fmt . Sprintf ( "'verifyproof' is not supported: %s" , errKeepOnlyLatestState ) )
2020-06-05 08:51:39 +00:00
}
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-06-05 08:51:39 +00:00
}
proofStr , err := ps . Value ( 1 ) . GetString ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-06-05 08:51:39 +00:00
}
var p result . ProofWithKey
if err := p . FromString ( proofStr ) ; err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-06-05 08:51:39 +00:00
}
vp := new ( result . VerifyProof )
val , ok := mpt . VerifyProof ( root , p . Key , p . Proof )
if ok {
2021-03-09 09:09:44 +00:00
vp . Value = val
2020-06-05 08:51:39 +00:00
}
return vp , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getState ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-07 09:03:37 +00:00
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , "invalid stateroot" )
2021-10-07 09:03:37 +00:00
}
2022-12-06 13:34:38 +00:00
if s . chain . GetConfig ( ) . Ledger . KeepOnlyLatestState {
2021-10-07 09:03:37 +00:00
curr , err := s . chain . GetStateModule ( ) . GetStateRoot ( s . chain . BlockHeight ( ) )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get current stateroot: %s" , err ) )
2021-10-07 09:03:37 +00:00
}
if ! curr . Root . Equals ( root ) {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidRequestError ( fmt . Sprintf ( "'getstate' is not supported for old states: %s" , errKeepOnlyLatestState ) )
2021-10-07 09:03:37 +00:00
}
}
csHash , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , "invalid contract hash" )
2021-10-07 09:03:37 +00:00
}
key , err := ps . Value ( 2 ) . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , "invalid key" )
2021-10-07 09:03:37 +00:00
}
2021-10-07 13:56:27 +00:00
cs , respErr := s . getHistoricalContractState ( root , csHash )
if respErr != nil {
return nil , respErr
}
sKey := makeStorageKey ( cs . ID , key )
res , err := s . chain . GetStateModule ( ) . GetState ( root , sKey )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get historical item state" , err . Error ( ) )
2021-10-07 13:56:27 +00:00
}
return res , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) findStates ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-07 13:56:27 +00:00
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , "invalid stateroot" )
2021-10-07 13:56:27 +00:00
}
2022-12-06 13:34:38 +00:00
if s . chain . GetConfig ( ) . Ledger . KeepOnlyLatestState {
2021-10-07 13:56:27 +00:00
curr , err := s . chain . GetStateModule ( ) . GetStateRoot ( s . chain . BlockHeight ( ) )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get current stateroot: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
if ! curr . Root . Equals ( root ) {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidRequestError ( fmt . Sprintf ( "'findstates' is not supported for old states: %s" , errKeepOnlyLatestState ) )
2021-10-07 13:56:27 +00:00
}
}
csHash , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "invalid contract hash: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
prefix , err := ps . Value ( 2 ) . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "invalid prefix: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
var (
key [ ] byte
count = s . config . MaxFindResultItems
)
if len ( ps ) > 3 {
key , err = ps . Value ( 3 ) . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "invalid key: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
2021-10-13 08:38:53 +00:00
if len ( key ) > 0 {
if ! bytes . HasPrefix ( key , prefix ) {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , "key doesn't match prefix" )
2021-10-13 08:38:53 +00:00
}
key = key [ len ( prefix ) : ]
} else {
// empty ("") key shouldn't exclude item matching prefix from the result
key = nil
}
2021-10-07 13:56:27 +00:00
}
if len ( ps ) > 4 {
count , err = ps . Value ( 4 ) . GetInt ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , fmt . Sprintf ( "invalid count: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
if count > s . config . MaxFindResultItems {
count = s . config . MaxFindResultItems
}
}
cs , respErr := s . getHistoricalContractState ( root , csHash )
if respErr != nil {
return nil , respErr
}
pKey := makeStorageKey ( cs . ID , prefix )
2021-10-13 08:38:53 +00:00
kvs , err := s . chain . GetStateModule ( ) . FindStates ( root , pKey , key , count + 1 ) // +1 to define result truncation
2021-10-07 13:56:27 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to find historical items: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
res := result . FindStates { }
if len ( kvs ) == count + 1 {
res . Truncated = true
kvs = kvs [ : len ( kvs ) - 1 ]
}
if len ( kvs ) > 0 {
proof , err := s . chain . GetStateModule ( ) . GetStateProof ( root , kvs [ 0 ] . Key )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get first proof: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
res . FirstProof = & result . ProofWithKey {
Key : kvs [ 0 ] . Key ,
Proof : proof ,
}
}
if len ( kvs ) > 1 {
proof , err := s . chain . GetStateModule ( ) . GetStateProof ( root , kvs [ len ( kvs ) - 1 ] . Key )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to get last proof: %s" , err ) )
2021-10-07 13:56:27 +00:00
}
res . LastProof = & result . ProofWithKey {
Key : kvs [ len ( kvs ) - 1 ] . Key ,
Proof : proof ,
}
}
res . Results = make ( [ ] result . KeyValue , len ( kvs ) )
for i , kv := range kvs {
res . Results [ i ] = result . KeyValue {
Key : kv . Key [ 4 : ] , // cut contract ID as it is done in C#
Value : kv . Value ,
}
}
return res , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getHistoricalContractState ( root util . Uint256 , csHash util . Uint160 ) ( * state . Contract , * neorpc . Error ) {
2021-10-07 09:03:37 +00:00
csKey := makeStorageKey ( native . ManagementContractID , native . MakeContractKey ( csHash ) )
csBytes , err := s . chain . GetStateModule ( ) . GetState ( root , csKey )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get historical contract state" , err . Error ( ) )
2021-10-07 09:03:37 +00:00
}
contract := new ( state . Contract )
err = stackitem . DeserializeConvertible ( csBytes , contract )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to deserialize historical contract state: %s" , err ) )
2021-10-07 09:03:37 +00:00
}
2021-10-07 13:56:27 +00:00
return contract , nil
2021-10-07 09:03:37 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getStateHeight ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-06-04 08:09:07 +00:00
var height = s . chain . BlockHeight ( )
2021-02-01 16:00:07 +00:00
var stateHeight = s . chain . GetStateModule ( ) . CurrentValidatedHeight ( )
2020-06-04 08:09:07 +00:00
if s . chain . GetConfig ( ) . StateRootInHeader {
stateHeight = height - 1
}
return & result . StateHeight {
2021-07-22 16:55:41 +00:00
Local : height ,
Validated : stateHeight ,
2020-06-04 08:09:07 +00:00
} , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getStateRoot ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2020-06-03 15:09:36 +00:00
p := ps . Value ( 0 )
if p == nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( "missing stateroot identifier" )
2020-06-03 15:09:36 +00:00
}
2021-01-29 14:33:24 +00:00
var rt * state . MPTRoot
2020-06-03 15:09:36 +00:00
var h util . Uint256
2021-10-29 14:06:45 +00:00
height , err := p . GetIntStrict ( )
2020-06-03 15:09:36 +00:00
if err == nil {
2021-04-19 07:48:35 +00:00
if err := checkUint32 ( height ) ; err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-04-19 07:48:35 +00:00
}
2021-01-29 14:33:24 +00:00
rt , err = s . chain . GetStateModule ( ) . GetStateRoot ( uint32 ( height ) )
2020-06-03 15:09:36 +00:00
} else if h , err = p . GetUint256 ( ) ; err == nil {
2020-12-28 14:31:50 +00:00
var hdr * block . Header
hdr , err = s . chain . GetHeader ( h )
2020-06-03 15:09:36 +00:00
if err == nil {
2021-01-29 14:33:24 +00:00
rt , err = s . chain . GetStateModule ( ) . GetStateRoot ( hdr . Index )
2020-06-03 15:09:36 +00:00
}
}
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrUnknownStateRoot
2020-06-03 15:09:36 +00:00
}
return rt , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getStorage ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2020-06-04 11:58:47 +00:00
id , rErr := s . contractIDFromParam ( ps . Value ( 0 ) )
2022-07-22 16:09:29 +00:00
if rErr == neorpc . ErrUnknown {
2020-06-18 10:50:30 +00:00
return nil , nil
}
if rErr != nil {
return nil , rErr
2020-01-30 08:03:44 +00:00
}
2021-02-07 15:27:19 +00:00
key , err := ps . Value ( 1 ) . GetBytesBase64 ( )
2020-01-30 08:03:44 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-01-30 08:03:44 +00:00
}
2020-06-18 10:50:30 +00:00
item := s . chain . GetStorageItem ( id , key )
2020-01-30 08:03:44 +00:00
if item == nil {
2020-08-31 15:27:32 +00:00
return "" , nil
2020-01-30 08:03:44 +00:00
}
2021-03-05 14:06:54 +00:00
return [ ] byte ( item ) , nil
2020-01-30 08:03:44 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getrawtransaction ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2020-11-13 13:54:38 +00:00
txHash , err := reqParams . Value ( 0 ) . GetUint256 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-11-13 13:54:38 +00:00
}
tx , height , err := s . chain . GetTransaction ( txHash )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrUnknownTransaction
2020-11-13 13:54:38 +00:00
}
2021-10-28 11:10:18 +00:00
if v , _ := reqParams . Value ( 1 ) . GetBoolean ( ) ; v {
2022-07-08 11:32:29 +00:00
res := result . TransactionOutputRaw {
Transaction : * tx ,
}
if height == math . MaxUint32 { // Mempooled transaction.
return res , nil
2020-11-13 13:54:38 +00:00
}
2022-11-18 20:19:50 +00:00
_header := s . chain . GetHeaderHash ( height )
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
header , err := s . chain . GetHeader ( _header )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get header for the transaction" , err . Error ( ) )
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
2020-11-11 15:43:28 +00:00
aers , err := s . chain . GetAppExecResults ( txHash , trigger . Application )
2020-07-27 15:07:05 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Failed to get application log for the transaction" , err . Error ( ) )
2020-07-27 15:07:05 +00:00
}
2020-11-11 15:43:28 +00:00
if len ( aers ) == 0 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Inconsistent application log" , "application log for the transaction is empty" )
2020-11-11 15:43:28 +00:00
}
2022-07-08 11:32:29 +00:00
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
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
2020-11-13 13:54:38 +00:00
return tx . Bytes ( ) , nil
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getTransactionHeight ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2020-06-04 11:58:47 +00:00
h , err := ps . Value ( 0 ) . GetUint256 ( )
2020-03-05 14:20:50 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-03-05 14:20:50 +00:00
}
_ , height , err := s . chain . GetTransaction ( h )
2020-11-13 13:54:38 +00:00
if err != nil || height == math . MaxUint32 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrUnknownTransaction
2020-03-05 14:20:50 +00:00
}
return height , nil
}
2020-09-25 09:40:57 +00:00
// getContractState returns contract state (contract information, according to the contract script hash,
// contract id or native contract name).
2022-07-22 16:09:29 +00:00
func ( s * Server ) getContractState ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2020-09-25 09:40:57 +00:00
scriptHash , err := s . contractScriptHashFromParam ( reqParams . Value ( 0 ) )
2020-06-04 11:58:47 +00:00
if err != nil {
2020-09-25 09:40:57 +00:00
return nil , err
2020-02-15 16:53:08 +00:00
}
2020-06-04 11:58:47 +00:00
cs := s . chain . GetContractState ( scriptHash )
if cs == nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Unknown contract" , "" )
2020-06-04 11:58:47 +00:00
}
return cs , nil
2020-02-15 16:53:08 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNativeContracts ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2021-02-09 09:25:38 +00:00
return s . chain . GetNatives ( ) , nil
}
2020-02-19 09:44:31 +00:00
// getBlockSysFee returns the system fees of the block, based on the specified index.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBlockSysFee ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-28 11:10:18 +00:00
num , err := s . blockHeightFromParam ( reqParams . Value ( 0 ) )
2020-02-19 09:44:31 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . NewRPCError ( "Invalid height" , "invalid block identifier" )
2020-02-19 09:44:31 +00:00
}
headerHash := s . chain . GetHeaderHash ( num )
2020-04-28 19:35:19 +00:00
block , errBlock := s . chain . GetBlock ( headerHash )
if errBlock != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . ErrUnknownBlock
2020-02-19 09:44:31 +00:00
}
2020-06-23 14:15:35 +00:00
var blockSysFee int64
2020-02-19 09:44:31 +00:00
for _ , tx := range block . Transactions {
2020-05-08 17:54:24 +00:00
blockSysFee += tx . SystemFee
2020-02-19 09:44:31 +00:00
}
return blockSysFee , nil
}
2020-03-04 17:35:37 +00:00
// getBlockHeader returns the corresponding block header information according to the specified script hash.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getBlockHeader ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2020-06-04 11:58:47 +00:00
param := reqParams . Value ( 0 )
2020-06-05 13:02:55 +00:00
hash , respErr := s . blockHashFromParam ( param )
if respErr != nil {
return nil , respErr
2020-03-04 17:35:37 +00:00
}
2021-10-28 11:10:18 +00:00
verbose , _ := reqParams . Value ( 1 ) . GetBoolean ( )
2020-03-04 17:35:37 +00:00
h , err := s . chain . GetHeader ( hash )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrUnknownHeader
2020-03-04 17:35:37 +00:00
}
if verbose {
2022-07-08 11:50:00 +00:00
res := result . Header {
Header : * h ,
BlockMetadata : s . fillBlockMetadata ( h , h ) ,
}
return res , nil
2020-03-04 17:35:37 +00:00
}
buf := io . NewBufBinWriter ( )
h . EncodeBinary ( buf . BinWriter )
if buf . Err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "encoding error: %s" , buf . Err ) )
2020-03-04 17:35:37 +00:00
}
2020-11-06 14:37:58 +00:00
return buf . Bytes ( ) , nil
2020-03-04 17:35:37 +00:00
}
2020-06-01 20:27:03 +00:00
// getUnclaimedGas returns unclaimed GAS amount of the specified address.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getUnclaimedGas ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-28 11:10:18 +00:00
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
2020-03-06 17:38:17 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-03-06 17:38:17 +00:00
}
2020-11-06 09:27:05 +00:00
neo , _ := s . chain . GetGoverningTokenBalance ( u )
2020-07-09 09:57:24 +00:00
if neo . Sign ( ) == 0 {
2020-07-09 14:25:26 +00:00
return result . UnclaimedGas {
Address : u ,
} , nil
2020-03-06 17:38:17 +00:00
}
2020-11-06 09:27:05 +00:00
gas , err := s . chain . CalculateClaimable ( u , s . chain . BlockHeight ( ) + 1 ) // +1 as in C#, for the next block.
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Can't calculate claimable" , err . Error ( ) )
2020-11-06 09:27:05 +00:00
}
2020-07-09 14:25:26 +00:00
return result . UnclaimedGas {
Address : u ,
Unclaimed : * gas ,
} , nil
2020-03-06 17:38:17 +00:00
}
2022-07-01 13:02:03 +00:00
// getCandidates returns the current list of candidates with their active/inactive voting status.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getCandidates ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2022-07-01 13:02:03 +00:00
var validators keys . PublicKeys
validators , err := s . chain . GetNextBlockValidators ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Can't get next block validators" , err . Error ( ) )
2022-07-01 13:02:03 +00:00
}
enrollments , err := s . chain . GetEnrollments ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Can't get enrollments" , err . Error ( ) )
2022-07-01 13:02:03 +00:00
}
var res = make ( [ ] result . Candidate , 0 )
for _ , v := range enrollments {
res = append ( res , result . Candidate {
PublicKey : * v . Key ,
Votes : v . Votes . Int64 ( ) ,
Active : validators . Contains ( v . Key ) ,
} )
}
return res , nil
}
2020-10-01 12:26:54 +00:00
// getNextBlockValidators returns validators for the next block with voting status.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getNextBlockValidators ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-03-05 14:48:30 +00:00
var validators keys . PublicKeys
2020-10-01 12:26:54 +00:00
validators , err := s . chain . GetNextBlockValidators ( )
2020-03-05 14:48:30 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Can't get next block validators" , err . Error ( ) )
2020-03-05 14:48:30 +00:00
}
enrollments , err := s . chain . GetEnrollments ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Can't get enrollments" , err . Error ( ) )
2020-03-05 14:48:30 +00:00
}
2020-08-10 14:50:19 +00:00
var res = make ( [ ] result . Validator , 0 )
2020-03-05 14:48:30 +00:00
for _ , v := range enrollments {
2022-07-01 09:34:43 +00:00
if ! validators . Contains ( v . Key ) {
continue
}
2020-03-05 14:48:30 +00:00
res = append ( res , result . Validator {
2020-04-26 17:04:16 +00:00
PublicKey : * v . Key ,
Votes : v . Votes . Int64 ( ) ,
2020-03-05 14:48:30 +00:00
} )
}
return res , nil
}
2021-05-12 20:17:03 +00:00
// getCommittee returns the current list of NEO committee members.
2022-07-22 16:09:29 +00:00
func ( s * Server ) getCommittee ( _ params . Params ) ( interface { } , * neorpc . Error ) {
2020-09-21 12:34:04 +00:00
keys , err := s . chain . GetCommittee ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "can't get committee members: %s" , err ) )
2020-09-21 12:34:04 +00:00
}
return keys , nil
}
2020-06-10 08:53:11 +00:00
// invokeFunction implements the `invokeFunction` RPC call.
2022-07-22 16:09:29 +00:00
func ( s * Server ) invokeFunction ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
tx , verbose , respErr := s . getInvokeFunctionParams ( reqParams )
if respErr != nil {
return nil , respErr
}
return s . runScriptInVM ( trigger . Application , tx . Script , util . Uint160 { } , tx , nil , verbose )
}
// invokeFunctionHistoric implements the `invokeFunctionHistoric` RPC call.
2022-07-22 16:09:29 +00:00
func ( s * Server ) invokeFunctionHistoric ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-10-06 10:24:57 +00:00
nextH , respErr := s . getHistoricParams ( reqParams )
2022-04-07 15:13:08 +00:00
if respErr != nil {
return nil , respErr
}
2021-11-20 16:25:42 +00:00
if len ( reqParams ) < 2 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2021-11-20 16:25:42 +00:00
}
2022-04-07 15:13:08 +00:00
tx , verbose , respErr := s . getInvokeFunctionParams ( reqParams [ 1 : ] )
if respErr != nil {
return nil , respErr
}
2022-10-06 10:24:57 +00:00
return s . runScriptInVM ( trigger . Application , tx . Script , util . Uint160 { } , tx , & nextH , verbose )
2022-04-07 15:13:08 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getInvokeFunctionParams ( reqParams params . Params ) ( * transaction . Transaction , bool , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
if len ( reqParams ) < 2 {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . ErrInvalidParams
2022-04-07 15:13:08 +00:00
}
2020-09-25 15:43:47 +00:00
scriptHash , responseErr := s . contractScriptHashFromParam ( reqParams . Value ( 0 ) )
if responseErr != nil {
2022-04-07 15:13:08 +00:00
return nil , false , responseErr
2019-11-26 10:13:17 +00:00
}
2021-11-20 16:25:42 +00:00
method , err := reqParams [ 1 ] . GetString ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . ErrInvalidParams
2021-11-20 16:25:42 +00:00
}
2022-07-07 14:41:01 +00:00
var invparams * params . Param
2021-11-20 16:25:42 +00:00
if len ( reqParams ) > 2 {
2022-07-07 14:41:01 +00:00
invparams = & reqParams [ 2 ]
2021-11-20 16:25:42 +00:00
}
2020-06-10 11:45:55 +00:00
tx := & transaction . Transaction { }
2021-11-20 16:25:42 +00:00
if len ( reqParams ) > 3 {
2020-12-14 12:23:39 +00:00
signers , _ , err := reqParams [ 3 ] . GetSignersWithWitnesses ( )
2020-06-10 11:45:55 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . ErrInvalidParams
2020-06-10 11:45:55 +00:00
}
2020-07-29 16:57:38 +00:00
tx . Signers = signers
2020-06-10 11:45:55 +00:00
}
2021-11-20 18:55:55 +00:00
var verbose bool
if len ( reqParams ) > 4 {
verbose , err = reqParams [ 4 ] . GetBoolean ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . ErrInvalidParams
2021-11-20 18:55:55 +00:00
}
}
2020-07-29 16:57:38 +00:00
if len ( tx . Signers ) == 0 {
2020-10-01 12:26:51 +00:00
tx . Signers = [ ] transaction . Signer { { Account : util . Uint160 { } , Scopes : transaction . None } }
2020-07-29 16:57:38 +00:00
}
2022-07-07 14:41:01 +00:00
script , err := params . CreateFunctionInvocationScript ( scriptHash , method , invparams )
2019-11-26 10:13:17 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . NewInternalServerError ( fmt . Sprintf ( "can't create invocation script: %s" , err ) )
2019-11-26 10:13:17 +00:00
}
2020-07-14 14:32:59 +00:00
tx . Script = script
2022-04-07 15:13:08 +00:00
return tx , verbose , nil
2019-11-26 10:13:17 +00:00
}
2019-10-29 15:31:39 +00:00
// invokescript implements the `invokescript` RPC call.
2022-07-22 16:09:29 +00:00
func ( s * Server ) invokescript ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
tx , verbose , respErr := s . getInvokeScriptParams ( reqParams )
if respErr != nil {
return nil , respErr
}
return s . runScriptInVM ( trigger . Application , tx . Script , util . Uint160 { } , tx , nil , verbose )
}
// invokescripthistoric implements the `invokescripthistoric` RPC call.
2022-07-22 16:09:29 +00:00
func ( s * Server ) invokescripthistoric ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-10-06 10:24:57 +00:00
nextH , respErr := s . getHistoricParams ( reqParams )
2022-04-07 15:13:08 +00:00
if respErr != nil {
return nil , respErr
}
if len ( reqParams ) < 2 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2019-10-29 15:31:39 +00:00
}
2022-04-07 15:13:08 +00:00
tx , verbose , respErr := s . getInvokeScriptParams ( reqParams [ 1 : ] )
if respErr != nil {
return nil , respErr
}
2022-10-06 10:24:57 +00:00
return s . runScriptInVM ( trigger . Application , tx . Script , util . Uint160 { } , tx , & nextH , verbose )
2022-04-07 15:13:08 +00:00
}
2019-11-21 14:42:02 +00:00
2022-07-22 16:09:29 +00:00
func ( s * Server ) getInvokeScriptParams ( reqParams params . Params ) ( * transaction . Transaction , bool , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
script , err := reqParams . Value ( 0 ) . GetBytesBase64 ( )
2019-10-29 15:31:39 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . ErrInvalidParams
2019-10-29 15:31:39 +00:00
}
2019-11-21 14:42:02 +00:00
2020-06-10 11:45:55 +00:00
tx := & transaction . Transaction { }
if len ( reqParams ) > 1 {
2021-09-09 14:26:53 +00:00
signers , witnesses , err := reqParams [ 1 ] . GetSignersWithWitnesses ( )
2020-06-10 11:45:55 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2020-06-10 11:45:55 +00:00
}
2020-07-29 16:57:38 +00:00
tx . Signers = signers
2021-09-09 14:26:53 +00:00
tx . Scripts = witnesses
2020-07-29 16:57:38 +00:00
}
2021-11-20 18:55:55 +00:00
var verbose bool
if len ( reqParams ) > 2 {
verbose , err = reqParams [ 2 ] . GetBoolean ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , false , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-11-20 18:55:55 +00:00
}
}
2020-07-29 16:57:38 +00:00
if len ( tx . Signers ) == 0 {
2020-10-01 12:26:51 +00:00
tx . Signers = [ ] transaction . Signer { { Account : util . Uint160 { } , Scopes : transaction . None } }
2020-06-10 11:45:55 +00:00
}
2020-07-14 14:32:59 +00:00
tx . Script = script
2022-04-07 15:13:08 +00:00
return tx , verbose , nil
2020-12-14 12:23:39 +00:00
}
// invokeContractVerify implements the `invokecontractverify` RPC call.
2022-07-22 16:09:29 +00:00
func ( s * Server ) invokeContractVerify ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
scriptHash , tx , invocationScript , respErr := s . getInvokeContractVerifyParams ( reqParams )
if respErr != nil {
return nil , respErr
}
return s . runScriptInVM ( trigger . Verification , invocationScript , scriptHash , tx , nil , false )
}
// invokeContractVerifyHistoric implements the `invokecontractverifyhistoric` RPC call.
2022-07-22 16:09:29 +00:00
func ( s * Server ) invokeContractVerifyHistoric ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-10-06 10:24:57 +00:00
nextH , respErr := s . getHistoricParams ( reqParams )
2022-04-07 15:13:08 +00:00
if respErr != nil {
return nil , respErr
}
if len ( reqParams ) < 2 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2022-04-07 15:13:08 +00:00
}
scriptHash , tx , invocationScript , respErr := s . getInvokeContractVerifyParams ( reqParams [ 1 : ] )
if respErr != nil {
return nil , respErr
}
2022-10-06 10:24:57 +00:00
return s . runScriptInVM ( trigger . Verification , invocationScript , scriptHash , tx , & nextH , false )
2022-04-07 15:13:08 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) getInvokeContractVerifyParams ( reqParams params . Params ) ( util . Uint160 , * transaction . Transaction , [ ] byte , * neorpc . Error ) {
2020-12-14 12:23:39 +00:00
scriptHash , responseErr := s . contractScriptHashFromParam ( reqParams . Value ( 0 ) )
if responseErr != nil {
2022-04-07 15:13:08 +00:00
return util . Uint160 { } , nil , nil , responseErr
2020-12-14 12:23:39 +00:00
}
2021-03-10 14:43:52 +00:00
bw := io . NewBufBinWriter ( )
if len ( reqParams ) > 1 {
args , err := reqParams [ 1 ] . GetArray ( ) // second `invokecontractverify` parameter is an array of arguments for `verify` method
if err != nil {
2022-07-22 16:09:29 +00:00
return util . Uint160 { } , nil , nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-03-10 14:43:52 +00:00
}
if len ( args ) > 0 {
2022-07-07 14:41:01 +00:00
err := params . ExpandArrayIntoScript ( bw . BinWriter , args )
2021-03-10 14:43:52 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return util . Uint160 { } , nil , nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "can't create witness invocation script: %s" , err ) )
2021-03-10 14:43:52 +00:00
}
}
}
invocationScript := bw . Bytes ( )
tx := & transaction . Transaction { Script : [ ] byte { byte ( opcode . RET ) } } // need something in script
2020-12-14 12:23:39 +00:00
if len ( reqParams ) > 2 {
signers , witnesses , err := reqParams [ 2 ] . GetSignersWithWitnesses ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return util . Uint160 { } , nil , nil , neorpc . ErrInvalidParams
2020-12-14 12:23:39 +00:00
}
2021-03-10 14:43:52 +00:00
tx . Signers = signers
tx . Scripts = witnesses
} else { // fill the only known signer - the contract with `verify` method
tx . Signers = [ ] transaction . Signer { { Account : scriptHash } }
tx . Scripts = [ ] transaction . Witness { { InvocationScript : invocationScript , VerificationScript : [ ] byte { } } }
2020-12-14 12:23:39 +00:00
}
2022-04-07 15:13:08 +00:00
return scriptHash , tx , invocationScript , nil
}
2022-10-07 13:06:12 +00:00
// getHistoricParams checks that historic calls are supported and returns index of
// a fake next block to perform the historic call. It also checks that
2022-04-07 15:13:08 +00:00
// specified stateroot is stored at the specified height for further request
// handling consistency.
2022-10-06 10:24:57 +00:00
func ( s * Server ) getHistoricParams ( reqParams params . Params ) ( uint32 , * neorpc . Error ) {
2022-12-06 13:34:38 +00:00
if s . chain . GetConfig ( ) . Ledger . KeepOnlyLatestState {
2022-10-06 10:24:57 +00:00
return 0 , neorpc . NewInvalidRequestError ( fmt . Sprintf ( "only latest state is supported: %s" , errKeepOnlyLatestState ) )
2022-04-07 15:13:08 +00:00
}
if len ( reqParams ) < 1 {
2022-10-06 10:24:57 +00:00
return 0 , neorpc . ErrInvalidParams
2022-04-07 15:13:08 +00:00
}
height , respErr := s . blockHeightFromParam ( reqParams . Value ( 0 ) )
if respErr != nil {
hash , err := reqParams . Value ( 0 ) . GetUint256 ( )
if err != nil {
2022-10-06 10:24:57 +00:00
return 0 , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "invalid block hash or index or stateroot hash: %s" , err ) )
2022-04-07 15:13:08 +00:00
}
b , err := s . chain . GetBlock ( hash )
if err != nil {
stateH , err := s . chain . GetStateModule ( ) . GetLatestStateHeight ( hash )
if err != nil {
2022-10-06 10:24:57 +00:00
return 0 , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "unknown block or stateroot: %s" , err ) )
2022-04-07 15:13:08 +00:00
}
2022-11-18 20:19:50 +00:00
height = stateH
2022-04-07 15:13:08 +00:00
} else {
2022-11-18 20:19:50 +00:00
height = b . Index
2022-04-07 15:13:08 +00:00
}
}
2022-11-18 20:19:50 +00:00
return height + 1 , nil
2021-11-17 20:04:50 +00:00
}
2020-11-19 15:01:42 +00:00
2022-10-06 10:24:57 +00:00
func ( s * Server ) prepareInvocationContext ( t trigger . Type , script [ ] byte , contractScriptHash util . Uint160 , tx * transaction . Transaction , nextH * uint32 , verbose bool ) ( * interop . Context , * neorpc . Error ) {
2022-04-07 15:13:08 +00:00
var (
err error
ic * interop . Context
)
2022-10-06 10:24:57 +00:00
if nextH == nil {
ic , err = s . chain . GetTestVM ( t , tx , nil )
2022-04-07 15:13:08 +00:00
if err != nil {
2022-10-06 10:24:57 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to create test VM: %s" , err ) )
2022-04-07 15:13:08 +00:00
}
} else {
2022-10-06 10:24:57 +00:00
ic , err = s . chain . GetTestHistoricVM ( t , tx , * nextH )
2022-04-07 15:13:08 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to create historic VM: %s" , err ) )
2022-04-07 15:13:08 +00:00
}
2021-11-17 20:04:50 +00:00
}
2021-11-20 18:55:55 +00:00
if verbose {
2022-01-12 22:20:08 +00:00
ic . VM . EnableInvocationTree ( )
2021-11-20 18:55:55 +00:00
}
2022-01-12 22:20:08 +00:00
ic . VM . GasLimit = int64 ( s . config . MaxGasInvoke )
2021-03-10 14:43:52 +00:00
if t == trigger . Verification {
// We need this special case because witnesses verification is not the simple System.Contract.Call,
// and we need to define exactly the amount of gas consumed for a contract witness verification.
2022-01-11 21:58:03 +00:00
gasPolicy := s . chain . GetMaxVerificationGAS ( )
2022-01-12 22:20:08 +00:00
if ic . VM . GasLimit > gasPolicy {
ic . VM . GasLimit = gasPolicy
2021-03-10 14:43:52 +00:00
}
2022-04-07 15:13:08 +00:00
err = s . chain . InitVerificationContext ( ic , contractScriptHash , & transaction . Witness { InvocationScript : script , VerificationScript : [ ] byte { } } )
2021-03-10 14:43:52 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "can't prepare verification VM: %s" , err ) )
2021-03-10 14:43:52 +00:00
}
} else {
2022-01-12 22:20:08 +00:00
ic . VM . LoadScriptWithFlags ( script , callflag . All )
2021-03-10 14:43:52 +00:00
}
rpc: add configuration extension for MPT-backed iterator sessions
Add ability to switch between current blockchain storage and MPT-backed
storage for iterator traversing process. It may be useful because
iterator implementation traverses underlying backed storage (BoltDB,
LevelDB) inside DB's Seek which is blocking operation for BoltDB:
```
Opening a read transaction and a write transaction in the same goroutine
can cause the writer to deadlock because the database periodically needs
to re-mmap itself as it grows and it cannot do that while a read transaction
is open.
If a long running read transaction (for example, a snapshot transaction)
is needed, you might want to set DB.InitialMmapSize to a large enough
value to avoid potential blocking of write transaction.
```
So during bbolt re-mmaping, standard blockchain DB operations (i.e. persist)
can be blocked until iterator resourses release. The described behaviour
is tested and confirmed on four-nodes privnet with BoltDB and
`SessionExpirationTime` set to be 180 seconds. After new iterator session
is added to the server, the subsequent persist took ~5m21s, see the log
record `2022-06-17T18:58:21.563+0300`:
```
anna@kiwi:~/Documents/GitProjects/nspcc-dev/neo-go$ ./bin/neo-go node -p
2022-06-17T18:52:21.535+0300 INFO initial gas supply is not set or wrong, setting default value {"InitialGASSupply": "52000000"}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSize is not set or wrong, setting default value {"MaxBlockSize": 262144}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSystemFee is not set or wrong, setting default value {"MaxBlockSystemFee": 900000000000}
2022-06-17T18:52:21.535+0300 INFO MaxTransactionsPerBlock is not set or wrong, using default value {"MaxTransactionsPerBlock": 512}
2022-06-17T18:52:21.535+0300 INFO MaxValidUntilBlockIncrement is not set or wrong, using default value {"MaxValidUntilBlockIncrement": 5760}
2022-06-17T18:52:21.535+0300 INFO Hardforks are not set, using default value
2022-06-17T18:52:21.543+0300 INFO no storage version found! creating genesis block
2022-06-17T18:52:21.546+0300 INFO ExtensiblePoolSize is not set or wrong, using default value {"ExtensiblePoolSize": 20}
2022-06-17T18:52:21.546+0300 INFO service is running {"service": "Prometheus", "endpoint": ":2112"}
2022-06-17T18:52:21.547+0300 INFO starting rpc-server {"endpoint": ":20331"}
2022-06-17T18:52:21.547+0300 INFO rpc-server iterator sessions are enabled
2022-06-17T18:52:21.547+0300 INFO service hasn't started since it's disabled {"service": "Pprof"}
2022-06-17T18:52:21.547+0300 INFO node started {"blockHeight": 0, "headerHeight": 0}
_ ____________ __________
/ | / / ____/ __ \ / ____/ __ \
/ |/ / __/ / / / /_____/ / __/ / / /
/ /| / /___/ /_/ /_____/ /_/ / /_/ /
/_/ |_/_____/\____/ \____/\____/
/NEO-GO:0.99.1-pre-53-g7ccb646e/
2022-06-17T18:52:21.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 1}
2022-06-17T18:52:21.550+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 1475228436}
2022-06-17T18:52:22.575+0300 INFO persisted to disk {"blocks": 65, "keys": 1410, "headerHeight": 65, "blockHeight": 65, "took": "28.193409ms"}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 2}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 3}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 4}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 6}
2022-06-17T18:52:24.549+0300 INFO started protocol {"addr": "127.0.0.1:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 3444438498}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:52:24.550+0300 INFO node reached synchronized state, starting services
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 2435677826}
2022-06-17T18:52:24.550+0300 INFO starting state validation service
2022-06-17T18:52:24.550+0300 INFO RPC server already started
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 10}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 970555896}
2022-06-17T18:52:24.551+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 5}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 4}
2022-06-17T18:52:29.564+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 66, "blockHeight": 66, "took": "12.51808ms"}
2022-06-17T18:52:44.558+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 67, "blockHeight": 67, "took": "1.563137ms"}
2022-06-17T18:55:21.549+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "ping/pong timeout", "peerCount": 3}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:55:21.553+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 1}
2022-06-17T18:55:21.554+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 970555896}
2022-06-17T18:55:24.554+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 2}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 3}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "10.78.13.84:59876", "peerCount": 4}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 7}
2022-06-17T18:55:24.556+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 76, "id": 3444438498}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 8}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 1475228436}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 9}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "172.200.0.3:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 2435677826}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.559+0300 WARN peer disconnected {"addr": "172.200.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 10}
2022-06-17T18:55:24.560+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 11}
2022-06-17T18:55:24.560+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "10.78.13.84:59876", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 11}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 13}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 10}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.563+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 6}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 5}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "max peers reached", "peerCount": 4}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 3}
2022-06-17T18:57:21.551+0300 WARN peer disconnected {"addr": "172.200.0.4:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:57:21.553+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 1}
2022-06-17T18:57:21.554+0300 INFO new peer connected {"addr": "10.78.13.84:20332", "peerCount": 2}
2022-06-17T18:57:21.555+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 3444438498}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 3}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:46076", "peerCount": 4}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 5}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:59972", "peerCount": 7}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 10}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 11}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 12}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 14}
2022-06-17T18:57:21.557+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 2435677826}
2022-06-17T18:57:21.557+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO started protocol {"addr": "172.200.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 970555896}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:46076", "error": "identical node id", "peerCount": 12}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 15}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:59972", "error": "identical node id", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 13}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "10.78.13.84:20332", "error": "unexpected empty payload: CMDVersion", "peerCount": 12}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 11}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 10}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 8}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:57:21.559+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 1475228436}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 6}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 5}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 4}
2022-06-17T18:58:21.561+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 68, "blockHeight": 68, "took": "5m21.993873018s"}
2022-06-17T18:58:21.563+0300 INFO persisted to disk {"blocks": 8, "keys": 111, "headerHeight": 76, "blockHeight": 76, "took": "2.243347ms"}
2022-06-17T18:58:22.567+0300 INFO persisted to disk {"blocks": 10, "keys": 135, "headerHeight": 86, "blockHeight": 86, "took": "5.637669ms"}
2022-06-17T18:58:25.565+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 87, "blockHeight": 87, "took": "1.879912ms"}
2022-06-17T18:58:40.572+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 88, "blockHeight": 88, "took": "1.560317ms"}
2022-06-17T18:58:55.579+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 89, "blockHeight": 89, "took": "1.925225ms"}
2022-06-17T18:59:10.587+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 90, "blockHeight": 90, "took": "3.118073ms"}
2022-06-17T18:59:25.592+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 91, "blockHeight": 91, "took": "1.607248ms"}
2022-06-17T18:59:40.600+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 92, "blockHeight": 92, "took": "931.806µs"}
2022-06-17T18:59:55.610+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 93, "blockHeight": 93, "took": "2.019041ms"}
```
2022-06-20 15:20:57 +00:00
return ic , nil
}
// runScriptInVM runs the given script in a new test VM and returns the invocation
// result. The script is either a simple script in case of `application` trigger,
// witness invocation script in case of `verification` trigger (it pushes `verify`
// arguments on stack before verification). In case of contract verification
// contractScriptHash should be specified.
2022-10-06 10:24:57 +00:00
func ( s * Server ) runScriptInVM ( t trigger . Type , script [ ] byte , contractScriptHash util . Uint160 , tx * transaction . Transaction , nextH * uint32 , verbose bool ) ( * result . Invoke , * neorpc . Error ) {
ic , respErr := s . prepareInvocationContext ( t , script , contractScriptHash , tx , nextH , verbose )
rpc: add configuration extension for MPT-backed iterator sessions
Add ability to switch between current blockchain storage and MPT-backed
storage for iterator traversing process. It may be useful because
iterator implementation traverses underlying backed storage (BoltDB,
LevelDB) inside DB's Seek which is blocking operation for BoltDB:
```
Opening a read transaction and a write transaction in the same goroutine
can cause the writer to deadlock because the database periodically needs
to re-mmap itself as it grows and it cannot do that while a read transaction
is open.
If a long running read transaction (for example, a snapshot transaction)
is needed, you might want to set DB.InitialMmapSize to a large enough
value to avoid potential blocking of write transaction.
```
So during bbolt re-mmaping, standard blockchain DB operations (i.e. persist)
can be blocked until iterator resourses release. The described behaviour
is tested and confirmed on four-nodes privnet with BoltDB and
`SessionExpirationTime` set to be 180 seconds. After new iterator session
is added to the server, the subsequent persist took ~5m21s, see the log
record `2022-06-17T18:58:21.563+0300`:
```
anna@kiwi:~/Documents/GitProjects/nspcc-dev/neo-go$ ./bin/neo-go node -p
2022-06-17T18:52:21.535+0300 INFO initial gas supply is not set or wrong, setting default value {"InitialGASSupply": "52000000"}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSize is not set or wrong, setting default value {"MaxBlockSize": 262144}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSystemFee is not set or wrong, setting default value {"MaxBlockSystemFee": 900000000000}
2022-06-17T18:52:21.535+0300 INFO MaxTransactionsPerBlock is not set or wrong, using default value {"MaxTransactionsPerBlock": 512}
2022-06-17T18:52:21.535+0300 INFO MaxValidUntilBlockIncrement is not set or wrong, using default value {"MaxValidUntilBlockIncrement": 5760}
2022-06-17T18:52:21.535+0300 INFO Hardforks are not set, using default value
2022-06-17T18:52:21.543+0300 INFO no storage version found! creating genesis block
2022-06-17T18:52:21.546+0300 INFO ExtensiblePoolSize is not set or wrong, using default value {"ExtensiblePoolSize": 20}
2022-06-17T18:52:21.546+0300 INFO service is running {"service": "Prometheus", "endpoint": ":2112"}
2022-06-17T18:52:21.547+0300 INFO starting rpc-server {"endpoint": ":20331"}
2022-06-17T18:52:21.547+0300 INFO rpc-server iterator sessions are enabled
2022-06-17T18:52:21.547+0300 INFO service hasn't started since it's disabled {"service": "Pprof"}
2022-06-17T18:52:21.547+0300 INFO node started {"blockHeight": 0, "headerHeight": 0}
_ ____________ __________
/ | / / ____/ __ \ / ____/ __ \
/ |/ / __/ / / / /_____/ / __/ / / /
/ /| / /___/ /_/ /_____/ /_/ / /_/ /
/_/ |_/_____/\____/ \____/\____/
/NEO-GO:0.99.1-pre-53-g7ccb646e/
2022-06-17T18:52:21.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 1}
2022-06-17T18:52:21.550+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 1475228436}
2022-06-17T18:52:22.575+0300 INFO persisted to disk {"blocks": 65, "keys": 1410, "headerHeight": 65, "blockHeight": 65, "took": "28.193409ms"}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 2}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 3}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 4}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 6}
2022-06-17T18:52:24.549+0300 INFO started protocol {"addr": "127.0.0.1:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 3444438498}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:52:24.550+0300 INFO node reached synchronized state, starting services
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 2435677826}
2022-06-17T18:52:24.550+0300 INFO starting state validation service
2022-06-17T18:52:24.550+0300 INFO RPC server already started
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 10}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 970555896}
2022-06-17T18:52:24.551+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 5}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 4}
2022-06-17T18:52:29.564+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 66, "blockHeight": 66, "took": "12.51808ms"}
2022-06-17T18:52:44.558+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 67, "blockHeight": 67, "took": "1.563137ms"}
2022-06-17T18:55:21.549+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "ping/pong timeout", "peerCount": 3}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:55:21.553+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 1}
2022-06-17T18:55:21.554+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 970555896}
2022-06-17T18:55:24.554+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 2}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 3}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "10.78.13.84:59876", "peerCount": 4}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 7}
2022-06-17T18:55:24.556+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 76, "id": 3444438498}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 8}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 1475228436}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 9}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "172.200.0.3:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 2435677826}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.559+0300 WARN peer disconnected {"addr": "172.200.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 10}
2022-06-17T18:55:24.560+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 11}
2022-06-17T18:55:24.560+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "10.78.13.84:59876", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 11}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 13}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 10}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.563+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 6}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 5}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "max peers reached", "peerCount": 4}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 3}
2022-06-17T18:57:21.551+0300 WARN peer disconnected {"addr": "172.200.0.4:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:57:21.553+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 1}
2022-06-17T18:57:21.554+0300 INFO new peer connected {"addr": "10.78.13.84:20332", "peerCount": 2}
2022-06-17T18:57:21.555+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 3444438498}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 3}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:46076", "peerCount": 4}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 5}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:59972", "peerCount": 7}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 10}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 11}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 12}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 14}
2022-06-17T18:57:21.557+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 2435677826}
2022-06-17T18:57:21.557+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO started protocol {"addr": "172.200.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 970555896}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:46076", "error": "identical node id", "peerCount": 12}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 15}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:59972", "error": "identical node id", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 13}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "10.78.13.84:20332", "error": "unexpected empty payload: CMDVersion", "peerCount": 12}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 11}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 10}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 8}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:57:21.559+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 1475228436}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 6}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 5}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 4}
2022-06-17T18:58:21.561+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 68, "blockHeight": 68, "took": "5m21.993873018s"}
2022-06-17T18:58:21.563+0300 INFO persisted to disk {"blocks": 8, "keys": 111, "headerHeight": 76, "blockHeight": 76, "took": "2.243347ms"}
2022-06-17T18:58:22.567+0300 INFO persisted to disk {"blocks": 10, "keys": 135, "headerHeight": 86, "blockHeight": 86, "took": "5.637669ms"}
2022-06-17T18:58:25.565+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 87, "blockHeight": 87, "took": "1.879912ms"}
2022-06-17T18:58:40.572+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 88, "blockHeight": 88, "took": "1.560317ms"}
2022-06-17T18:58:55.579+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 89, "blockHeight": 89, "took": "1.925225ms"}
2022-06-17T18:59:10.587+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 90, "blockHeight": 90, "took": "3.118073ms"}
2022-06-17T18:59:25.592+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 91, "blockHeight": 91, "took": "1.607248ms"}
2022-06-17T18:59:40.600+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 92, "blockHeight": 92, "took": "931.806µs"}
2022-06-17T18:59:55.610+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 93, "blockHeight": 93, "took": "2.019041ms"}
```
2022-06-20 15:20:57 +00:00
if respErr != nil {
return nil , respErr
}
err := ic . VM . Run ( )
2020-10-05 13:33:20 +00:00
var faultException string
if err != nil {
faultException = err . Error ( )
}
2022-07-08 20:25:22 +00:00
items := ic . VM . Estack ( ) . ToArray ( )
sess := s . postProcessExecStack ( items )
var id uuid . UUID
if sess != nil {
2022-10-06 10:24:57 +00:00
// nextH == nil only when we're not using MPT-backed storage, therefore
2022-07-08 20:25:22 +00:00
// the second attempt won't stop here.
2022-10-06 10:24:57 +00:00
if s . config . SessionBackedByMPT && nextH == nil {
2022-07-08 20:25:22 +00:00
ic . Finalize ( )
// Rerun with MPT-backed storage.
2022-10-06 10:24:57 +00:00
return s . runScriptInVM ( t , script , contractScriptHash , tx , & ic . Block . Index , verbose )
2022-07-08 20:25:22 +00:00
}
id = uuid . New ( )
sessionID := id . String ( )
sess . finalize = ic . Finalize
sess . timer = time . AfterFunc ( time . Second * time . Duration ( s . config . SessionExpirationTime ) , func ( ) {
2022-07-07 19:03:11 +00:00
s . sessionsLock . Lock ( )
2022-07-08 20:25:22 +00:00
defer s . sessionsLock . Unlock ( )
if len ( s . sessions ) == 0 {
return
}
2022-07-07 19:03:11 +00:00
sess , ok := s . sessions [ sessionID ]
if ! ok {
2022-07-08 20:25:22 +00:00
return
rpc: add configuration extension for MPT-backed iterator sessions
Add ability to switch between current blockchain storage and MPT-backed
storage for iterator traversing process. It may be useful because
iterator implementation traverses underlying backed storage (BoltDB,
LevelDB) inside DB's Seek which is blocking operation for BoltDB:
```
Opening a read transaction and a write transaction in the same goroutine
can cause the writer to deadlock because the database periodically needs
to re-mmap itself as it grows and it cannot do that while a read transaction
is open.
If a long running read transaction (for example, a snapshot transaction)
is needed, you might want to set DB.InitialMmapSize to a large enough
value to avoid potential blocking of write transaction.
```
So during bbolt re-mmaping, standard blockchain DB operations (i.e. persist)
can be blocked until iterator resourses release. The described behaviour
is tested and confirmed on four-nodes privnet with BoltDB and
`SessionExpirationTime` set to be 180 seconds. After new iterator session
is added to the server, the subsequent persist took ~5m21s, see the log
record `2022-06-17T18:58:21.563+0300`:
```
anna@kiwi:~/Documents/GitProjects/nspcc-dev/neo-go$ ./bin/neo-go node -p
2022-06-17T18:52:21.535+0300 INFO initial gas supply is not set or wrong, setting default value {"InitialGASSupply": "52000000"}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSize is not set or wrong, setting default value {"MaxBlockSize": 262144}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSystemFee is not set or wrong, setting default value {"MaxBlockSystemFee": 900000000000}
2022-06-17T18:52:21.535+0300 INFO MaxTransactionsPerBlock is not set or wrong, using default value {"MaxTransactionsPerBlock": 512}
2022-06-17T18:52:21.535+0300 INFO MaxValidUntilBlockIncrement is not set or wrong, using default value {"MaxValidUntilBlockIncrement": 5760}
2022-06-17T18:52:21.535+0300 INFO Hardforks are not set, using default value
2022-06-17T18:52:21.543+0300 INFO no storage version found! creating genesis block
2022-06-17T18:52:21.546+0300 INFO ExtensiblePoolSize is not set or wrong, using default value {"ExtensiblePoolSize": 20}
2022-06-17T18:52:21.546+0300 INFO service is running {"service": "Prometheus", "endpoint": ":2112"}
2022-06-17T18:52:21.547+0300 INFO starting rpc-server {"endpoint": ":20331"}
2022-06-17T18:52:21.547+0300 INFO rpc-server iterator sessions are enabled
2022-06-17T18:52:21.547+0300 INFO service hasn't started since it's disabled {"service": "Pprof"}
2022-06-17T18:52:21.547+0300 INFO node started {"blockHeight": 0, "headerHeight": 0}
_ ____________ __________
/ | / / ____/ __ \ / ____/ __ \
/ |/ / __/ / / / /_____/ / __/ / / /
/ /| / /___/ /_/ /_____/ /_/ / /_/ /
/_/ |_/_____/\____/ \____/\____/
/NEO-GO:0.99.1-pre-53-g7ccb646e/
2022-06-17T18:52:21.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 1}
2022-06-17T18:52:21.550+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 1475228436}
2022-06-17T18:52:22.575+0300 INFO persisted to disk {"blocks": 65, "keys": 1410, "headerHeight": 65, "blockHeight": 65, "took": "28.193409ms"}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 2}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 3}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 4}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 6}
2022-06-17T18:52:24.549+0300 INFO started protocol {"addr": "127.0.0.1:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 3444438498}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:52:24.550+0300 INFO node reached synchronized state, starting services
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 2435677826}
2022-06-17T18:52:24.550+0300 INFO starting state validation service
2022-06-17T18:52:24.550+0300 INFO RPC server already started
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 10}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 970555896}
2022-06-17T18:52:24.551+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 5}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 4}
2022-06-17T18:52:29.564+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 66, "blockHeight": 66, "took": "12.51808ms"}
2022-06-17T18:52:44.558+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 67, "blockHeight": 67, "took": "1.563137ms"}
2022-06-17T18:55:21.549+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "ping/pong timeout", "peerCount": 3}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:55:21.553+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 1}
2022-06-17T18:55:21.554+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 970555896}
2022-06-17T18:55:24.554+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 2}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 3}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "10.78.13.84:59876", "peerCount": 4}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 7}
2022-06-17T18:55:24.556+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 76, "id": 3444438498}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 8}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 1475228436}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 9}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "172.200.0.3:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 2435677826}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.559+0300 WARN peer disconnected {"addr": "172.200.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 10}
2022-06-17T18:55:24.560+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 11}
2022-06-17T18:55:24.560+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "10.78.13.84:59876", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 11}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 13}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 10}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.563+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 6}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 5}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "max peers reached", "peerCount": 4}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 3}
2022-06-17T18:57:21.551+0300 WARN peer disconnected {"addr": "172.200.0.4:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:57:21.553+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 1}
2022-06-17T18:57:21.554+0300 INFO new peer connected {"addr": "10.78.13.84:20332", "peerCount": 2}
2022-06-17T18:57:21.555+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 3444438498}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 3}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:46076", "peerCount": 4}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 5}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:59972", "peerCount": 7}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 10}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 11}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 12}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 14}
2022-06-17T18:57:21.557+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 2435677826}
2022-06-17T18:57:21.557+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO started protocol {"addr": "172.200.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 970555896}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:46076", "error": "identical node id", "peerCount": 12}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 15}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:59972", "error": "identical node id", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 13}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "10.78.13.84:20332", "error": "unexpected empty payload: CMDVersion", "peerCount": 12}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 11}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 10}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 8}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:57:21.559+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 1475228436}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 6}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 5}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 4}
2022-06-17T18:58:21.561+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 68, "blockHeight": 68, "took": "5m21.993873018s"}
2022-06-17T18:58:21.563+0300 INFO persisted to disk {"blocks": 8, "keys": 111, "headerHeight": 76, "blockHeight": 76, "took": "2.243347ms"}
2022-06-17T18:58:22.567+0300 INFO persisted to disk {"blocks": 10, "keys": 135, "headerHeight": 86, "blockHeight": 86, "took": "5.637669ms"}
2022-06-17T18:58:25.565+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 87, "blockHeight": 87, "took": "1.879912ms"}
2022-06-17T18:58:40.572+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 88, "blockHeight": 88, "took": "1.560317ms"}
2022-06-17T18:58:55.579+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 89, "blockHeight": 89, "took": "1.925225ms"}
2022-06-17T18:59:10.587+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 90, "blockHeight": 90, "took": "3.118073ms"}
2022-06-17T18:59:25.592+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 91, "blockHeight": 91, "took": "1.607248ms"}
2022-06-17T18:59:40.600+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 92, "blockHeight": 92, "took": "931.806µs"}
2022-06-17T18:59:55.610+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 93, "blockHeight": 93, "took": "2.019041ms"}
```
2022-06-20 15:20:57 +00:00
}
2022-07-08 20:25:22 +00:00
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 ( )
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( "max session capacity reached" )
2022-07-08 20:25:22 +00:00
}
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 ( ) ) ,
}
}
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 { } ) {
2022-07-07 19:03:11 +00:00
sess . iteratorIdentifiers = append ( sess . iteratorIdentifiers , & iteratorIdentifier {
2022-07-08 20:25:22 +00:00
ID : id . String ( ) ,
Item : v ,
2022-07-07 19:03:11 +00:00
} )
rpc: add configuration extension for MPT-backed iterator sessions
Add ability to switch between current blockchain storage and MPT-backed
storage for iterator traversing process. It may be useful because
iterator implementation traverses underlying backed storage (BoltDB,
LevelDB) inside DB's Seek which is blocking operation for BoltDB:
```
Opening a read transaction and a write transaction in the same goroutine
can cause the writer to deadlock because the database periodically needs
to re-mmap itself as it grows and it cannot do that while a read transaction
is open.
If a long running read transaction (for example, a snapshot transaction)
is needed, you might want to set DB.InitialMmapSize to a large enough
value to avoid potential blocking of write transaction.
```
So during bbolt re-mmaping, standard blockchain DB operations (i.e. persist)
can be blocked until iterator resourses release. The described behaviour
is tested and confirmed on four-nodes privnet with BoltDB and
`SessionExpirationTime` set to be 180 seconds. After new iterator session
is added to the server, the subsequent persist took ~5m21s, see the log
record `2022-06-17T18:58:21.563+0300`:
```
anna@kiwi:~/Documents/GitProjects/nspcc-dev/neo-go$ ./bin/neo-go node -p
2022-06-17T18:52:21.535+0300 INFO initial gas supply is not set or wrong, setting default value {"InitialGASSupply": "52000000"}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSize is not set or wrong, setting default value {"MaxBlockSize": 262144}
2022-06-17T18:52:21.535+0300 INFO MaxBlockSystemFee is not set or wrong, setting default value {"MaxBlockSystemFee": 900000000000}
2022-06-17T18:52:21.535+0300 INFO MaxTransactionsPerBlock is not set or wrong, using default value {"MaxTransactionsPerBlock": 512}
2022-06-17T18:52:21.535+0300 INFO MaxValidUntilBlockIncrement is not set or wrong, using default value {"MaxValidUntilBlockIncrement": 5760}
2022-06-17T18:52:21.535+0300 INFO Hardforks are not set, using default value
2022-06-17T18:52:21.543+0300 INFO no storage version found! creating genesis block
2022-06-17T18:52:21.546+0300 INFO ExtensiblePoolSize is not set or wrong, using default value {"ExtensiblePoolSize": 20}
2022-06-17T18:52:21.546+0300 INFO service is running {"service": "Prometheus", "endpoint": ":2112"}
2022-06-17T18:52:21.547+0300 INFO starting rpc-server {"endpoint": ":20331"}
2022-06-17T18:52:21.547+0300 INFO rpc-server iterator sessions are enabled
2022-06-17T18:52:21.547+0300 INFO service hasn't started since it's disabled {"service": "Pprof"}
2022-06-17T18:52:21.547+0300 INFO node started {"blockHeight": 0, "headerHeight": 0}
_ ____________ __________
/ | / / ____/ __ \ / ____/ __ \
/ |/ / __/ / / / /_____/ / __/ / / /
/ /| / /___/ /_/ /_____/ /_/ / /_/ /
/_/ |_/_____/\____/ \____/\____/
/NEO-GO:0.99.1-pre-53-g7ccb646e/
2022-06-17T18:52:21.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 1}
2022-06-17T18:52:21.550+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 1475228436}
2022-06-17T18:52:22.575+0300 INFO persisted to disk {"blocks": 65, "keys": 1410, "headerHeight": 65, "blockHeight": 65, "took": "28.193409ms"}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 2}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 3}
2022-06-17T18:52:24.548+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 4}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 6}
2022-06-17T18:52:24.549+0300 INFO started protocol {"addr": "127.0.0.1:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 3444438498}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.549+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:52:24.550+0300 INFO node reached synchronized state, starting services
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 2435677826}
2022-06-17T18:52:24.550+0300 INFO starting state validation service
2022-06-17T18:52:24.550+0300 INFO RPC server already started
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:52:24.550+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 10}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:52:24.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.550+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 65, "id": 970555896}
2022-06-17T18:52:24.551+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 7}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 6}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 5}
2022-06-17T18:52:24.551+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 4}
2022-06-17T18:52:29.564+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 66, "blockHeight": 66, "took": "12.51808ms"}
2022-06-17T18:52:44.558+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 67, "blockHeight": 67, "took": "1.563137ms"}
2022-06-17T18:55:21.549+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "ping/pong timeout", "peerCount": 3}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:55:21.550+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:55:21.553+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 1}
2022-06-17T18:55:21.554+0300 INFO started protocol {"addr": "127.0.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 970555896}
2022-06-17T18:55:24.554+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 2}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 3}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "10.78.13.84:59876", "peerCount": 4}
2022-06-17T18:55:24.555+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 5}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 7}
2022-06-17T18:55:24.556+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 76, "id": 3444438498}
2022-06-17T18:55:24.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 8}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 1475228436}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 9}
2022-06-17T18:55:24.558+0300 INFO started protocol {"addr": "172.200.0.3:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 77, "id": 2435677826}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.559+0300 WARN peer disconnected {"addr": "172.200.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.559+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 10}
2022-06-17T18:55:24.560+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 11}
2022-06-17T18:55:24.560+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "10.78.13.84:59876", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 9}
2022-06-17T18:55:24.561+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 8}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 9}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 10}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 11}
2022-06-17T18:55:24.561+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 12}
2022-06-17T18:55:24.562+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 13}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 12}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 11}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 10}
2022-06-17T18:55:24.562+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:55:24.563+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 10}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 9}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 8}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 7}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 6}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 5}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "max peers reached", "peerCount": 4}
2022-06-17T18:55:24.563+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 3}
2022-06-17T18:57:21.551+0300 WARN peer disconnected {"addr": "172.200.0.4:20333", "error": "ping/pong timeout", "peerCount": 2}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "ping/pong timeout", "peerCount": 1}
2022-06-17T18:57:21.552+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "ping/pong timeout", "peerCount": 0}
2022-06-17T18:57:21.553+0300 INFO new peer connected {"addr": "172.200.0.4:20333", "peerCount": 1}
2022-06-17T18:57:21.554+0300 INFO new peer connected {"addr": "10.78.13.84:20332", "peerCount": 2}
2022-06-17T18:57:21.555+0300 INFO started protocol {"addr": "172.200.0.4:20333", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 3444438498}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 3}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:46076", "peerCount": 4}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.1:20335", "peerCount": 5}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "172.200.0.254:20332", "peerCount": 6}
2022-06-17T18:57:21.556+0300 INFO new peer connected {"addr": "10.78.13.84:59972", "peerCount": 7}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 8}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 9}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.2:20336", "peerCount": 10}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 11}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 12}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "172.200.0.3:20334", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 14}
2022-06-17T18:57:21.557+0300 INFO started protocol {"addr": "127.0.0.1:20334", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 2435677826}
2022-06-17T18:57:21.557+0300 WARN peer disconnected {"addr": "172.200.0.2:20336", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.557+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO started protocol {"addr": "172.200.0.1:20335", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 970555896}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "172.200.0.254:20332", "error": "identical node id", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20334", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "max peers reached", "peerCount": 13}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:46076", "error": "identical node id", "peerCount": 12}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20333", "peerCount": 13}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20335", "peerCount": 14}
2022-06-17T18:57:21.558+0300 INFO new peer connected {"addr": "127.0.0.1:20336", "peerCount": 15}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "10.78.13.84:59972", "error": "identical node id", "peerCount": 14}
2022-06-17T18:57:21.558+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 13}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "10.78.13.84:20332", "error": "unexpected empty payload: CMDVersion", "peerCount": 12}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 11}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "172.200.0.3:20334", "error": "unexpected empty payload: CMDVersion", "peerCount": 10}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "unexpected empty payload: CMDVersion", "peerCount": 9}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20334", "error": "already connected", "peerCount": 8}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "unexpected empty payload: CMDVersion", "peerCount": 7}
2022-06-17T18:57:21.559+0300 INFO started protocol {"addr": "127.0.0.1:20336", "userAgent": "/NEO-GO:0.99.1-pre-53-g7ccb646e/", "startHeight": 82, "id": 1475228436}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20333", "error": "already connected", "peerCount": 6}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20335", "error": "already connected", "peerCount": 5}
2022-06-17T18:57:21.559+0300 WARN peer disconnected {"addr": "127.0.0.1:20336", "error": "already connected", "peerCount": 4}
2022-06-17T18:58:21.561+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 68, "blockHeight": 68, "took": "5m21.993873018s"}
2022-06-17T18:58:21.563+0300 INFO persisted to disk {"blocks": 8, "keys": 111, "headerHeight": 76, "blockHeight": 76, "took": "2.243347ms"}
2022-06-17T18:58:22.567+0300 INFO persisted to disk {"blocks": 10, "keys": 135, "headerHeight": 86, "blockHeight": 86, "took": "5.637669ms"}
2022-06-17T18:58:25.565+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 87, "blockHeight": 87, "took": "1.879912ms"}
2022-06-17T18:58:40.572+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 88, "blockHeight": 88, "took": "1.560317ms"}
2022-06-17T18:58:55.579+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 89, "blockHeight": 89, "took": "1.925225ms"}
2022-06-17T18:59:10.587+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 90, "blockHeight": 90, "took": "3.118073ms"}
2022-06-17T18:59:25.592+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 91, "blockHeight": 91, "took": "1.607248ms"}
2022-06-17T18:59:40.600+0300 INFO persisted to disk {"blocks": 1, "keys": 20, "headerHeight": 92, "blockHeight": 92, "took": "931.806µs"}
2022-06-17T18:59:55.610+0300 INFO persisted to disk {"blocks": 1, "keys": 19, "headerHeight": 93, "blockHeight": 93, "took": "2.019041ms"}
```
2022-06-20 15:20:57 +00:00
}
2022-06-15 18:23:29 +00:00
}
2022-07-08 20:25:22 +00:00
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
2022-06-15 18:23:29 +00:00
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) traverseIterator ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-06-15 18:23:29 +00:00
if ! s . config . SessionEnabled {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidRequestError ( "sessions are disabled" )
2022-06-15 18:23:29 +00:00
}
sID , err := reqParams . Value ( 0 ) . GetUUID ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "invalid session ID: %s" , err ) )
2022-06-15 18:23:29 +00:00
}
iID , err := reqParams . Value ( 1 ) . GetUUID ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "invalid iterator ID: %s" , err ) )
2022-06-15 18:23:29 +00:00
}
count , err := reqParams . Value ( 2 ) . GetInt ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "invalid iterator items count: %s" , err ) )
2022-06-15 18:23:29 +00:00
}
if err := checkInt32 ( count ) ; err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( "invalid iterator items count: not an int32" )
2022-06-15 18:23:29 +00:00
}
if count > s . config . MaxIteratorResultItems {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "iterator items count is out of range (%d at max)" , s . config . MaxIteratorResultItems ) )
2022-06-15 18:23:29 +00:00
}
s . sessionsLock . Lock ( )
session , ok := s . sessions [ sID . String ( ) ]
if ! ok {
s . sessionsLock . Unlock ( )
return [ ] json . RawMessage { } , nil
}
session . iteratorsLock . Lock ( )
// Perform `till` update only after session.iteratorsLock is taken in order to have more
// precise session lifetime.
session . timer . Reset ( time . Second * time . Duration ( s . config . SessionExpirationTime ) )
s . sessionsLock . Unlock ( )
var (
2022-07-08 20:25:22 +00:00
iIDStr = iID . String ( )
iVals [ ] stackitem . Item
2022-06-15 18:23:29 +00:00
)
2022-07-07 19:03:11 +00:00
for _ , it := range session . iteratorIdentifiers {
2022-06-15 18:23:29 +00:00
if iIDStr == it . ID {
2022-07-07 19:03:11 +00:00
iVals = iterator . Values ( it . Item , count )
2022-06-15 18:23:29 +00:00
break
}
}
session . iteratorsLock . Unlock ( )
result := make ( [ ] json . RawMessage , len ( iVals ) )
for j := range iVals {
result [ j ] , err = stackitem . ToJSONWithTypes ( iVals [ j ] )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( fmt . Sprintf ( "failed to marshal iterator value: %s" , err ) )
2022-06-15 18:23:29 +00:00
}
}
return result , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) terminateSession ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2022-06-15 18:23:29 +00:00
if ! s . config . SessionEnabled {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidRequestError ( "sessions are disabled" )
2022-06-15 18:23:29 +00:00
}
sID , err := reqParams . Value ( 0 ) . GetUUID ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "invalid session ID: %s" , err ) )
2022-06-15 18:23:29 +00:00
}
strSID := sID . String ( )
s . sessionsLock . Lock ( )
defer s . sessionsLock . Unlock ( )
session , ok := s . sessions [ strSID ]
if ok {
// Iterators access Seek channel under the hood; finalizer closes this channel, thus,
// we need to perform finalisation under iteratorsLock.
session . iteratorsLock . Lock ( )
2022-07-08 20:25:22 +00:00
session . finalize ( )
2022-06-15 18:23:29 +00:00
if ! session . timer . Stop ( ) {
<- session . timer . C
}
delete ( s . sessions , strSID )
session . iteratorsLock . Unlock ( )
}
return ok , nil
2019-10-29 15:31:39 +00:00
}
2022-12-07 13:51:03 +00:00
// submitBlock broadcasts a raw block over the Neo network.
2022-07-22 16:09:29 +00:00
func ( s * Server ) submitBlock ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2021-10-28 11:10:18 +00:00
blockBytes , err := reqParams . Value ( 0 ) . GetBytesBase64 ( )
2020-03-02 17:01:32 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "missing parameter or not a base64: %s" , err ) )
2020-03-02 17:01:32 +00:00
}
2021-03-25 18:46:52 +00:00
b := block . New ( s . stateRootEnabled )
2020-03-02 17:01:32 +00:00
r := io . NewBinReaderFromBuf ( blockBytes )
b . DecodeBinary ( r )
if r . Err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "can't decode block: %s" , r . Err ) )
2020-03-02 17:01:32 +00:00
}
2020-06-18 09:00:51 +00:00
err = s . chain . AddBlock ( b )
2020-03-02 17:01:32 +00:00
if err != nil {
2020-08-06 15:34:44 +00:00
switch {
case errors . Is ( err , core . ErrInvalidBlockIndex ) || errors . Is ( err , core . ErrAlreadyExists ) :
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrAlreadyExists , err . Error ( ) )
2020-03-02 17:01:32 +00:00
default :
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrValidationFailed , err . Error ( ) )
2020-03-02 17:01:32 +00:00
}
}
2020-07-21 07:41:18 +00:00
return & result . RelayResult {
Hash : b . Hash ( ) ,
} , nil
2020-03-02 17:01:32 +00:00
}
2022-12-07 13:51:03 +00:00
// submitNotaryRequest broadcasts P2PNotaryRequest over the Neo network.
2022-07-22 16:09:29 +00:00
func ( s * Server ) submitNotaryRequest ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2021-02-05 07:26:23 +00:00
if ! s . chain . P2PSigExtensionsEnabled ( ) {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "P2PSignatureExtensions are disabled" , "" )
2021-02-05 07:26:23 +00:00
}
2021-10-29 14:06:45 +00:00
bytePayload , err := ps . Value ( 0 ) . GetBytesBase64 ( )
2021-02-05 07:26:23 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "not a base64: %s" , err ) )
2021-02-05 07:26:23 +00:00
}
2021-03-25 19:18:47 +00:00
r , err := payload . NewP2PNotaryRequestFromBytes ( bytePayload )
2021-02-05 07:26:23 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "can't decode notary payload: %s" , err ) )
2021-02-05 07:26:23 +00:00
}
return getRelayResult ( s . coreServer . RelayP2PNotaryRequest ( r ) , r . FallbackTransaction . Hash ( ) )
}
// getRelayResult returns successful relay result or an error.
2022-07-22 16:09:29 +00:00
func getRelayResult ( err error , hash util . Uint256 ) ( interface { } , * neorpc . Error ) {
2021-02-17 11:51:54 +00:00
switch {
case err == nil :
2021-02-05 07:26:23 +00:00
return result . RelayResult {
Hash : hash ,
} , nil
2021-02-17 11:51:54 +00:00
case errors . Is ( err , core . ErrAlreadyExists ) :
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrAlreadyExists , err . Error ( ) )
2021-02-17 11:51:54 +00:00
case errors . Is ( err , core . ErrOOM ) :
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrOutOfMemory , err . Error ( ) )
2021-02-17 11:51:54 +00:00
case errors . Is ( err , core . ErrPolicy ) :
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrPolicyFail , err . Error ( ) )
2021-02-05 07:26:23 +00:00
default :
2022-07-22 16:09:29 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrValidationFailed , err . Error ( ) )
2021-02-05 07:26:23 +00:00
}
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) submitOracleResponse ( ps params . Params ) ( interface { } , * neorpc . Error ) {
2022-07-26 18:36:37 +00:00
oracle := s . oracle . Load ( ) . ( * OracleHandler )
if oracle == nil || * oracle == nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Oracle is not enabled" , "" )
2020-09-28 11:58:04 +00:00
}
var pub * keys . PublicKey
pubBytes , err := ps . Value ( 0 ) . GetBytesBase64 ( )
if err == nil {
pub , err = keys . NewPublicKeyFromBytes ( pubBytes , elliptic . P256 ( ) )
}
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "public key is missing: %s" , err ) )
2020-09-28 11:58:04 +00:00
}
reqID , err := ps . Value ( 1 ) . GetInt ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "request ID is missing: %s" , err ) )
2020-09-28 11:58:04 +00:00
}
txSig , err := ps . Value ( 2 ) . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "tx signature is missing: %s" , err ) )
2020-09-28 11:58:04 +00:00
}
msgSig , err := ps . Value ( 3 ) . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "msg signature is missing: %s" , err ) )
2020-09-28 11:58:04 +00:00
}
data := broadcaster . GetMessage ( pubBytes , uint64 ( reqID ) , txSig )
if ! pub . Verify ( msgSig , hash . Sha256 ( data ) . BytesBE ( ) ) {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewRPCError ( "Invalid request signature" , "" )
2020-09-28 11:58:04 +00:00
}
2022-07-26 18:36:37 +00:00
( * oracle ) . AddResponse ( pub , uint64 ( reqID ) , txSig )
2020-09-28 11:58:04 +00:00
return json . RawMessage ( [ ] byte ( "{}" ) ) , nil
}
2022-07-22 16:09:29 +00:00
func ( s * Server ) sendrawtransaction ( reqParams params . Params ) ( interface { } , * neorpc . Error ) {
2019-11-21 14:42:02 +00:00
if len ( reqParams ) < 1 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( "not enough parameters" )
2021-02-05 07:26:23 +00:00
}
byteTx , err := reqParams [ 0 ] . GetBytesBase64 ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "not a base64: %s" , err ) )
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
2021-03-25 16:18:01 +00:00
tx , err := transaction . NewTransactionFromBytes ( byteTx )
2021-02-05 07:26:23 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInvalidParamsError ( fmt . Sprintf ( "can't decode transaction: %s" , err ) )
2021-02-05 07:26:23 +00:00
}
return getRelayResult ( s . coreServer . RelayTxn ( tx ) , tx . Hash ( ) )
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
2020-05-10 22:00:19 +00:00
// subscribe handles subscription requests from websocket clients.
2022-07-22 16:09:29 +00:00
func ( s * Server ) subscribe ( reqParams params . Params , sub * subscriber ) ( interface { } , * neorpc . Error ) {
2020-06-04 11:58:47 +00:00
streamName , err := reqParams . Value ( 0 ) . GetString ( )
2020-05-10 22:00:19 +00:00
if err != nil {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-05-10 22:00:19 +00:00
}
2022-07-22 16:09:29 +00:00
event , err := neorpc . GetEventIDFromString ( streamName )
if err != nil || event == neorpc . MissedEventID {
return nil , neorpc . ErrInvalidParams
2020-05-10 22:00:19 +00:00
}
2022-07-22 16:09:29 +00:00
if event == neorpc . NotaryRequestEventID && ! s . chain . P2PSigExtensionsEnabled ( ) {
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , "P2PSigExtensions are disabled" )
2021-05-28 11:55:06 +00:00
}
2020-05-13 14:13:33 +00:00
// Optional filter.
var filter interface { }
2020-06-04 11:58:47 +00:00
if p := reqParams . Value ( 1 ) ; p != nil {
2021-10-28 11:10:18 +00:00
param := * p
jd := json . NewDecoder ( bytes . NewReader ( param . RawMessage ) )
2021-08-05 14:56:17 +00:00
jd . DisallowUnknownFields ( )
2020-05-13 14:13:33 +00:00
switch event {
2022-07-22 16:09:29 +00:00
case neorpc . BlockEventID :
flt := new ( neorpc . BlockFilter )
2021-08-05 14:56:17 +00:00
err = jd . Decode ( flt )
2021-10-28 11:10:18 +00:00
filter = * flt
2022-07-22 16:09:29 +00:00
case neorpc . TransactionEventID , neorpc . NotaryRequestEventID :
flt := new ( neorpc . TxFilter )
2021-08-05 14:56:17 +00:00
err = jd . Decode ( flt )
2021-10-28 11:10:18 +00:00
filter = * flt
2022-07-22 16:09:29 +00:00
case neorpc . NotificationEventID :
flt := new ( neorpc . NotificationFilter )
2021-08-05 14:56:17 +00:00
err = jd . Decode ( flt )
2021-10-28 11:10:18 +00:00
filter = * flt
2022-07-22 16:09:29 +00:00
case neorpc . ExecutionEventID :
flt := new ( neorpc . ExecutionFilter )
2021-08-05 14:56:17 +00:00
err = jd . Decode ( flt )
2022-11-16 10:32:39 +00:00
if err == nil && ( flt . State == nil || ( * flt . State == "HALT" || * flt . State == "FAULT" ) ) {
2021-10-28 11:10:18 +00:00
filter = * flt
2021-08-05 14:56:17 +00:00
} else if err == nil {
err = errors . New ( "invalid state" )
2021-05-28 11:55:06 +00:00
}
2020-05-13 14:13:33 +00:00
}
2021-08-05 14:56:17 +00:00
if err != nil {
2022-11-16 09:37:18 +00:00
return nil , neorpc . WrapErrorWithData ( neorpc . ErrInvalidParams , err . Error ( ) )
2021-08-05 14:56:17 +00:00
}
2020-05-13 14:13:33 +00:00
}
2020-05-10 22:00:19 +00:00
s . subsLock . Lock ( )
var id int
for ; id < len ( sub . feeds ) ; id ++ {
2022-07-22 16:09:29 +00:00
if sub . feeds [ id ] . event == neorpc . InvalidEventID {
2020-05-10 22:00:19 +00:00
break
}
}
if id == len ( sub . feeds ) {
2022-11-17 13:00:22 +00:00
s . subsLock . Unlock ( )
2022-07-22 16:09:29 +00:00
return nil , neorpc . NewInternalServerError ( "maximum number of subscriptions is reached" )
2020-05-10 22:00:19 +00:00
}
2020-05-13 14:13:33 +00:00
sub . feeds [ id ] . event = event
sub . feeds [ id ] . filter = filter
2022-11-17 13:00:22 +00:00
s . subsLock . Unlock ( )
s . subsCounterLock . Lock ( )
2022-11-18 07:40:28 +00:00
select {
case <- s . shutdown :
s . subsCounterLock . Unlock ( )
return nil , neorpc . NewInternalServerError ( "server is shutting down" )
default :
}
2020-05-10 22:00:19 +00:00
s . subscribeToChannel ( event )
2022-11-17 13:00:22 +00:00
s . subsCounterLock . Unlock ( )
2020-05-10 22:00:19 +00:00
return strconv . FormatInt ( int64 ( id ) , 10 ) , nil
}
// subscribeToChannel subscribes RPC server to appropriate chain events if
2022-11-17 13:00:22 +00:00
// it's not yet subscribed for them. It's supposed to be called with s.subsCounterLock
2020-05-10 22:00:19 +00:00
// taken by the caller.
2022-07-22 16:09:29 +00:00
func ( s * Server ) subscribeToChannel ( event neorpc . EventID ) {
2020-05-10 22:00:19 +00:00
switch event {
2022-07-22 16:09:29 +00:00
case neorpc . BlockEventID :
2020-05-10 22:00:19 +00:00
if s . blockSubs == 0 {
s . chain . SubscribeForBlocks ( s . blockCh )
}
s . blockSubs ++
2022-07-22 16:09:29 +00:00
case neorpc . TransactionEventID :
2020-05-10 22:00:19 +00:00
if s . transactionSubs == 0 {
s . chain . SubscribeForTransactions ( s . transactionCh )
}
s . transactionSubs ++
2022-07-22 16:09:29 +00:00
case neorpc . NotificationEventID :
2020-05-10 22:00:19 +00:00
if s . notificationSubs == 0 {
s . chain . SubscribeForNotifications ( s . notificationCh )
}
s . notificationSubs ++
2022-07-22 16:09:29 +00:00
case neorpc . ExecutionEventID :
2020-05-10 22:00:19 +00:00
if s . executionSubs == 0 {
s . chain . SubscribeForExecutions ( s . executionCh )
}
s . executionSubs ++
2022-07-22 16:09:29 +00:00
case neorpc . NotaryRequestEventID :
2021-05-28 11:55:06 +00:00
if s . notaryRequestSubs == 0 {
s . coreServer . SubscribeForNotaryRequests ( s . notaryRequestCh )
}
s . notaryRequestSubs ++
2020-05-10 22:00:19 +00:00
}
}
// unsubscribe handles unsubscription requests from websocket clients.
2022-07-22 16:09:29 +00:00
func ( s * Server ) unsubscribe ( reqParams params . Params , sub * subscriber ) ( interface { } , * neorpc . Error ) {
2020-06-04 11:58:47 +00:00
id , err := reqParams . Value ( 0 ) . GetInt ( )
2020-05-10 22:00:19 +00:00
if err != nil || id < 0 {
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-05-10 22:00:19 +00:00
}
s . subsLock . Lock ( )
2022-07-22 16:09:29 +00:00
if len ( sub . feeds ) <= id || sub . feeds [ id ] . event == neorpc . InvalidEventID {
2022-11-17 13:00:22 +00:00
s . subsLock . Unlock ( )
2022-07-22 16:09:29 +00:00
return nil , neorpc . ErrInvalidParams
2020-05-10 22:00:19 +00:00
}
2020-05-13 14:13:33 +00:00
event := sub . feeds [ id ] . event
2022-07-22 16:09:29 +00:00
sub . feeds [ id ] . event = neorpc . InvalidEventID
2020-05-13 14:13:33 +00:00
sub . feeds [ id ] . filter = nil
2022-11-17 13:00:22 +00:00
s . subsLock . Unlock ( )
s . subsCounterLock . Lock ( )
2020-05-10 22:00:19 +00:00
s . unsubscribeFromChannel ( event )
2022-11-17 13:00:22 +00:00
s . subsCounterLock . Unlock ( )
2020-05-10 22:00:19 +00:00
return true , nil
}
// unsubscribeFromChannel unsubscribes RPC server from appropriate chain events
2022-11-17 13:00:22 +00:00
// if there are no other subscribers for it. It must be called with s.subsConutersLock
// holding by the caller.
2022-07-22 16:09:29 +00:00
func ( s * Server ) unsubscribeFromChannel ( event neorpc . EventID ) {
2020-05-10 22:00:19 +00:00
switch event {
2022-07-22 16:09:29 +00:00
case neorpc . BlockEventID :
2020-05-10 22:00:19 +00:00
s . blockSubs --
if s . blockSubs == 0 {
s . chain . UnsubscribeFromBlocks ( s . blockCh )
}
2022-07-22 16:09:29 +00:00
case neorpc . TransactionEventID :
2020-05-10 22:00:19 +00:00
s . transactionSubs --
if s . transactionSubs == 0 {
s . chain . UnsubscribeFromTransactions ( s . transactionCh )
}
2022-07-22 16:09:29 +00:00
case neorpc . NotificationEventID :
2020-05-10 22:00:19 +00:00
s . notificationSubs --
if s . notificationSubs == 0 {
s . chain . UnsubscribeFromNotifications ( s . notificationCh )
}
2022-07-22 16:09:29 +00:00
case neorpc . ExecutionEventID :
2020-05-10 22:00:19 +00:00
s . executionSubs --
if s . executionSubs == 0 {
s . chain . UnsubscribeFromExecutions ( s . executionCh )
}
2022-07-22 16:09:29 +00:00
case neorpc . NotaryRequestEventID :
2021-05-28 11:55:06 +00:00
s . notaryRequestSubs --
if s . notaryRequestSubs == 0 {
s . coreServer . UnsubscribeFromNotaryRequests ( s . notaryRequestCh )
}
2020-05-10 22:00:19 +00:00
}
}
func ( s * Server ) handleSubEvents ( ) {
2022-07-22 16:09:29 +00:00
b , err := json . Marshal ( neorpc . Notification {
JSONRPC : neorpc . JSONRPCVersion ,
Event : neorpc . MissedEventID ,
2020-05-12 19:38:29 +00:00
Payload : make ( [ ] interface { } , 0 ) ,
} )
if err != nil {
s . log . Error ( "fatal: failed to marshal overflow event" , zap . Error ( err ) )
return
}
overflowMsg , err := websocket . NewPreparedMessage ( websocket . TextMessage , b )
if err != nil {
s . log . Error ( "fatal: failed to prepare overflow message" , zap . Error ( err ) )
return
}
2020-05-10 22:00:19 +00:00
chloop :
for {
2022-07-22 16:09:29 +00:00
var resp = neorpc . Notification {
JSONRPC : neorpc . JSONRPCVersion ,
2020-05-10 22:00:19 +00:00
Payload : make ( [ ] interface { } , 1 ) ,
}
var msg * websocket . PreparedMessage
select {
case <- s . shutdown :
break chloop
case b := <- s . blockCh :
2022-07-22 16:09:29 +00:00
resp . Event = neorpc . BlockEventID
2020-05-10 22:00:19 +00:00
resp . Payload [ 0 ] = b
case execution := <- s . executionCh :
2022-07-22 16:09:29 +00:00
resp . Event = neorpc . ExecutionEventID
2020-09-03 16:58:50 +00:00
resp . Payload [ 0 ] = execution
2020-05-10 22:00:19 +00:00
case notification := <- s . notificationCh :
2022-07-22 16:09:29 +00:00
resp . Event = neorpc . NotificationEventID
2020-10-19 10:44:20 +00:00
resp . Payload [ 0 ] = notification
2020-05-10 22:00:19 +00:00
case tx := <- s . transactionCh :
2022-07-22 16:09:29 +00:00
resp . Event = neorpc . TransactionEventID
2020-05-10 22:00:19 +00:00
resp . Payload [ 0 ] = tx
2021-05-28 11:55:06 +00:00
case e := <- s . notaryRequestCh :
2022-07-22 16:09:29 +00:00
resp . Event = neorpc . NotaryRequestEventID
2022-07-22 18:26:29 +00:00
resp . Payload [ 0 ] = & result . NotaryRequestEvent {
2021-05-28 11:55:06 +00:00
Type : e . Type ,
NotaryRequest : e . Data . ( * payload . P2PNotaryRequest ) ,
}
2020-05-10 22:00:19 +00:00
}
s . subsLock . RLock ( )
subloop :
for sub := range s . subscribers {
2020-05-12 19:38:29 +00:00
if sub . overflown . Load ( ) {
continue
}
2020-05-13 14:13:33 +00:00
for i := range sub . feeds {
2022-10-17 10:31:24 +00:00
if rpcevent . Matches ( sub . feeds [ i ] , & resp ) {
2020-05-10 22:00:19 +00:00
if msg == nil {
2020-05-12 19:38:29 +00:00
b , err = json . Marshal ( resp )
2020-05-10 22:00:19 +00:00
if err != nil {
s . log . Error ( "failed to marshal notification" ,
zap . Error ( err ) ,
2022-12-13 09:44:54 +00:00
zap . Stringer ( "type" , resp . Event ) )
2020-05-10 22:00:19 +00:00
break subloop
}
msg , err = websocket . NewPreparedMessage ( websocket . TextMessage , b )
if err != nil {
s . log . Error ( "failed to prepare notification message" ,
zap . Error ( err ) ,
2022-12-13 09:44:54 +00:00
zap . Stringer ( "type" , resp . Event ) )
2020-05-10 22:00:19 +00:00
break subloop
}
}
2020-05-12 19:38:29 +00:00
select {
case sub . writer <- msg :
default :
sub . overflown . Store ( true )
// MissedEvent is to be delivered eventually.
go func ( sub * subscriber ) {
sub . writer <- overflowMsg
sub . overflown . Store ( false )
} ( sub )
}
2020-05-10 22:00:19 +00:00
// The message is sent only once per subscriber.
break
}
}
}
s . subsLock . RUnlock ( )
}
2022-11-18 07:40:28 +00:00
// It's important to do it with subsCounterLock held because no subscription routine
2020-05-10 22:00:19 +00:00
// should be running concurrently to this one. And even if one is to run
// after unlock, it'll see closed s.shutdown and won't subscribe.
2022-11-17 13:00:22 +00:00
s . subsCounterLock . Lock ( )
2020-05-10 22:00:19 +00:00
// There might be no subscription in reality, but it's not a problem as
// core.Blockchain allows unsubscribing non-subscribed channels.
s . chain . UnsubscribeFromBlocks ( s . blockCh )
s . chain . UnsubscribeFromTransactions ( s . transactionCh )
s . chain . UnsubscribeFromNotifications ( s . notificationCh )
s . chain . UnsubscribeFromExecutions ( s . executionCh )
2021-05-28 11:55:06 +00:00
if s . chain . P2PSigExtensionsEnabled ( ) {
s . coreServer . UnsubscribeFromNotaryRequests ( s . notaryRequestCh )
}
2022-11-17 13:00:22 +00:00
s . subsCounterLock . Unlock ( )
2020-05-10 22:00:19 +00:00
drainloop :
for {
select {
case <- s . blockCh :
case <- s . executionCh :
case <- s . notificationCh :
case <- s . transactionCh :
2021-05-28 11:55:06 +00:00
case <- s . notaryRequestCh :
2020-05-10 22:00:19 +00:00
default :
break drainloop
}
}
// It's not required closing these, but since they're drained already
// this is safe and it also allows to give a signal to Shutdown routine.
close ( s . blockCh )
close ( s . transactionCh )
close ( s . notificationCh )
close ( s . executionCh )
2021-05-28 11:55:06 +00:00
close ( s . notaryRequestCh )
2020-05-10 22:00:19 +00:00
}
2022-11-18 20:19:50 +00:00
func ( s * Server ) blockHeightFromParam ( param * params . Param ) ( uint32 , * neorpc . Error ) {
2019-11-26 10:13:17 +00:00
num , err := param . GetInt ( )
if err != nil {
2022-07-22 16:09:29 +00:00
return 0 , neorpc . ErrInvalidParams
2019-11-26 10:13:17 +00:00
}
2022-11-18 20:19:50 +00:00
if num < 0 || int64 ( num ) > int64 ( s . chain . BlockHeight ( ) ) {
2019-11-26 10:13:17 +00:00
return 0 , invalidBlockHeightError ( 0 , num )
}
2022-11-18 20:19:50 +00:00
return uint32 ( num ) , nil
2018-03-23 20:36:59 +00:00
}
2020-01-13 08:27:22 +00:00
2022-07-22 16:09:29 +00:00
func ( s * Server ) packResponse ( r * params . In , result interface { } , respErr * neorpc . Error ) abstract {
2022-06-09 15:19:01 +00:00
resp := abstract {
2022-07-22 16:09:29 +00:00
Header : neorpc . Header {
2022-06-08 15:14:00 +00:00
JSONRPC : r . JSONRPC ,
ID : r . RawID ,
2020-01-14 12:02:38 +00:00
} ,
}
2020-04-28 19:56:19 +00:00
if respErr != nil {
2022-06-10 13:26:33 +00:00
resp . Error = respErr
2020-04-28 19:56:19 +00:00
} else {
2020-08-31 15:27:32 +00:00
resp . Result = result
2020-04-28 19:56:19 +00:00
}
return resp
}
2020-01-14 12:02:38 +00:00
2020-04-28 19:56:19 +00:00
// logRequestError is a request error logger.
2022-07-22 16:09:29 +00:00
func ( s * Server ) logRequestError ( r * params . Request , jsonErr * neorpc . Error ) {
2020-01-14 12:02:38 +00:00
logFields := [ ] zap . Field {
2022-06-23 14:41:28 +00:00
zap . Int64 ( "code" , jsonErr . Code ) ,
}
if len ( jsonErr . Data ) != 0 {
logFields = append ( logFields , zap . String ( "cause" , jsonErr . Data ) )
2020-01-14 12:02:38 +00:00
}
2020-10-26 17:22:20 +00:00
if r . In != nil {
logFields = append ( logFields , zap . String ( "method" , r . In . Method ) )
2022-07-07 14:41:01 +00:00
params := params . Params ( r . In . RawParams )
2021-10-28 11:10:18 +00:00
logFields = append ( logFields , zap . Any ( "params" , params ) )
2020-01-14 12:02:38 +00:00
}
2022-05-16 09:48:08 +00:00
logText := "Error encountered with rpc request"
switch jsonErr . Code {
2022-07-22 16:09:29 +00:00
case neorpc . InternalServerErrorCode :
2022-05-16 09:48:08 +00:00
s . log . Error ( logText , logFields ... )
default :
s . log . Info ( logText , logFields ... )
}
2020-01-14 12:02:38 +00:00
}
2020-04-28 19:56:19 +00:00
// writeHTTPErrorResponse writes an error response to the ResponseWriter.
2022-07-22 16:09:29 +00:00
func ( s * Server ) writeHTTPErrorResponse ( r * params . In , w http . ResponseWriter , jsonErr * neorpc . Error ) {
2020-08-31 15:27:32 +00:00
resp := s . packResponse ( r , nil , jsonErr )
2022-07-07 14:41:01 +00:00
s . writeHTTPServerResponse ( & params . Request { In : r } , w , resp )
2020-01-14 12:02:38 +00:00
}
2022-10-04 18:50:46 +00:00
func setCORSOriginHeaders ( h http . Header ) {
h . Set ( "Access-Control-Allow-Origin" , "*" )
h . Set ( "Access-Control-Allow-Headers" , "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With" )
}
2022-07-07 14:41:01 +00:00
func ( s * Server ) writeHTTPServerResponse ( r * params . Request , w http . ResponseWriter , resp abstractResult ) {
2020-04-28 19:56:19 +00:00
// Errors can happen in many places and we can only catch ALL of them here.
2022-07-22 16:09:29 +00:00
resp . RunForErrors ( func ( jsonErr * neorpc . Error ) {
2020-10-26 17:22:20 +00:00
s . logRequestError ( r , jsonErr )
} )
if r . In != nil {
2022-06-09 15:19:01 +00:00
resp := resp . ( abstract )
2020-10-26 17:22:20 +00:00
if resp . Error != nil {
2022-06-10 13:26:33 +00:00
w . WriteHeader ( getHTTPCodeForError ( resp . Error ) )
2020-10-26 17:22:20 +00:00
}
2020-04-28 19:56:19 +00:00
}
2020-01-14 12:02:38 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json; charset=utf-8" )
if s . config . EnableCORSWorkaround {
2022-10-04 18:50:46 +00:00
setCORSOriginHeaders ( w . Header ( ) )
2020-01-14 12:02:38 +00:00
}
encoder := json . NewEncoder ( w )
err := encoder . Encode ( resp )
if err != nil {
2020-10-26 17:22:20 +00:00
switch {
case r . In != nil :
s . log . Error ( "Error encountered while encoding response" ,
zap . String ( "err" , err . Error ( ) ) ,
zap . String ( "method" , r . In . Method ) )
case r . Batch != nil :
s . log . Error ( "Error encountered while encoding batch response" ,
zap . String ( "err" , err . Error ( ) ) )
}
2020-01-14 12:02:38 +00:00
}
}
2022-12-07 13:51:03 +00:00
// validateAddress verifies that the address is a correct Neo address
2020-01-13 08:27:22 +00:00
// see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html
2021-10-28 11:10:18 +00:00
func validateAddress ( addr interface { } ) bool {
2020-01-13 08:27:22 +00:00
if addr , ok := addr . ( string ) ; ok {
_ , err := address . StringToUint160 ( addr )
2021-10-28 11:10:18 +00:00
return err == nil
2020-01-13 08:27:22 +00:00
}
2021-10-28 11:10:18 +00:00
return false
2020-01-13 08:27:22 +00:00
}
2022-03-21 20:36:19 +00:00
func escapeForLog ( in string ) string {
return strings . Map ( func ( c rune ) rune {
if ! strconv . IsGraphic ( c ) {
return - 1
}
return c
} , in )
}
2022-11-25 10:20:53 +00:00
// Addresses returns the list of addresses RPC server is listening to in the form of
// address:port.
func ( s * Server ) Addresses ( ) [ ] string {
res := make ( [ ] string , len ( s . http ) )
for i , srv := range s . http {
res [ i ] = srv . Addr
}
return res
}