2020-02-17 12:17:02 +00:00
package server
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
2020-04-29 12:25:58 +00:00
"github.com/gorilla/websocket"
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"
2020-04-29 12:25:58 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
2021-03-24 17:32:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/fee"
2021-11-17 20:04:50 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
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"
2021-11-17 20:04:50 +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"
"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"
2020-04-29 12:25:58 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpc"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
2021-09-24 09:15:25 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
2021-02-07 19:01:22 +00:00
"github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
2020-12-29 10:45:49 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
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"
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 (
// Server represents the JSON-RPC 2.0 server.
Server struct {
* http . Server
2020-11-17 12:57:50 +00:00
chain blockchainer . Blockchainer
config rpc . Config
network netmode . Magic
stateRootEnabled bool
coreServer * network . Server
2020-09-28 11:58:04 +00:00
oracle * oracle . Oracle
2020-11-17 12:57:50 +00:00
log * zap . Logger
https * http . Server
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
2021-05-28 11:55:06 +00:00
subsLock sync . RWMutex
subscribers map [ * subscriber ] bool
blockSubs int
executionSubs int
notificationSubs int
transactionSubs int
notaryRequestSubs int
blockCh chan * block . Block
executionCh chan * state . AppExecResult
2021-09-24 09:15:25 +00:00
notificationCh chan * subscriptions . NotificationEvent
2021-05-28 11:55:06 +00:00
transactionCh chan * transaction . Transaction
notaryRequestCh chan mempoolevent . Event
2018-03-23 20:36:59 +00:00
}
)
2020-04-29 12:25:58 +00:00
const (
// Message limit for receiving side.
wsReadLimit = 4096
// 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
// Maximum number of subscribers per Server. Each websocket client is
// treated like subscriber, so technically it's a limit on websocket
// connections.
maxSubscribers = 64
2020-09-14 14:48:17 +00:00
// Maximum number of elements for get*transfers requests.
maxTransfersLimit = 1000
2020-04-29 12:25:58 +00:00
)
2020-04-28 19:35:19 +00:00
var rpcHandlers = map [ string ] func ( * Server , request . Params ) ( interface { } , * response . Error ) {
2021-03-24 17:32:48 +00:00
"calculatenetworkfee" : ( * Server ) . calculateNetworkFee ,
2021-10-07 13:56:27 +00:00
"findstates" : ( * Server ) . findStates ,
2020-10-01 12:26:54 +00:00
"getapplicationlog" : ( * Server ) . getApplicationLog ,
"getbestblockhash" : ( * Server ) . getBestBlockHash ,
"getblock" : ( * Server ) . getBlock ,
"getblockcount" : ( * Server ) . getBlockCount ,
"getblockhash" : ( * Server ) . getBlockHash ,
"getblockheader" : ( * Server ) . getBlockHeader ,
2021-02-07 19:01:22 +00:00
"getblockheadercount" : ( * Server ) . getBlockHeaderCount ,
2020-10-01 12:26:54 +00:00
"getblocksysfee" : ( * Server ) . getBlockSysFee ,
"getcommittee" : ( * Server ) . getCommittee ,
"getconnectioncount" : ( * Server ) . getConnectionCount ,
"getcontractstate" : ( * Server ) . getContractState ,
2021-02-09 09:25:38 +00:00
"getnativecontracts" : ( * Server ) . getNativeContracts ,
2021-11-17 20:04:50 +00:00
"getnep11balances" : ( * Server ) . getNEP11Balances ,
"getnep11properties" : ( * Server ) . getNEP11Properties ,
"getnep11transfers" : ( * Server ) . getNEP11Transfers ,
2020-11-24 08:14:25 +00:00
"getnep17balances" : ( * Server ) . getNEP17Balances ,
"getnep17transfers" : ( * Server ) . getNEP17Transfers ,
2020-10-01 12:26:54 +00:00
"getpeers" : ( * Server ) . getPeers ,
2020-06-04 08:59:22 +00:00
"getproof" : ( * Server ) . getProof ,
2020-10-01 12:26:54 +00:00
"getrawmempool" : ( * Server ) . getRawMempool ,
"getrawtransaction" : ( * Server ) . getrawtransaction ,
2021-10-07 09:03:37 +00:00
"getstate" : ( * Server ) . getState ,
2020-06-04 08:09:07 +00:00
"getstateheight" : ( * Server ) . getStateHeight ,
2020-06-03 15:09:36 +00:00
"getstateroot" : ( * Server ) . getStateRoot ,
2020-10-01 12:26:54 +00:00
"getstorage" : ( * Server ) . getStorage ,
"gettransactionheight" : ( * Server ) . getTransactionHeight ,
"getunclaimedgas" : ( * Server ) . getUnclaimedGas ,
"getnextblockvalidators" : ( * Server ) . getNextBlockValidators ,
"getversion" : ( * Server ) . getVersion ,
"invokefunction" : ( * Server ) . invokeFunction ,
"invokescript" : ( * Server ) . invokescript ,
2020-12-14 12:23:39 +00:00
"invokecontractverify" : ( * Server ) . invokeContractVerify ,
2020-10-01 12:26:54 +00:00
"sendrawtransaction" : ( * Server ) . sendrawtransaction ,
"submitblock" : ( * Server ) . submitBlock ,
2021-02-05 07:26:23 +00:00
"submitnotaryrequest" : ( * Server ) . submitNotaryRequest ,
2020-09-28 11:58:04 +00:00
"submitoracleresponse" : ( * Server ) . submitOracleResponse ,
2020-10-01 12:26:54 +00:00
"validateaddress" : ( * Server ) . validateAddress ,
2020-06-05 08:51:39 +00:00
"verifyproof" : ( * Server ) . verifyProof ,
2020-03-13 07:29:49 +00:00
}
2020-05-10 22:00:19 +00:00
var rpcWsHandlers = map [ string ] func ( * Server , request . Params , * subscriber ) ( interface { } , * response . Error ) {
"subscribe" : ( * Server ) . subscribe ,
"unsubscribe" : ( * Server ) . unsubscribe ,
}
2020-04-28 19:35:19 +00:00
var invalidBlockHeightError = func ( index int , height int ) * response . Error {
return response . NewRPCError ( 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 ) , "" , nil )
2019-11-21 13:42:51 +00:00
}
2018-03-25 10:13:47 +00:00
2020-04-29 12:25:58 +00:00
// upgrader is a no-op websocket.Upgrader that reuses HTTP server buffers and
// doesn't set any Error function.
var upgrader = websocket . Upgrader { }
2020-02-17 12:17:02 +00:00
// New creates a new Server struct.
2020-09-28 11:58:04 +00:00
func New ( chain blockchainer . Blockchainer , conf rpc . Config , coreServer * network . Server ,
2022-04-22 07:49:06 +00:00
orc * oracle . Oracle , log * zap . Logger , errChan chan error ) Server {
2019-11-01 10:23:46 +00:00
httpServer := & http . Server {
Addr : conf . Address + ":" + strconv . FormatUint ( uint64 ( conf . Port ) , 10 ) ,
}
2020-03-10 11:56:18 +00:00
var tlsServer * http . Server
if cfg := conf . TLSConfig ; cfg . Enabled {
tlsServer = & http . Server {
Addr : net . JoinHostPort ( cfg . Address , strconv . FormatUint ( uint64 ( cfg . Port ) , 10 ) ) ,
}
}
2020-09-28 11:58:04 +00:00
if orc != nil {
orc . SetBroadcaster ( broadcaster . New ( orc . MainCfg , log ) )
}
2018-03-23 20:36:59 +00:00
return Server {
2020-11-17 12:57:50 +00:00
Server : httpServer ,
chain : chain ,
config : conf ,
network : chain . GetConfig ( ) . Magic ,
stateRootEnabled : chain . GetConfig ( ) . StateRootInHeader ,
coreServer : coreServer ,
log : log ,
2020-09-28 11:58:04 +00:00
oracle : orc ,
2020-11-17 12:57:50 +00:00
https : tlsServer ,
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
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 ) ,
2021-09-24 09:15:25 +00:00
notificationCh : make ( chan * subscriptions . NotificationEvent ) ,
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 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().
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
}
2020-04-28 13:56:33 +00:00
s . Handler = http . HandlerFunc ( s . handleHTTPRequest )
2019-12-30 08:44:52 +00:00
s . log . Info ( "starting rpc-server" , zap . String ( "endpoint" , s . Addr ) )
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 {
2020-04-28 13:56:33 +00:00
s . https . Handler = http . HandlerFunc ( s . handleHTTPRequest )
2020-03-10 11:56:18 +00:00
s . log . Info ( "starting rpc-server (https)" , zap . String ( "endpoint" , s . https . Addr ) )
go func ( ) {
2020-08-31 12:35:55 +00:00
ln , err := net . Listen ( "tcp" , s . https . Addr )
if err != nil {
2022-04-22 07:49:06 +00:00
s . errChan <- err
2020-08-31 12:35:55 +00:00
return
}
s . https . Addr = ln . Addr ( ) . String ( )
err = s . https . ServeTLS ( ln , cfg . CertFile , cfg . KeyFile )
2020-06-25 16:31:45 +00:00
if err != http . ErrServerClosed {
2020-03-10 11:56:18 +00:00
s . log . Error ( "failed to start TLS RPC server" , zap . Error ( err ) )
2022-04-22 07:49:06 +00:00
s . errChan <- err
2020-03-10 11:56:18 +00:00
}
} ( )
}
2020-08-31 12:35:55 +00:00
ln , err := net . Listen ( "tcp" , s . Addr )
if err != nil {
2022-04-22 07:49:06 +00:00
s . errChan <- err
2020-08-31 12:35:55 +00:00
return
2020-03-10 11:56:18 +00:00
}
2020-08-31 12:35:55 +00:00
s . Addr = ln . Addr ( ) . String ( ) // set Addr to the actual address
go func ( ) {
err = s . Serve ( ln )
if err != http . ErrServerClosed {
s . log . Error ( "failed to start RPC server" , zap . Error ( err ) )
2022-04-22 07:49:06 +00:00
s . errChan <- err
2020-08-31 12:35:55 +00:00
}
} ( )
2018-03-23 20:36:59 +00:00
}
2022-04-22 07:49:06 +00:00
// Shutdown stops the RPC server. It can only be called once.
func ( s * Server ) Shutdown ( ) {
2020-03-10 11:56:18 +00:00
var httpsErr error
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-04-22 07:49:06 +00:00
s . log . Info ( "shutting down RPC server (https)" , zap . String ( "endpoint" , s . https . Addr ) )
2020-03-10 11:56:18 +00:00
httpsErr = s . https . Shutdown ( context . Background ( ) )
}
2022-04-22 07:49:06 +00:00
s . log . Info ( "shutting down RPC server" , zap . String ( "endpoint" , s . Addr ) )
2020-03-10 11:56:18 +00:00
err := s . Server . Shutdown ( context . Background ( ) )
2020-05-10 22:00:19 +00:00
// Wait for handleSubEvents to finish.
<- s . executionCh
2022-04-22 07:49:06 +00:00
if httpsErr != nil {
s . errChan <- httpsErr
}
if err != nil {
s . errChan <- err
2020-03-10 11:56:18 +00:00
}
2018-03-23 20:36:59 +00:00
}
2020-04-28 13:56:33 +00:00
func ( s * Server ) handleHTTPRequest ( w http . ResponseWriter , httpRequest * http . Request ) {
2020-10-26 17:22:20 +00:00
req := request . 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 ( )
if numOfSubs >= maxSubscribers {
s . writeHTTPErrorResponse (
2020-10-26 17:22:20 +00:00
request . NewIn ( ) ,
2020-05-10 22:00:19 +00:00
w ,
response . NewInternalServerError ( "websocket users limit reached" , nil ) ,
)
return
}
2020-04-29 12:25:58 +00:00
ws , err := upgrader . Upgrade ( w , httpRequest , nil )
if err != nil {
s . log . Info ( "websocket connection upgrade failed" , zap . Error ( err ) )
return
}
2020-10-26 17:22:20 +00:00
resChan := make ( chan response . 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
}
2018-03-23 20:36:59 +00:00
if httpRequest . Method != "POST" {
2020-04-28 19:56:19 +00:00
s . writeHTTPErrorResponse (
2020-10-26 17:22:20 +00:00
request . NewIn ( ) ,
2018-03-23 20:36:59 +00:00
w ,
2020-01-14 12:02:38 +00:00
response . NewInvalidParamsError (
2018-03-23 20:36:59 +00:00
fmt . Sprintf ( "Invalid method '%s', please retry with 'POST'" , httpRequest . Method ) , nil ,
) ,
)
return
}
err := req . DecodeData ( httpRequest . Body )
if err != nil {
2020-10-26 17:22:20 +00:00
s . writeHTTPErrorResponse ( request . NewIn ( ) , w , response . NewParseError ( "Problem parsing JSON-RPC request body" , err ) )
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
}
2020-10-26 17:22:20 +00:00
func ( s * Server ) handleRequest ( req * request . Request , sub * subscriber ) response . AbstractResult {
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 )
}
resp := make ( response . AbstractBatch , len ( req . Batch ) )
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
}
func ( s * Server ) handleIn ( req * request . In , sub * subscriber ) response . Abstract {
2020-05-10 22:00:19 +00:00
var res interface { }
var resErr * response . Error
2020-10-26 17:22:20 +00:00
if req . JSONRPC != request . JSONRPCVersion {
return s . packResponse ( req , nil , response . NewInvalidParamsError ( "Problem parsing JSON" , fmt . Errorf ( "invalid version, expected 2.0 got: '%s'" , req . JSONRPC ) ) )
}
2020-05-10 22:00:19 +00:00
2021-10-28 11:10:18 +00:00
reqParams := request . 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
2020-03-12 17:36:36 +00:00
incCounter ( req . Method )
2022-03-21 20:36:19 +00:00
resErr = response . NewMethodNotFoundError ( fmt . Sprintf ( "Method %q not supported" , req . Method ) , nil )
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
}
2020-10-26 17:22:20 +00:00
func ( s * Server ) handleWsWrites ( ws * websocket . Conn , resChan <- chan response . 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
}
2020-10-26 17:22:20 +00:00
func ( s * Server ) handleWsReads ( ws * websocket . Conn , resChan chan <- response . AbstractResult , subscr * subscriber ) {
2020-04-29 12:25:58 +00:00
ws . SetReadLimit ( 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 {
2020-10-26 17:22:20 +00:00
req := request . 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 )
2020-10-26 17:22:20 +00:00
res . RunForErrors ( func ( jsonErr * response . Error ) {
s . logRequestError ( req , jsonErr )
} )
2020-05-10 22:00:19 +00:00
select {
case <- s . shutdown :
break requestloop
case resChan <- res :
}
}
s . subsLock . Lock ( )
delete ( s . subscribers , subscr )
for _ , e := range subscr . feeds {
2020-05-13 14:13:33 +00:00
if e . event != response . InvalidEventID {
s . unsubscribeFromChannel ( e . event )
2020-05-10 22:00:19 +00:00
}
2020-04-29 12:25:58 +00:00
}
2020-05-10 22:00:19 +00:00
s . subsLock . Unlock ( )
2020-04-29 12:25:58 +00:00
close ( resChan )
ws . Close ( )
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBestBlockHash ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:48 +00:00
return "0x" + s . chain . CurrentBlockHash ( ) . StringLE ( ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockCount ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:48 +00:00
return s . chain . BlockHeight ( ) + 1 , nil
}
2021-02-07 19:01:22 +00:00
func ( s * Server ) getBlockHeaderCount ( _ request . Params ) ( interface { } , * response . Error ) {
return s . chain . HeaderHeight ( ) + 1 , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getConnectionCount ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:48 +00:00
return s . coreServer . PeerCount ( ) , nil
}
2020-06-05 13:02:55 +00:00
func ( s * Server ) blockHashFromParam ( param * request . Param ) ( util . Uint256 , * response . 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 {
return hash , response . ErrInvalidParams
}
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
}
func ( s * Server ) getBlock ( reqParams request . Params ) ( interface { } , * response . 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 {
return nil , response . NewInternalServerError ( fmt . Sprintf ( "Problem locating block with hash: %s" , hash ) , err )
}
2021-10-28 11:10:18 +00:00
if v , _ := reqParams . Value ( 1 ) . GetBoolean ( ) ; v {
2020-03-13 07:08:30 +00:00
return result . NewBlock ( block , s . chain ) , nil
}
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
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockHash ( reqParams request . Params ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
return s . chain . GetHeaderHash ( num ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getVersion ( _ request . Params ) ( interface { } , * response . Error ) {
2020-06-10 07:01:21 +00:00
port , err := s . coreServer . Port ( )
if err != nil {
return nil , response . NewInternalServerError ( "Cannot fetch tcp port" , err )
}
2021-09-07 12:42:04 +00:00
cfg := s . chain . GetConfig ( )
2020-03-13 07:04:57 +00:00
return result . Version {
2021-03-26 15:38:46 +00:00
Magic : s . network ,
TCPPort : port ,
Nonce : s . coreServer . ID ( ) ,
UserAgent : s . coreServer . UserAgent ,
2021-09-07 12:42:04 +00:00
StateRootInHeader : cfg . StateRootInHeader ,
Protocol : result . Protocol {
AddressVersion : address . NEO3Prefix ,
Network : cfg . Magic ,
MillisecondsPerBlock : cfg . SecondsPerBlock * 1000 ,
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 ( ) ) ) ,
rpc: adjust `getversion` RPC response
Reference implementation includes `initialgasdistribution` as integer
value with decimals.
C# response:
```
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"nonce": 1677922561,
"protocol": {
"addressversion": 53,
"initialgasdistribution": 5200000000000000,
"maxtraceableblocks": 2102400,
"maxtransactionsperblock": 512,
"maxvaliduntilblockincrement": 5760,
"memorypoolmaxtransactions": 50000,
"msperblock": 15000,
"network": 860833102,
"validatorscount": 7
},
"tcpport": 10333,
"useragent": "/Neo:3.1.0/",
"wsport": 10334
}
}
```
Neo-Go response:
```
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"network": 860833102,
"nonce": 2847278838,
"protocol": {
"addressversion": 53,
"initialgasdistribution": "52000000",
"maxtraceableblocks": 2102400,
"maxtransactionsperblock": 512,
"maxvaliduntilblockincrement": 5760,
"memorypoolmaxtransactions": 50000,
"msperblock": 15000,
"network": 860833102,
"validatorscount": 7
},
"tcpport": 10333,
"useragent": "/NEO-GO:0.98.2/"
}
}
```
2022-04-14 10:51:30 +00:00
InitialGasDistribution : int64 ( cfg . InitialGASSupply ) ,
2021-09-07 12:42:04 +00:00
StateRootInHeader : cfg . StateRootInHeader ,
} ,
2020-03-13 07:04:57 +00:00
} , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getPeers ( _ request . Params ) ( interface { } , * response . 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
}
2020-07-27 14:27:21 +00:00
func ( s * Server ) getRawMempool ( reqParams request . Params ) ( interface { } , * response . 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
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) validateAddress ( reqParams request . Params ) ( interface { } , * response . Error ) {
2021-10-28 11:10:18 +00:00
param , err := reqParams . Value ( 0 ) . GetString ( )
if err != nil {
2020-03-13 07:01:49 +00:00
return nil , response . ErrInvalidParams
}
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.
func ( s * Server ) calculateNetworkFee ( reqParams request . Params ) ( interface { } , * response . Error ) {
if len ( reqParams ) < 1 {
return 0 , response . ErrInvalidParams
}
byteTx , err := reqParams [ 0 ] . GetBytesBase64 ( )
if err != nil {
return 0 , response . WrapErrorWithData ( response . ErrInvalidParams , err )
}
tx , err := transaction . NewTransactionFromBytes ( byteTx )
if err != nil {
return 0 , response . WrapErrorWithData ( response . ErrInvalidParams , err )
}
hashablePart , err := tx . EncodeHashableFields ( )
if err != nil {
return 0 , response . WrapErrorWithData ( response . ErrInvalidParams , fmt . Errorf ( "failed to compute tx size: %w" , err ) )
}
size := len ( hashablePart ) + io . GetVarSize ( len ( tx . Signers ) )
var (
ef int64
netFee int64
)
for i , signer := range tx . Signers {
var verificationScript [ ] byte
for _ , w := range tx . Scripts {
if w . VerificationScript != nil && hash . Hash160 ( w . VerificationScript ) . Equals ( signer . Account ) {
// then it's a standard sig/multisig witness
verificationScript = w . VerificationScript
break
}
}
if verificationScript == nil { // then it still might be a contract-based verification
2021-10-25 14:42:20 +00:00
gasConsumed , err := s . chain . VerifyWitness ( signer . Account , tx , & tx . Scripts [ i ] , int64 ( s . config . MaxGasInvoke ) )
2021-03-24 17:32:48 +00:00
if err != nil {
2021-10-25 14:42:20 +00:00
return 0 , response . NewRPCError ( fmt . Sprintf ( "contract verification for signer #%d failed" , i ) , err . Error ( ) , err )
2021-03-24 17:32:48 +00:00
}
2021-10-25 14:42:20 +00:00
netFee += gasConsumed
2021-03-26 15:52:51 +00:00
size += io . GetVarSize ( [ ] byte { } ) + // verification script is empty (contract-based witness)
io . GetVarSize ( tx . Scripts [ i ] . InvocationScript ) // invocation script might not be empty (args for `verify`)
2021-03-24 17:32:48 +00:00
continue
}
if ef == 0 {
2022-01-11 21:58:03 +00:00
ef = s . chain . GetBaseExecFee ( )
2021-03-24 17:32:48 +00:00
}
fee , sizeDelta := fee . Calculate ( ef , verificationScript )
netFee += fee
size += sizeDelta
}
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.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getApplicationLog ( reqParams request . Params ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
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 {
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
return nil , response . ErrInvalidParams
}
2021-10-28 11:10:18 +00:00
trig , err = trigger . FromString ( trigString )
2020-11-10 11:30:00 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
}
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 {
2020-09-21 11:08:15 +00:00
return nil , response . NewRPCError ( "Unknown transaction or block" , "" , 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
}
2021-11-17 20:04:50 +00:00
func ( s * Server ) getNEP11Tokens ( h util . Uint160 , acc util . Uint160 , bw * io . BufBinWriter ) ( [ ] stackitem . Item , error ) {
item , finalize , err := s . invokeReadOnly ( bw , h , "tokensOf" , acc )
if err != nil {
return nil , err
}
defer finalize ( )
if ( item . Type ( ) == stackitem . InteropT ) && iterator . IsIterator ( item ) {
vals , _ := iterator . Values ( item , s . config . MaxNEP11Tokens )
return vals , nil
}
return nil , fmt . Errorf ( "invalid `tokensOf` result type %s" , item . String ( ) )
}
func ( s * Server ) getNEP11Balances ( ps request . Params ) ( interface { } , * response . Error ) {
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
bs := & result . NEP11Balances {
Address : address . Uint160ToString ( u ) ,
Balances : [ ] result . NEP11AssetBalance { } ,
}
lastUpdated , err := s . chain . GetTokenLastUpdated ( u )
if err != nil {
2021-11-18 13:37:42 +00:00
return nil , response . NewRPCError ( "Failed to get NEP-11 last updated block" , err . Error ( ) , err )
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 ( ) {
toks , err := s . getNEP11Tokens ( h , u , bw )
if err != nil {
continue
}
if len ( toks ) == 0 {
continue
}
cs := s . chain . GetContractState ( h )
if cs == nil {
continue
}
isDivisible := ( cs . Manifest . ABI . GetMethod ( "balanceOf" , 2 ) != nil )
lub , ok := lastUpdated [ cs . ID ]
if ! ok {
cfg := s . chain . GetConfig ( )
if ! cfg . P2PStateExchangeExtensions && cfg . RemoveUntraceableBlocks {
return nil , response . NewInternalServerError ( fmt . Sprintf ( "failed to get LastUpdatedBlock for balance of %s token" , cs . Hash . StringLE ( ) ) , nil )
}
lub = stateSyncPoint
}
bs . Balances = append ( bs . Balances , result . NEP11AssetBalance {
Asset : h ,
Tokens : make ( [ ] result . NEP11TokenBalance , 0 , len ( toks ) ) ,
} )
curAsset := & bs . Balances [ len ( bs . Balances ) - 1 ]
for i := range toks {
id , err := toks [ i ] . TryBytes ( )
if err != nil || len ( id ) > storage . MaxStorageKeyLen {
continue
}
var amount = "1"
if isDivisible {
balance , err := s . getTokenBalance ( h , u , id , bw )
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
}
func ( s * Server ) getNEP11Properties ( ps request . Params ) ( interface { } , * response . Error ) {
asset , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
token , err := ps . Value ( 1 ) . GetBytesHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
props , err := s . invokeNEP11Properties ( asset , token , nil )
if err != nil {
return nil , response . NewRPCError ( "failed to get NEP-11 properties" , err . Error ( ) , err )
}
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
}
2020-11-24 08:14:25 +00:00
func ( s * Server ) getNEP17Balances ( ps request . Params ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
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 {
2021-11-18 13:37:42 +00:00
return nil , response . NewRPCError ( "Failed to get NEP-17 last updated block" , err . Error ( ) , err )
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 ( ) {
2021-11-17 20:04:50 +00:00
balance , err := s . getTokenBalance ( h , u , nil , 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 ( )
if ! cfg . P2PStateExchangeExtensions && cfg . RemoveUntraceableBlocks {
return nil , response . NewInternalServerError ( fmt . Sprintf ( "failed to get LastUpdatedBlock for balance of %s token" , cs . Hash . StringLE ( ) ) , nil )
}
lub = stateSyncPoint
}
2021-07-25 12:00:44 +00:00
bs . Balances = append ( bs . Balances , result . NEP17Balance {
Asset : h ,
Amount : balance . String ( ) ,
2021-08-11 11:29:03 +00:00
LastUpdated : lub ,
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 ) {
2021-07-25 12:00:44 +00:00
if bw == nil {
bw = io . NewBufBinWriter ( )
} else {
bw . Reset ( )
}
2021-11-17 20:04:50 +00:00
emit . AppCall ( bw . BinWriter , h , method , callflag . ReadStates | callflag . AllowCall , params ... )
2021-07-25 12:00:44 +00:00
if bw . Err != nil {
2021-11-17 20:04:50 +00:00
return nil , nil , fmt . Errorf ( "failed to create `%s` invocation script: %w" , method , bw . Err )
2021-07-25 12:00:44 +00:00
}
script := bw . Bytes ( )
tx := & transaction . Transaction { Script : script }
2021-11-17 20:04:50 +00:00
b , err := s . getFakeNextBlock ( )
if err != nil {
return nil , nil , err
}
2022-01-12 22:20:08 +00:00
ic := s . chain . GetTestVM ( trigger . Application , tx , b )
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 ( )
2021-11-17 20:04:50 +00:00
return nil , nil , fmt . Errorf ( "failed to run `%s` for %s: %w" , method , h . StringLE ( ) , err )
2021-07-25 12:00:44 +00:00
}
2022-01-12 22:20:08 +00:00
if ic . VM . Estack ( ) . Len ( ) != 1 {
ic . Finalize ( )
return nil , nil , fmt . Errorf ( "invalid `%s` return values count: expected 1, got %d" , method , ic . VM . Estack ( ) . Len ( ) )
2021-11-17 20:04:50 +00:00
}
2022-01-12 22:20:08 +00:00
return ic . VM . Estack ( ) . Pop ( ) . Item ( ) , ic . Finalize , nil
2021-11-17 20:04:50 +00:00
}
func ( s * Server ) getTokenBalance ( h util . Uint160 , acc util . Uint160 , id [ ] byte , bw * io . BufBinWriter ) ( * big . Int , error ) {
var (
item stackitem . Item
finalize func ( )
err error
)
if id == nil { // NEP-17 and NEP-11 generic.
item , finalize , err = s . invokeReadOnly ( bw , h , "balanceOf" , acc )
} else { // NEP-11 divisible.
item , finalize , err = s . invokeReadOnly ( bw , h , "balanceOf" , acc , id )
}
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
}
2020-09-12 21:12:45 +00:00
func getTimestampsAndLimit ( ps request . 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
}
2021-11-17 20:04:50 +00:00
func ( s * Server ) getNEP11Transfers ( ps request . Params ) ( interface { } , * response . Error ) {
return s . getTokenTransfers ( ps , true )
}
2020-11-24 08:14:25 +00:00
func ( s * Server ) getNEP17Transfers ( ps request . Params ) ( interface { } , * response . Error ) {
2021-11-17 20:04:50 +00:00
return s . getTokenTransfers ( ps , false )
}
func ( s * Server ) getTokenTransfers ( ps request . Params , isNEP11 bool ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
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 {
return nil , response . NewInvalidParamsError ( err . Error ( ) , err )
}
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
2021-11-17 20:04:50 +00:00
// handleTransfer returns items to be added into received and sent arrays
// along with a continue flag and error.
var handleTransfer = func ( tr * state . NEP17Transfer ) ( * result . NEP17Transfer , * result . NEP17Transfer , bool , error ) {
var received , sent * result . NEP17Transfer
2020-09-12 21:12:45 +00:00
// Iterating from newest to oldest, not yet reached required
// 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
}
2020-09-12 21:12:45 +00:00
// Iterating from newest to oldest, moved past required
// 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-01-18 15:28:24 +00:00
return nil , response . NewInternalServerError ( fmt . Sprintf ( "invalid transfer log: %v" , err ) , 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
}
2020-06-18 10:50:30 +00:00
func ( s * Server ) contractIDFromParam ( param * request . Param ) ( int32 , * response . Error ) {
var result int32
2020-06-04 11:58:47 +00:00
if param == nil {
return 0 , response . ErrInvalidParams
}
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 {
return 0 , response . ErrUnknown
}
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 {
return 0 , response . ErrInvalidParams
}
2021-04-19 07:48:35 +00:00
if err := checkInt32 ( id ) ; err != nil {
return 0 , response . WrapErrorWithData ( response . ErrInvalidParams , err )
}
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.
func ( s * Server ) contractScriptHashFromParam ( param * request . Param ) ( util . Uint160 , * response . Error ) {
var result util . Uint160
if param == nil {
return result , response . ErrInvalidParams
}
2021-10-29 14:06:45 +00:00
nameOrHashOrIndex , err := param . GetString ( )
if err != nil {
2020-09-25 09:40:57 +00:00
return result , response . ErrInvalidParams
}
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 {
return result , response . NewRPCError ( "Unknown contract" , "" , err )
}
if err := checkInt32 ( id ) ; err != nil {
return result , response . WrapErrorWithData ( response . ErrInvalidParams , err )
}
result , err = s . chain . GetContractScriptHash ( int32 ( id ) )
if err != nil {
return result , response . NewRPCError ( "Unknown contract" , "" , err )
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" )
func ( s * Server ) getProof ( ps request . Params ) ( interface { } , * response . Error ) {
if s . chain . GetConfig ( ) . KeepOnlyLatestState {
return nil , response . NewInvalidRequestError ( "'getproof' is not supported" , errKeepOnlyLatestState )
}
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
sc , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
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 {
return nil , response . ErrInvalidParams
}
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 {
return nil , response . NewInternalServerError ( "failed to get proof" , err )
}
2021-03-30 10:08:13 +00:00
return & result . ProofWithKey {
Key : skey ,
Proof : proof ,
2020-06-04 08:59:22 +00:00
} , nil
}
2020-06-05 08:51:39 +00:00
func ( s * Server ) verifyProof ( ps request . Params ) ( interface { } , * response . Error ) {
if s . chain . GetConfig ( ) . KeepOnlyLatestState {
return nil , response . NewInvalidRequestError ( "'verifyproof' is not supported" , errKeepOnlyLatestState )
}
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
proofStr , err := ps . Value ( 1 ) . GetString ( )
if err != nil {
return nil , response . ErrInvalidParams
}
var p result . ProofWithKey
if err := p . FromString ( proofStr ) ; err != nil {
return nil , response . ErrInvalidParams
}
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
}
2021-10-07 09:03:37 +00:00
func ( s * Server ) getState ( ps request . Params ) ( interface { } , * response . Error ) {
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , errors . New ( "invalid stateroot" ) )
}
if s . chain . GetConfig ( ) . KeepOnlyLatestState {
curr , err := s . chain . GetStateModule ( ) . GetStateRoot ( s . chain . BlockHeight ( ) )
if err != nil {
return nil , response . NewInternalServerError ( "failed to get current stateroot" , err )
}
if ! curr . Root . Equals ( root ) {
return nil , response . NewInvalidRequestError ( "'getstate' is not supported for old states" , errKeepOnlyLatestState )
}
}
csHash , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , errors . New ( "invalid contract hash" ) )
}
key , err := ps . Value ( 2 ) . GetBytesBase64 ( )
if err != nil {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , errors . New ( "invalid key" ) )
}
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 {
return nil , response . NewInternalServerError ( "failed to get historical item state" , err )
}
return res , nil
}
func ( s * Server ) findStates ( ps request . Params ) ( interface { } , * response . Error ) {
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , errors . New ( "invalid stateroot" ) )
}
if s . chain . GetConfig ( ) . KeepOnlyLatestState {
curr , err := s . chain . GetStateModule ( ) . GetStateRoot ( s . chain . BlockHeight ( ) )
if err != nil {
return nil , response . NewInternalServerError ( "failed to get current stateroot" , err )
}
if ! curr . Root . Equals ( root ) {
return nil , response . NewInvalidRequestError ( "'findstates' is not supported for old states" , errKeepOnlyLatestState )
}
}
csHash , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
2022-01-17 09:24:06 +00:00
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , fmt . Errorf ( "invalid contract hash: %w" , err ) )
2021-10-07 13:56:27 +00:00
}
prefix , err := ps . Value ( 2 ) . GetBytesBase64 ( )
if err != nil {
2022-01-17 09:24:06 +00:00
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , fmt . Errorf ( "invalid prefix: %w" , 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-01-17 09:24:06 +00:00
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , fmt . Errorf ( "invalid key: %w" , 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 ) {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , errors . New ( "key doesn't match prefix" ) )
}
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-01-17 09:24:06 +00:00
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , fmt . Errorf ( "invalid count: %w" , 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 {
return nil , response . NewInternalServerError ( "failed to find historical items" , err )
}
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 {
return nil , response . NewInternalServerError ( "failed to get first proof" , err )
}
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 {
return nil , response . NewInternalServerError ( "failed to get first proof" , err )
}
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
}
func ( s * Server ) getHistoricalContractState ( root util . Uint256 , csHash util . Uint160 ) ( * state . Contract , * response . 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 {
return nil , response . NewInternalServerError ( "failed to get historical contract state" , err )
}
contract := new ( state . Contract )
err = stackitem . DeserializeConvertible ( csBytes , contract )
if err != nil {
return nil , response . NewInternalServerError ( "failed to deserialize historical contract state" , err )
}
2021-10-07 13:56:27 +00:00
return contract , nil
2021-10-07 09:03:37 +00:00
}
2020-06-04 08:09:07 +00:00
func ( s * Server ) getStateHeight ( _ request . Params ) ( interface { } , * response . Error ) {
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
}
2020-06-03 15:09:36 +00:00
func ( s * Server ) getStateRoot ( ps request . Params ) ( interface { } , * response . Error ) {
p := ps . Value ( 0 )
if p == nil {
return nil , response . NewRPCError ( "Invalid parameter." , "" , nil )
}
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 {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , err )
}
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 {
return nil , response . NewRPCError ( "Unknown state root." , "" , err )
}
return rt , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getStorage ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
id , rErr := s . contractIDFromParam ( ps . Value ( 0 ) )
2020-06-18 10:50:30 +00:00
if rErr == response . ErrUnknown {
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 {
2020-01-14 12:02:38 +00:00
return nil , response . 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
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getrawtransaction ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-11-13 13:54:38 +00:00
txHash , err := reqParams . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
tx , height , err := s . chain . GetTransaction ( txHash )
if err != nil {
2020-08-06 14:44:08 +00:00
err = fmt . Errorf ( "invalid transaction %s: %w" , txHash , err )
2020-01-14 12:02:38 +00:00
return nil , response . NewRPCError ( "Unknown transaction" , err . Error ( ) , err )
2020-11-13 13:54:38 +00:00
}
2021-10-28 11:10:18 +00:00
if v , _ := reqParams . Value ( 1 ) . GetBoolean ( ) ; v {
2020-11-13 13:54:38 +00:00
if height == math . MaxUint32 {
return result . NewTransactionOutputRaw ( tx , nil , nil , s . chain ) , 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
_header := s . chain . GetHeaderHash ( int ( height ) )
header , err := s . chain . GetHeader ( _header )
if err != nil {
2020-11-13 13:54:38 +00:00
return nil , response . NewRPCError ( "Failed to get header for the transaction" , err . Error ( ) , 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
}
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 {
2020-11-13 13:54:38 +00:00
return nil , response . NewRPCError ( "Failed to get application log for the transaction" , err . Error ( ) , err )
2020-07-27 15:07:05 +00:00
}
2020-11-11 15:43:28 +00:00
if len ( aers ) == 0 {
2020-11-13 13:54:38 +00:00
return nil , response . NewRPCError ( "Application log for the transaction is empty" , "" , nil )
2020-11-11 15:43:28 +00:00
}
2020-11-13 13:54:38 +00:00
return result . NewTransactionOutputRaw ( tx , header , & aers [ 0 ] , s . chain ) , 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
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getTransactionHeight ( ps request . Params ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
_ , height , err := s . chain . GetTransaction ( h )
2020-11-13 13:54:38 +00:00
if err != nil || height == math . MaxUint32 {
2021-11-09 13:39:41 +00:00
return nil , response . NewRPCError ( "Unknown transaction" , "" , nil )
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).
2020-04-28 19:35:19 +00:00
func ( s * Server ) getContractState ( reqParams request . Params ) ( interface { } , * response . 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 {
return nil , response . NewRPCError ( "Unknown contract" , "" , nil )
}
return cs , nil
2020-02-15 16:53:08 +00:00
}
2021-02-09 09:25:38 +00:00
func ( s * Server ) getNativeContracts ( _ request . Params ) ( interface { } , * response . Error ) {
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.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockSysFee ( reqParams request . Params ) ( interface { } , * response . 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 {
return 0 , response . NewRPCError ( "Invalid height" , "" , nil )
}
headerHash := s . chain . GetHeaderHash ( num )
2020-04-28 19:35:19 +00:00
block , errBlock := s . chain . GetBlock ( headerHash )
if errBlock != nil {
return 0 , response . NewRPCError ( errBlock . Error ( ) , "" , nil )
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.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockHeader ( reqParams request . Params ) ( interface { } , * response . 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 {
return nil , response . NewRPCError ( "unknown block" , "" , nil )
}
if verbose {
return result . NewHeader ( h , s . chain ) , nil
}
buf := io . NewBufBinWriter ( )
h . EncodeBinary ( buf . BinWriter )
if buf . Err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewInternalServerError ( "encoding error" , 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.
func ( s * Server ) getUnclaimedGas ( ps request . Params ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
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 {
return nil , response . NewInternalServerError ( "can't calculate claimable" , err )
}
2020-07-09 14:25:26 +00:00
return result . UnclaimedGas {
Address : u ,
Unclaimed : * gas ,
} , nil
2020-03-06 17:38:17 +00:00
}
2020-10-01 12:26:54 +00:00
// getNextBlockValidators returns validators for the next block with voting status.
func ( s * Server ) getNextBlockValidators ( _ request . Params ) ( interface { } , * response . 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 {
2020-10-01 12:26:54 +00:00
return nil , response . NewRPCError ( "can't get next block validators" , "" , err )
2020-03-05 14:48:30 +00:00
}
enrollments , err := s . chain . GetEnrollments ( )
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewRPCError ( "can't get enrollments" , "" , err )
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 {
res = append ( res , result . Validator {
2020-04-26 17:04:16 +00:00
PublicKey : * v . Key ,
Votes : v . Votes . Int64 ( ) ,
Active : validators . Contains ( v . Key ) ,
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.
2020-09-21 12:34:04 +00:00
func ( s * Server ) getCommittee ( _ request . Params ) ( interface { } , * response . Error ) {
keys , err := s . chain . GetCommittee ( )
if err != nil {
return nil , response . NewInternalServerError ( "can't get committee members" , err )
}
return keys , nil
}
2020-06-10 08:53:11 +00:00
// invokeFunction implements the `invokeFunction` RPC call.
2020-04-28 19:35:19 +00:00
func ( s * Server ) invokeFunction ( reqParams request . Params ) ( interface { } , * response . Error ) {
2021-11-20 16:25:42 +00:00
if len ( reqParams ) < 2 {
return nil , response . ErrInvalidParams
}
2020-09-25 15:43:47 +00:00
scriptHash , responseErr := s . contractScriptHashFromParam ( reqParams . Value ( 0 ) )
if responseErr != nil {
return nil , 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 {
return nil , response . ErrInvalidParams
}
var params * request . Param
if len ( reqParams ) > 2 {
params = & reqParams [ 2 ]
}
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 {
return nil , response . ErrInvalidParams
}
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 {
return nil , response . ErrInvalidParams
}
}
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
}
2021-11-20 16:25:42 +00:00
script , err := request . CreateFunctionInvocationScript ( scriptHash , method , params )
2019-11-26 10:13:17 +00:00
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewInternalServerError ( "can't create invocation script" , err )
2019-11-26 10:13:17 +00:00
}
2020-07-14 14:32:59 +00:00
tx . Script = script
2021-11-20 18:55:55 +00:00
return s . runScriptInVM ( trigger . Application , script , util . Uint160 { } , tx , verbose )
2019-11-26 10:13:17 +00:00
}
2019-10-29 15:31:39 +00:00
// invokescript implements the `invokescript` RPC call.
2020-04-28 19:35:19 +00:00
func ( s * Server ) invokescript ( reqParams request . Params ) ( interface { } , * response . Error ) {
2019-11-21 14:42:02 +00:00
if len ( reqParams ) < 1 {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2019-10-29 15:31:39 +00:00
}
2019-11-21 14:42:02 +00:00
2020-10-14 13:46:06 +00:00
script , err := reqParams [ 0 ] . GetBytesBase64 ( )
2019-10-29 15:31:39 +00:00
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . 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 {
return nil , response . ErrInvalidParams
}
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 {
return nil , response . ErrInvalidParams
}
}
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
2021-11-20 18:55:55 +00:00
return s . runScriptInVM ( trigger . Application , script , util . Uint160 { } , tx , verbose )
2020-12-14 12:23:39 +00:00
}
// invokeContractVerify implements the `invokecontractverify` RPC call.
func ( s * Server ) invokeContractVerify ( reqParams request . Params ) ( interface { } , * response . Error ) {
scriptHash , responseErr := s . contractScriptHashFromParam ( reqParams . Value ( 0 ) )
if responseErr != nil {
return nil , responseErr
}
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 {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , err )
}
if len ( args ) > 0 {
err := request . ExpandArrayIntoScript ( bw . BinWriter , args )
if err != nil {
return nil , response . NewRPCError ( "can't create witness invocation script" , err . Error ( ) , err )
}
}
}
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 {
return nil , response . ErrInvalidParams
}
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
}
2021-11-20 18:55:55 +00:00
return s . runScriptInVM ( trigger . Verification , invocationScript , scriptHash , tx , false )
2019-11-28 16:12:23 +00:00
}
2021-11-17 20:04:50 +00:00
func ( s * Server ) getFakeNextBlock ( ) ( * block . Block , error ) {
2020-12-28 14:27:04 +00:00
// When transferring funds, script execution does no auto GAS claim,
2020-11-19 15:01:42 +00:00
// because it depends on persisting tx height.
// This is why we provide block here.
2021-03-25 18:46:52 +00:00
b := block . New ( s . stateRootEnabled )
2020-11-19 15:01:42 +00:00
b . Index = s . chain . BlockHeight ( ) + 1
hdr , err := s . chain . GetHeader ( s . chain . GetHeaderHash ( int ( s . chain . BlockHeight ( ) ) ) )
if err != nil {
2021-11-17 20:04:50 +00:00
return nil , err
2020-11-19 15:01:42 +00:00
}
b . Timestamp = hdr . Timestamp + uint64 ( s . chain . GetConfig ( ) . SecondsPerBlock * int ( time . Second / time . Millisecond ) )
2021-11-17 20:04:50 +00:00
return b , nil
}
2020-11-19 15:01:42 +00:00
2021-11-17 20:04:50 +00:00
// runScriptInVM runs 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.
2021-11-20 18:55:55 +00:00
func ( s * Server ) runScriptInVM ( t trigger . Type , script [ ] byte , contractScriptHash util . Uint160 , tx * transaction . Transaction , verbose bool ) ( * result . Invoke , * response . Error ) {
2021-11-17 20:04:50 +00:00
b , err := s . getFakeNextBlock ( )
if err != nil {
return nil , response . NewInternalServerError ( "can't create fake block" , err )
}
2022-01-12 22:20:08 +00:00
ic := s . chain . GetTestVM ( t , tx , b )
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-01-12 22:34:46 +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 {
return nil , response . NewInternalServerError ( "can't prepare verification VM" , err )
}
} else {
2022-01-12 22:20:08 +00:00
ic . VM . LoadScriptWithFlags ( script , callflag . All )
2021-03-10 14:43:52 +00:00
}
2022-01-12 22:20:08 +00:00
err = ic . VM . Run ( )
2020-10-05 13:33:20 +00:00
var faultException string
if err != nil {
faultException = err . Error ( )
}
2022-01-18 19:35:44 +00:00
return result . NewInvoke ( ic , script , faultException , s . config . MaxIteratorResultItems ) , nil
2019-10-29 15:31:39 +00:00
}
2020-03-02 17:01:32 +00:00
// submitBlock broadcasts a raw block over the NEO network.
2020-04-28 19:35:19 +00:00
func ( s * Server ) submitBlock ( reqParams request . Params ) ( interface { } , * response . 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 {
2021-04-10 19:47:24 +00:00
return nil , response . NewInvalidParamsError ( "missing parameter or not base64" , 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 {
2021-04-10 19:47:24 +00:00
return nil , response . NewInvalidParamsError ( "can't decode block" , 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 ) :
2021-02-25 09:08:41 +00:00
return nil , response . WrapErrorWithData ( response . ErrAlreadyExists , err )
2020-03-02 17:01:32 +00:00
default :
2021-02-25 09:08:41 +00:00
return nil , response . WrapErrorWithData ( response . ErrValidationFailed , err )
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
}
2021-02-05 07:26:23 +00:00
// submitNotaryRequest broadcasts P2PNotaryRequest over the NEO network.
func ( s * Server ) submitNotaryRequest ( ps request . Params ) ( interface { } , * response . Error ) {
if ! s . chain . P2PSigExtensionsEnabled ( ) {
return nil , response . NewInternalServerError ( "P2PNotaryRequest was received, but P2PSignatureExtensions are disabled" , nil )
}
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 {
2021-04-10 19:47:24 +00:00
return nil , response . NewInvalidParamsError ( "not base64" , 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 {
2021-04-10 19:47:24 +00:00
return nil , response . NewInvalidParamsError ( "can't decode notary payload" , 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.
2021-02-17 11:51:54 +00:00
func getRelayResult ( err error , hash util . Uint256 ) ( interface { } , * response . Error ) {
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 ) :
2021-02-25 09:08:41 +00:00
return nil , response . WrapErrorWithData ( response . ErrAlreadyExists , err )
2021-02-17 11:51:54 +00:00
case errors . Is ( err , core . ErrOOM ) :
2021-02-25 09:08:41 +00:00
return nil , response . WrapErrorWithData ( response . ErrOutOfMemory , err )
2021-02-17 11:51:54 +00:00
case errors . Is ( err , core . ErrPolicy ) :
2021-02-25 09:08:41 +00:00
return nil , response . WrapErrorWithData ( response . ErrPolicyFail , err )
2021-02-05 07:26:23 +00:00
default :
2021-02-25 09:08:41 +00:00
return nil , response . WrapErrorWithData ( response . ErrValidationFailed , err )
2021-02-05 07:26:23 +00:00
}
}
2020-09-28 11:58:04 +00:00
func ( s * Server ) submitOracleResponse ( ps request . Params ) ( interface { } , * response . Error ) {
if s . oracle == nil {
return nil , response . NewInternalServerError ( "oracle is not enabled" , nil )
}
var pub * keys . PublicKey
pubBytes , err := ps . Value ( 0 ) . GetBytesBase64 ( )
if err == nil {
pub , err = keys . NewPublicKeyFromBytes ( pubBytes , elliptic . P256 ( ) )
}
if err != nil {
return nil , response . NewInvalidParamsError ( "public key is missing" , err )
}
reqID , err := ps . Value ( 1 ) . GetInt ( )
if err != nil {
return nil , response . NewInvalidParamsError ( "request ID is missing" , err )
}
txSig , err := ps . Value ( 2 ) . GetBytesBase64 ( )
if err != nil {
return nil , response . NewInvalidParamsError ( "tx signature is missing" , err )
}
msgSig , err := ps . Value ( 3 ) . GetBytesBase64 ( )
if err != nil {
return nil , response . NewInvalidParamsError ( "msg signature is missing" , err )
}
data := broadcaster . GetMessage ( pubBytes , uint64 ( reqID ) , txSig )
if ! pub . Verify ( msgSig , hash . Sha256 ( data ) . BytesBE ( ) ) {
return nil , response . NewRPCError ( "Invalid sign" , "" , nil )
}
s . oracle . AddResponse ( pub , uint64 ( reqID ) , txSig )
return json . RawMessage ( [ ] byte ( "{}" ) ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) sendrawtransaction ( reqParams request . Params ) ( interface { } , * response . Error ) {
2019-11-21 14:42:02 +00:00
if len ( reqParams ) < 1 {
2021-04-09 21:31:42 +00:00
return nil , response . NewInvalidParamsError ( "not enough parameters" , nil )
2021-02-05 07:26:23 +00:00
}
byteTx , err := reqParams [ 0 ] . GetBytesBase64 ( )
if err != nil {
2021-04-09 21:31:42 +00:00
return nil , response . NewInvalidParamsError ( "not base64" , 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 {
2021-04-09 21:31:42 +00:00
return nil , response . NewInvalidParamsError ( "can't decode transaction" , 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.
func ( s * Server ) subscribe ( reqParams request . Params , sub * subscriber ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
event , err := response . GetEventIDFromString ( streamName )
2020-05-12 19:38:29 +00:00
if err != nil || event == response . MissedEventID {
2020-05-10 22:00:19 +00:00
return nil , response . ErrInvalidParams
}
2021-05-28 11:55:06 +00:00
if event == response . NotaryRequestEventID && ! s . chain . P2PSigExtensionsEnabled ( ) {
return nil , response . WrapErrorWithData ( response . ErrInvalidParams , errors . New ( "P2PSigExtensions are disabled" ) )
}
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 {
case response . BlockEventID :
2021-08-05 14:56:17 +00:00
flt := new ( request . BlockFilter )
err = jd . Decode ( flt )
2021-10-28 11:10:18 +00:00
filter = * flt
2021-08-05 14:56:17 +00:00
case response . TransactionEventID , response . NotaryRequestEventID :
flt := new ( request . TxFilter )
err = jd . Decode ( flt )
2021-10-28 11:10:18 +00:00
filter = * flt
2020-05-13 14:13:33 +00:00
case response . NotificationEventID :
2021-08-05 14:56:17 +00:00
flt := new ( request . NotificationFilter )
err = jd . Decode ( flt )
2021-10-28 11:10:18 +00:00
filter = * flt
2020-05-13 14:13:33 +00:00
case response . ExecutionEventID :
2021-08-05 14:56:17 +00:00
flt := new ( request . ExecutionFilter )
err = jd . Decode ( flt )
if err == 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 {
return nil , response . ErrInvalidParams
}
2020-05-13 14:13:33 +00:00
}
2020-05-10 22:00:19 +00:00
s . subsLock . Lock ( )
defer s . subsLock . Unlock ( )
select {
case <- s . shutdown :
return nil , response . NewInternalServerError ( "server is shutting down" , nil )
default :
}
var id int
for ; id < len ( sub . feeds ) ; id ++ {
2020-05-13 14:13:33 +00:00
if sub . feeds [ id ] . event == response . InvalidEventID {
2020-05-10 22:00:19 +00:00
break
}
}
if id == len ( sub . feeds ) {
return nil , response . NewInternalServerError ( "maximum number of subscriptions is reached" , nil )
}
2020-05-13 14:13:33 +00:00
sub . feeds [ id ] . event = event
sub . feeds [ id ] . filter = filter
2020-05-10 22:00:19 +00:00
s . subscribeToChannel ( event )
return strconv . FormatInt ( int64 ( id ) , 10 ) , nil
}
// subscribeToChannel subscribes RPC server to appropriate chain events if
// it's not yet subscribed for them. It's supposed to be called with s.subsLock
// taken by the caller.
func ( s * Server ) subscribeToChannel ( event response . EventID ) {
switch event {
case response . BlockEventID :
if s . blockSubs == 0 {
s . chain . SubscribeForBlocks ( s . blockCh )
}
s . blockSubs ++
case response . TransactionEventID :
if s . transactionSubs == 0 {
s . chain . SubscribeForTransactions ( s . transactionCh )
}
s . transactionSubs ++
case response . NotificationEventID :
if s . notificationSubs == 0 {
s . chain . SubscribeForNotifications ( s . notificationCh )
}
s . notificationSubs ++
case response . ExecutionEventID :
if s . executionSubs == 0 {
s . chain . SubscribeForExecutions ( s . executionCh )
}
s . executionSubs ++
2021-05-28 11:55:06 +00:00
case response . NotaryRequestEventID :
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.
func ( s * Server ) unsubscribe ( reqParams request . Params , sub * subscriber ) ( interface { } , * response . 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 {
return nil , response . ErrInvalidParams
}
s . subsLock . Lock ( )
defer s . subsLock . Unlock ( )
2020-05-13 14:13:33 +00:00
if len ( sub . feeds ) <= id || sub . feeds [ id ] . event == response . InvalidEventID {
2020-05-10 22:00:19 +00:00
return nil , response . ErrInvalidParams
}
2020-05-13 14:13:33 +00:00
event := sub . feeds [ id ] . event
sub . feeds [ id ] . event = response . InvalidEventID
sub . feeds [ id ] . filter = nil
2020-05-10 22:00:19 +00:00
s . unsubscribeFromChannel ( event )
return true , nil
}
// unsubscribeFromChannel unsubscribes RPC server from appropriate chain events
// if there are no other subscribers for it. It's supposed to be called with
// s.subsLock taken by the caller.
func ( s * Server ) unsubscribeFromChannel ( event response . EventID ) {
switch event {
case response . BlockEventID :
s . blockSubs --
if s . blockSubs == 0 {
s . chain . UnsubscribeFromBlocks ( s . blockCh )
}
case response . TransactionEventID :
s . transactionSubs --
if s . transactionSubs == 0 {
s . chain . UnsubscribeFromTransactions ( s . transactionCh )
}
case response . NotificationEventID :
s . notificationSubs --
if s . notificationSubs == 0 {
s . chain . UnsubscribeFromNotifications ( s . notificationCh )
}
case response . ExecutionEventID :
s . executionSubs --
if s . executionSubs == 0 {
s . chain . UnsubscribeFromExecutions ( s . executionCh )
}
2021-05-28 11:55:06 +00:00
case response . NotaryRequestEventID :
s . notaryRequestSubs --
if s . notaryRequestSubs == 0 {
s . coreServer . UnsubscribeFromNotaryRequests ( s . notaryRequestCh )
}
2020-05-10 22:00:19 +00:00
}
}
func ( s * Server ) handleSubEvents ( ) {
2020-05-12 19:38:29 +00:00
b , err := json . Marshal ( response . Notification {
JSONRPC : request . JSONRPCVersion ,
Event : response . MissedEventID ,
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 {
var resp = response . Notification {
JSONRPC : request . JSONRPCVersion ,
Payload : make ( [ ] interface { } , 1 ) ,
}
var msg * websocket . PreparedMessage
select {
case <- s . shutdown :
break chloop
case b := <- s . blockCh :
resp . Event = response . BlockEventID
resp . Payload [ 0 ] = b
case execution := <- s . executionCh :
resp . Event = response . 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 :
resp . Event = response . 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 :
resp . Event = response . TransactionEventID
resp . Payload [ 0 ] = tx
2021-05-28 11:55:06 +00:00
case e := <- s . notaryRequestCh :
resp . Event = response . NotaryRequestEventID
2021-09-24 09:23:30 +00:00
resp . Payload [ 0 ] = & subscriptions . 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 {
if sub . feeds [ i ] . Matches ( & 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 ) ,
zap . String ( "type" , resp . Event . String ( ) ) )
break subloop
}
msg , err = websocket . NewPreparedMessage ( websocket . TextMessage , b )
if err != nil {
s . log . Error ( "failed to prepare notification message" ,
zap . Error ( err ) ,
zap . String ( "type" , resp . Event . String ( ) ) )
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 ( )
}
// It's important to do it with lock held because no subscription routine
// 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.
s . subsLock . Lock ( )
// 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 )
}
2020-05-10 22:00:19 +00:00
s . subsLock . Unlock ( )
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
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) blockHeightFromParam ( param * request . Param ) ( int , * response . Error ) {
2019-11-26 10:13:17 +00:00
num , err := param . GetInt ( )
if err != nil {
2021-10-28 11:10:18 +00:00
return 0 , response . ErrInvalidParams
2019-11-26 10:13:17 +00:00
}
if num < 0 || num > int ( s . chain . BlockHeight ( ) ) {
return 0 , invalidBlockHeightError ( 0 , num )
}
return num , nil
2018-03-23 20:36:59 +00:00
}
2020-01-13 08:27:22 +00:00
2020-08-31 15:27:32 +00:00
func ( s * Server ) packResponse ( r * request . In , result interface { } , respErr * response . Error ) response . Abstract {
resp := response . Abstract {
2020-01-14 12:02:38 +00:00
HeaderAndError : response . HeaderAndError {
Header : response . Header {
JSONRPC : r . JSONRPC ,
ID : r . RawID ,
} ,
} ,
}
2020-04-28 19:56:19 +00:00
if respErr != nil {
resp . Error = respErr
} 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.
2020-10-26 17:22:20 +00:00
func ( s * Server ) logRequestError ( r * request . Request , jsonErr * response . Error ) {
2020-01-14 12:02:38 +00:00
logFields := [ ] zap . Field {
zap . Error ( jsonErr . Cause ) ,
}
2020-10-26 17:22:20 +00:00
if r . In != nil {
logFields = append ( logFields , zap . String ( "method" , r . In . Method ) )
2021-10-28 11:10:18 +00:00
params := request . Params ( r . In . RawParams )
logFields = append ( logFields , zap . Any ( "params" , params ) )
2020-01-14 12:02:38 +00:00
}
s . log . Error ( "Error encountered with rpc request" , logFields ... )
}
2020-04-28 19:56:19 +00:00
// writeHTTPErrorResponse writes an error response to the ResponseWriter.
func ( s * Server ) writeHTTPErrorResponse ( r * request . In , w http . ResponseWriter , jsonErr * response . Error ) {
2020-08-31 15:27:32 +00:00
resp := s . packResponse ( r , nil , jsonErr )
2020-10-26 17:22:20 +00:00
s . writeHTTPServerResponse ( & request . Request { In : r } , w , resp )
2020-01-14 12:02:38 +00:00
}
2020-10-26 17:22:20 +00:00
func ( s * Server ) writeHTTPServerResponse ( r * request . Request , w http . ResponseWriter , resp response . AbstractResult ) {
2020-04-28 19:56:19 +00:00
// Errors can happen in many places and we can only catch ALL of them here.
2020-10-26 17:22:20 +00:00
resp . RunForErrors ( func ( jsonErr * response . Error ) {
s . logRequestError ( r , jsonErr )
} )
if r . In != nil {
resp := resp . ( response . Abstract )
if resp . Error != nil {
w . WriteHeader ( resp . Error . HTTPCode )
}
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 {
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . Header ( ) . Set ( "Access-Control-Allow-Headers" , "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With" )
}
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
}
}
2020-01-13 08:27:22 +00:00
// validateAddress verifies that the address is a correct NEO address
// 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 )
}