aboutsummaryrefslogtreecommitdiffstats
path: root/rpc/types.go
blob: 4252c3602762b0b1e5c024b70ec6eae8dd3c76c5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package rpc

import (
    "fmt"
    "math"
    "reflect"
    "strings"
    "sync"

    mapset "github.com/deckarep/golang-set"
    "github.com/ethereum/go-ethereum/common/hexutil"
)

// API describes the set of methods offered over the RPC interface
type API struct {
    Namespace string      // namespace under which the rpc methods of Service are exposed
    Version   string      // api version for DApp's
    Service   interface{} // receiver instance which holds the methods
    Public    bool        // indication if the methods must be considered safe for public use
}

// callback is a method callback which was registered in the server
type callback struct {
    rcvr        reflect.Value  // receiver of method
    method      reflect.Method // callback
    argTypes    []reflect.Type // input argument types
    hasCtx      bool           // method's first argument is a context (not included in argTypes)
    errPos      int            // err return idx, of -1 when method cannot return error
    isSubscribe bool           // indication if the callback is a subscription
}

// service represents a registered object
type service struct {
    name          string        // name for service
    typ           reflect.Type  // receiver type
    callbacks     callbacks     // registered handlers
    subscriptions subscriptions // available subscriptions/notifications
}

// serverRequest is an incoming request
type serverRequest struct {
    id            interface{}
    svcname       string
    callb         *callback
    args          []reflect.Value
    isUnsubscribe bool
    err           Error
}

type serviceRegistry map[string]*service // collection of services
type callbacks map[string]*callback      // collection of RPC callbacks
type subscriptions map[string]*callback  // collection of subscription callbacks

// Server represents a RPC server
type Server struct {
    services serviceRegistry

    run      int32
    codecsMu sync.Mutex
    codecs   mapset.Set
}

// rpcRequest represents a raw incoming RPC request
type rpcRequest struct {
    service  string
    method   string
    id       interface{}
    isPubSub bool
    params   interface{}
    err      Error // invalid batch element
}

// Error wraps RPC errors, which contain an error code in addition to the message.
type Error interface {
    Error() string  // returns the message
    ErrorCode() int // returns the code
}

// ServerCodec implements reading, parsing and writing RPC messages for the server side of
// a RPC session. Implementations must be go-routine safe since the codec can be called in
// multiple go-routines concurrently.
type ServerCodec interface {
    // Read next request
    ReadRequestHeaders() ([]rpcRequest, bool, Error)
    // Parse request argument to the given types
    ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, Error)
    // Assemble success response, expects response id and payload
    CreateResponse(id interface{}, reply interface{}) interface{}
    // Assemble error response, expects response id and error
    CreateErrorResponse(id interface{}, err Error) interface{}
    // Assemble error response with extra information about the error through info
    CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{}
    // Create notification response
    CreateNotification(id, namespace string, event interface{}) interface{}
    // Write msg to client.
    Write(msg interface{}) error
    // Close underlying data stream
    Close()
    // Closed when underlying connection is closed
    Closed() <-chan interface{}
}

type BlockNumber int64

const (
    PendingBlockNumber  = BlockNumber(-2)
    LatestBlockNumber   = BlockNumber(-1)
    EarliestBlockNumber = BlockNumber(0)
)

// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
// - "latest", "earliest" or "pending" as string arguments
// - the block number
// Returned errors:
// - an invalid block number error when the given argument isn't a known strings
// - an out of range error when the given block number is either too little or too large
func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
    input := strings.TrimSpace(string(data))
    if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
        input = input[1 : len(input)-1]
    }

    switch input {
    case "earliest":
        *bn = EarliestBlockNumber
        return nil
    case "latest":
        *bn = LatestBlockNumber
        return nil
    case "pending":
        *bn = PendingBlockNumber
        return nil
    }

    blckNum, err := hexutil.DecodeUint64(input)
    if err != nil {
        return err
    }
    if blckNum > math.MaxInt64 {
        return fmt.Errorf("Blocknumber too high")
    }

    *bn = BlockNumber(blckNum)
    return nil
}

func (bn BlockNumber) Int64() int64 {
    return (int64)(bn)
}