Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
2b34a1ee
Commit
2b34a1ee
authored
Jan 21, 2022
by
inphi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
removed eth_call sync code; cache mod latest blocks
parent
1cf8b15d
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
377 additions
and
376 deletions
+377
-376
backend.go
go/proxyd/backend.go
+6
-37
cache.go
go/proxyd/cache.go
+32
-21
cache_test.go
go/proxyd/cache_test.go
+143
-109
config.go
go/proxyd/config.go
+3
-2
lvc.go
go/proxyd/lvc.go
+19
-17
methods.go
go/proxyd/methods.go
+101
-139
metrics.go
go/proxyd/metrics.go
+18
-2
proxyd.go
go/proxyd/proxyd.go
+52
-44
server.go
go/proxyd/server.go
+3
-5
No files found.
go/proxyd/backend.go
View file @
2b34a1ee
...
@@ -16,9 +16,7 @@ import (
...
@@ -16,9 +16,7 @@ import (
"strings"
"strings"
"time"
"time"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gorilla/websocket"
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus"
)
)
...
@@ -86,7 +84,6 @@ type Backend struct {
...
@@ -86,7 +84,6 @@ type Backend struct {
authPassword
string
authPassword
string
rateLimiter
RateLimiter
rateLimiter
RateLimiter
client
*
http
.
Client
client
*
http
.
Client
blockNumberLVC
*
EthLastValueCache
dialer
*
websocket
.
Dialer
dialer
*
websocket
.
Dialer
maxRetries
int
maxRetries
int
maxResponseSize
int64
maxResponseSize
int64
...
@@ -169,7 +166,7 @@ func NewBackend(
...
@@ -169,7 +166,7 @@ func NewBackend(
wsURL
string
,
wsURL
string
,
rateLimiter
RateLimiter
,
rateLimiter
RateLimiter
,
opts
...
BackendOpt
,
opts
...
BackendOpt
,
)
(
*
Backend
,
error
)
{
)
*
Backend
{
backend
:=
&
Backend
{
backend
:=
&
Backend
{
Name
:
name
,
Name
:
name
,
rpcURL
:
rpcURL
,
rpcURL
:
rpcURL
,
...
@@ -186,28 +183,11 @@ func NewBackend(
...
@@ -186,28 +183,11 @@ func NewBackend(
opt
(
backend
)
opt
(
backend
)
}
}
rpcClient
,
err
:=
rpc
.
DialHTTPWithClient
(
rpcURL
,
backend
.
client
)
if
err
!=
nil
{
return
nil
,
err
}
backend
.
blockNumberLVC
=
newLVC
(
ethclient
.
NewClient
(
rpcClient
),
func
(
ctx
context
.
Context
,
client
*
ethclient
.
Client
)
(
interface
{},
error
)
{
blockNumber
,
err
:=
client
.
BlockNumber
(
ctx
)
return
blockNumber
,
err
})
if
!
backend
.
stripTrailingXFF
&&
backend
.
proxydIP
==
""
{
if
!
backend
.
stripTrailingXFF
&&
backend
.
proxydIP
==
""
{
log
.
Warn
(
"proxied requests' XFF header will not contain the proxyd ip address"
)
log
.
Warn
(
"proxied requests' XFF header will not contain the proxyd ip address"
)
}
}
return
backend
,
nil
return
backend
}
func
(
b
*
Backend
)
Start
()
{
b
.
blockNumberLVC
.
Start
()
}
func
(
b
*
Backend
)
Stop
()
{
b
.
blockNumberLVC
.
Stop
()
}
}
func
(
b
*
Backend
)
Forward
(
ctx
context
.
Context
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
func
(
b
*
Backend
)
Forward
(
ctx
context
.
Context
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
...
@@ -288,14 +268,6 @@ func (b *Backend) ProxyWS(clientConn *websocket.Conn, methodWhitelist *StringSet
...
@@ -288,14 +268,6 @@ func (b *Backend) ProxyWS(clientConn *websocket.Conn, methodWhitelist *StringSet
return
NewWSProxier
(
b
,
clientConn
,
backendConn
,
methodWhitelist
),
nil
return
NewWSProxier
(
b
,
clientConn
,
backendConn
,
methodWhitelist
),
nil
}
}
func
(
b
*
Backend
)
BlockNumber
()
uint64
{
var
blockNum
uint64
if
val
:=
b
.
blockNumberLVC
.
Read
();
val
!=
nil
{
blockNum
=
val
.
(
uint64
)
}
return
blockNum
}
func
(
b
*
Backend
)
Online
()
bool
{
func
(
b
*
Backend
)
Online
()
bool
{
online
,
err
:=
b
.
rateLimiter
.
IsBackendOnline
(
b
.
Name
)
online
,
err
:=
b
.
rateLimiter
.
IsBackendOnline
(
b
.
Name
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -423,16 +395,13 @@ type BackendGroup struct {
...
@@ -423,16 +395,13 @@ type BackendGroup struct {
Backends
[]
*
Backend
Backends
[]
*
Backend
}
}
func
(
b
*
BackendGroup
)
Forward
(
ctx
context
.
Context
,
rpcReq
*
RPCReq
)
(
*
RPCRes
,
uint64
,
error
)
{
func
(
b
*
BackendGroup
)
Forward
(
ctx
context
.
Context
,
rpcReq
*
RPCReq
)
(
*
RPCRes
,
error
)
{
rpcRequestsTotal
.
Inc
()
rpcRequestsTotal
.
Inc
()
for
_
,
back
:=
range
b
.
Backends
{
for
_
,
back
:=
range
b
.
Backends
{
// The blockNum must precede the forwarded RPC to establish a synchronization point
blockNum
:=
back
.
BlockNumber
()
res
,
err
:=
back
.
Forward
(
ctx
,
rpcReq
)
res
,
err
:=
back
.
Forward
(
ctx
,
rpcReq
)
if
errors
.
Is
(
err
,
ErrMethodNotWhitelisted
)
{
if
errors
.
Is
(
err
,
ErrMethodNotWhitelisted
)
{
return
nil
,
0
,
err
return
nil
,
err
}
}
if
errors
.
Is
(
err
,
ErrBackendOffline
)
{
if
errors
.
Is
(
err
,
ErrBackendOffline
)
{
log
.
Warn
(
log
.
Warn
(
...
@@ -462,11 +431,11 @@ func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, ui
...
@@ -462,11 +431,11 @@ func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, ui
)
)
continue
continue
}
}
return
res
,
blockNum
,
nil
return
res
,
nil
}
}
RecordUnserviceableRequest
(
ctx
,
RPCRequestSourceHTTP
)
RecordUnserviceableRequest
(
ctx
,
RPCRequestSourceHTTP
)
return
nil
,
0
,
ErrNoBackends
return
nil
,
ErrNoBackends
}
}
func
(
b
*
BackendGroup
)
ProxyWS
(
ctx
context
.
Context
,
clientConn
*
websocket
.
Conn
,
methodWhitelist
*
StringSet
)
(
*
WSProxier
,
error
)
{
func
(
b
*
BackendGroup
)
ProxyWS
(
ctx
context
.
Context
,
clientConn
*
websocket
.
Conn
,
methodWhitelist
*
StringSet
)
(
*
WSProxier
,
error
)
{
...
...
go/proxyd/cache.go
View file @
2b34a1ee
...
@@ -4,13 +4,13 @@ import (
...
@@ -4,13 +4,13 @@ import (
"context"
"context"
"github.com/go-redis/redis/v8"
"github.com/go-redis/redis/v8"
"github.com/golang/snappy"
lru
"github.com/hashicorp/golang-lru"
lru
"github.com/hashicorp/golang-lru"
)
)
type
Cache
interface
{
type
Cache
interface
{
Get
(
ctx
context
.
Context
,
key
string
)
(
string
,
error
)
Get
(
ctx
context
.
Context
,
key
string
)
(
string
,
error
)
Put
(
ctx
context
.
Context
,
key
string
,
value
string
)
error
Put
(
ctx
context
.
Context
,
key
string
,
value
string
)
error
Remove
(
ctx
context
.
Context
,
key
string
)
error
}
}
const
(
const
(
...
@@ -39,11 +39,6 @@ func (c *cache) Put(ctx context.Context, key string, value string) error {
...
@@ -39,11 +39,6 @@ func (c *cache) Put(ctx context.Context, key string, value string) error {
return
nil
return
nil
}
}
func
(
c
*
cache
)
Remove
(
ctx
context
.
Context
,
key
string
)
error
{
c
.
lru
.
Remove
(
key
)
return
nil
}
type
redisCache
struct
{
type
redisCache
struct
{
rdb
*
redis
.
Client
rdb
*
redis
.
Client
}
}
...
@@ -79,12 +74,32 @@ func (c *redisCache) Put(ctx context.Context, key string, value string) error {
...
@@ -79,12 +74,32 @@ func (c *redisCache) Put(ctx context.Context, key string, value string) error {
return
err
return
err
}
}
func
(
c
*
redisCache
)
Remove
(
ctx
context
.
Context
,
key
string
)
error
{
type
cacheWithCompression
struct
{
err
:=
c
.
rdb
.
Del
(
ctx
,
key
)
.
Err
()
cache
Cache
}
func
newCacheWithCompression
(
cache
Cache
)
*
cacheWithCompression
{
return
&
cacheWithCompression
{
cache
}
}
func
(
c
*
cacheWithCompression
)
Get
(
ctx
context
.
Context
,
key
string
)
(
string
,
error
)
{
encodedVal
,
err
:=
c
.
cache
.
Get
(
ctx
,
key
)
if
err
!=
nil
{
if
err
!=
nil
{
RecordRedisError
(
"CacheDel"
)
return
""
,
err
}
}
return
err
if
encodedVal
==
""
{
return
""
,
nil
}
val
,
err
:=
snappy
.
Decode
(
nil
,
[]
byte
(
encodedVal
))
if
err
!=
nil
{
return
""
,
err
}
return
string
(
val
),
nil
}
func
(
c
*
cacheWithCompression
)
Put
(
ctx
context
.
Context
,
key
string
,
value
string
)
error
{
encodedVal
:=
snappy
.
Encode
(
nil
,
[]
byte
(
value
))
return
c
.
cache
.
Put
(
ctx
,
key
,
string
(
encodedVal
))
}
}
type
GetLatestBlockNumFn
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
type
GetLatestBlockNumFn
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
...
@@ -92,11 +107,7 @@ type GetLatestGasPriceFn func(ctx context.Context) (uint64, error)
...
@@ -92,11 +107,7 @@ type GetLatestGasPriceFn func(ctx context.Context) (uint64, error)
type
RPCCache
interface
{
type
RPCCache
interface
{
GetRPC
(
ctx
context
.
Context
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
GetRPC
(
ctx
context
.
Context
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
PutRPC
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
)
error
// The blockNumberSync is used to enforce Sequential Consistency for cache invalidation. We make the following assumptions to do this:
// 1. blockNumberSync is monotonically increasing (sans reorgs)
// 2. blockNumberSync is ordered before block state of the RPCRes
PutRPC
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
,
blockNumberSync
uint64
)
error
}
}
type
rpcCache
struct
{
type
rpcCache
struct
{
...
@@ -104,15 +115,15 @@ type rpcCache struct {
...
@@ -104,15 +115,15 @@ type rpcCache struct {
handlers
map
[
string
]
RPCMethodHandler
handlers
map
[
string
]
RPCMethodHandler
}
}
func
newRPCCache
(
cache
Cache
,
getLatestBlockNumFn
GetLatestBlockNumFn
,
getLatestGasPriceFn
GetLatestGasPriceFn
)
RPCCache
{
func
newRPCCache
(
cache
Cache
,
getLatestBlockNumFn
GetLatestBlockNumFn
,
getLatestGasPriceFn
GetLatestGasPriceFn
,
numBlockConfirmations
int
)
RPCCache
{
handlers
:=
map
[
string
]
RPCMethodHandler
{
handlers
:=
map
[
string
]
RPCMethodHandler
{
"eth_chainId"
:
&
StaticMethodHandler
{},
"eth_chainId"
:
&
StaticMethodHandler
{},
"net_version"
:
&
StaticMethodHandler
{},
"net_version"
:
&
StaticMethodHandler
{},
"eth_getBlockByNumber"
:
&
EthGetBlockByNumberMethodHandler
{
cache
,
getLatestBlockNumFn
},
"eth_getBlockByNumber"
:
&
EthGetBlockByNumberMethodHandler
{
cache
,
getLatestBlockNumFn
,
numBlockConfirmations
},
"eth_getBlockRange"
:
&
EthGetBlockRangeMethodHandler
{
cache
,
getLatestBlockNumFn
},
"eth_getBlockRange"
:
&
EthGetBlockRangeMethodHandler
{
cache
,
getLatestBlockNumFn
,
numBlockConfirmations
},
"eth_blockNumber"
:
&
EthBlockNumberMethodHandler
{
getLatestBlockNumFn
},
"eth_blockNumber"
:
&
EthBlockNumberMethodHandler
{
getLatestBlockNumFn
},
"eth_gasPrice"
:
&
EthGasPriceMethodHandler
{
getLatestGasPriceFn
},
"eth_gasPrice"
:
&
EthGasPriceMethodHandler
{
getLatestGasPriceFn
},
"eth_call"
:
&
EthCallMethodHandler
{
cache
,
getLatestBlockNumFn
},
"eth_call"
:
&
EthCallMethodHandler
{
cache
,
getLatestBlockNumFn
,
numBlockConfirmations
},
}
}
return
&
rpcCache
{
return
&
rpcCache
{
cache
:
cache
,
cache
:
cache
,
...
@@ -136,10 +147,10 @@ func (c *rpcCache) GetRPC(ctx context.Context, req *RPCReq) (*RPCRes, error) {
...
@@ -136,10 +147,10 @@ func (c *rpcCache) GetRPC(ctx context.Context, req *RPCReq) (*RPCRes, error) {
return
res
,
err
return
res
,
err
}
}
func
(
c
*
rpcCache
)
PutRPC
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
,
blockNumberSync
uint64
)
error
{
func
(
c
*
rpcCache
)
PutRPC
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
handler
:=
c
.
handlers
[
req
.
Method
]
handler
:=
c
.
handlers
[
req
.
Method
]
if
handler
==
nil
{
if
handler
==
nil
{
return
nil
return
nil
}
}
return
handler
.
PutRPCMethod
(
ctx
,
req
,
res
,
blockNumberSync
)
return
handler
.
PutRPCMethod
(
ctx
,
req
,
res
)
}
}
go/proxyd/cache_test.go
View file @
2b34a1ee
...
@@ -5,11 +5,12 @@ import (
...
@@ -5,11 +5,12 @@ import (
"math"
"math"
"strconv"
"strconv"
"testing"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
const
numBlockConfirmations
=
10
func
TestRPCCacheImmutableRPCs
(
t
*
testing
.
T
)
{
func
TestRPCCacheImmutableRPCs
(
t
*
testing
.
T
)
{
const
blockHead
=
math
.
MaxUint64
const
blockHead
=
math
.
MaxUint64
ctx
:=
context
.
Background
()
ctx
:=
context
.
Background
()
...
@@ -17,7 +18,7 @@ func TestRPCCacheImmutableRPCs(t *testing.T) {
...
@@ -17,7 +18,7 @@ func TestRPCCacheImmutableRPCs(t *testing.T) {
getBlockNum
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
getBlockNum
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
getBlockNum
,
nil
)
cache
:=
newRPCCache
(
newMemoryCache
(),
getBlockNum
,
nil
,
numBlockConfirmations
)
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
rpcs
:=
[]
struct
{
rpcs
:=
[]
struct
{
...
@@ -111,7 +112,7 @@ func TestRPCCacheImmutableRPCs(t *testing.T) {
...
@@ -111,7 +112,7 @@ func TestRPCCacheImmutableRPCs(t *testing.T) {
for
_
,
rpc
:=
range
rpcs
{
for
_
,
rpc
:=
range
rpcs
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
...
@@ -133,7 +134,7 @@ func TestRPCCacheBlockNumber(t *testing.T) {
...
@@ -133,7 +134,7 @@ func TestRPCCacheBlockNumber(t *testing.T) {
getBlockNum
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
getBlockNum
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
getBlockNum
,
getGasPrice
)
cache
:=
newRPCCache
(
newMemoryCache
(),
getBlockNum
,
getGasPrice
,
numBlockConfirmations
)
req
:=
&
RPCReq
{
req
:=
&
RPCReq
{
JSONRPC
:
"2.0"
,
JSONRPC
:
"2.0"
,
...
@@ -146,12 +147,17 @@ func TestRPCCacheBlockNumber(t *testing.T) {
...
@@ -146,12 +147,17 @@ func TestRPCCacheBlockNumber(t *testing.T) {
ID
:
ID
,
ID
:
ID
,
}
}
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
res
,
cachedRes
)
require
.
Equal
(
t
,
res
,
cachedRes
)
blockHead
=
0x1001
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
&
RPCRes
{
JSONRPC
:
"2.0"
,
Result
:
`0x1001`
,
ID
:
ID
},
cachedRes
)
}
}
func
TestRPCCacheGasPrice
(
t
*
testing
.
T
)
{
func
TestRPCCacheGasPrice
(
t
*
testing
.
T
)
{
...
@@ -166,7 +172,7 @@ func TestRPCCacheGasPrice(t *testing.T) {
...
@@ -166,7 +172,7 @@ func TestRPCCacheGasPrice(t *testing.T) {
getBlockNum
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
getBlockNum
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
getBlockNum
,
getGasPrice
)
cache
:=
newRPCCache
(
newMemoryCache
(),
getBlockNum
,
getGasPrice
,
numBlockConfirmations
)
req
:=
&
RPCReq
{
req
:=
&
RPCReq
{
JSONRPC
:
"2.0"
,
JSONRPC
:
"2.0"
,
...
@@ -179,12 +185,17 @@ func TestRPCCacheGasPrice(t *testing.T) {
...
@@ -179,12 +185,17 @@ func TestRPCCacheGasPrice(t *testing.T) {
ID
:
ID
,
ID
:
ID
,
}
}
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
res
,
cachedRes
)
require
.
Equal
(
t
,
res
,
cachedRes
)
gasPrice
=
0x101
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
&
RPCRes
{
JSONRPC
:
"2.0"
,
Result
:
`0x101`
,
ID
:
ID
},
cachedRes
)
}
}
func
TestRPCCacheUnsupportedMethod
(
t
*
testing
.
T
)
{
func
TestRPCCacheUnsupportedMethod
(
t
*
testing
.
T
)
{
...
@@ -194,7 +205,7 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) {
...
@@ -194,7 +205,7 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) {
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
req
:=
&
RPCReq
{
req
:=
&
RPCReq
{
...
@@ -208,7 +219,7 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) {
...
@@ -208,7 +219,7 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) {
ID
:
ID
,
ID
:
ID
,
}
}
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
...
@@ -219,12 +230,11 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) {
...
@@ -219,12 +230,11 @@ func TestRPCCacheUnsupportedMethod(t *testing.T) {
func
TestRPCCacheEthGetBlockByNumber
(
t
*
testing
.
T
)
{
func
TestRPCCacheEthGetBlockByNumber
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ctx
:=
context
.
Background
()
var
blockHead
uint64
=
100
var
blockHead
uint64
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
makeCache
:=
func
()
RPCCache
{
return
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
}
resetCache
:=
func
()
{
cache
=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
}
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
req
:=
&
RPCReq
{
req
:=
&
RPCReq
{
...
@@ -250,22 +260,27 @@ func TestRPCCacheEthGetBlockByNumber(t *testing.T) {
...
@@ -250,22 +260,27 @@ func TestRPCCacheEthGetBlockByNumber(t *testing.T) {
ID
:
ID
,
ID
:
ID
,
}
}
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
))
t
.
Run
(
"set multiple finalized blocks"
,
func
(
t
*
testing
.
T
)
{
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req2
,
res2
,
blockHead
))
blockHead
=
100
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cache
:=
makeCache
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
require
.
Equal
(
t
,
res
,
cachedRes
)
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req2
,
res2
))
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req2
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
res2
,
cachedRes
)
require
.
Equal
(
t
,
res
,
cachedRes
)
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req2
)
// scenario: input references block that has not yet finalized
require
.
NoError
(
t
,
err
)
resetCache
()
require
.
Equal
(
t
,
res2
,
cachedRes
)
blockHead
=
0xc
})
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
))
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
t
.
Run
(
"unconfirmed block"
,
func
(
t
*
testing
.
T
)
{
require
.
NoError
(
t
,
err
)
blockHead
=
0xc
require
.
Nil
(
t
,
cachedRes
)
cache
:=
makeCache
()
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
Nil
(
t
,
cachedRes
)
})
}
}
func
TestRPCCacheEthGetBlockByNumberForRecentBlocks
(
t
*
testing
.
T
)
{
func
TestRPCCacheEthGetBlockByNumberForRecentBlocks
(
t
*
testing
.
T
)
{
...
@@ -275,7 +290,7 @@ func TestRPCCacheEthGetBlockByNumberForRecentBlocks(t *testing.T) {
...
@@ -275,7 +290,7 @@ func TestRPCCacheEthGetBlockByNumberForRecentBlocks(t *testing.T) {
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
rpcs
:=
[]
struct
{
rpcs
:=
[]
struct
{
...
@@ -315,7 +330,7 @@ func TestRPCCacheEthGetBlockByNumberForRecentBlocks(t *testing.T) {
...
@@ -315,7 +330,7 @@ func TestRPCCacheEthGetBlockByNumberForRecentBlocks(t *testing.T) {
for
_
,
rpc
:=
range
rpcs
{
for
_
,
rpc
:=
range
rpcs
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
...
@@ -332,7 +347,7 @@ func TestRPCCacheEthGetBlockByNumberInvalidRequest(t *testing.T) {
...
@@ -332,7 +347,7 @@ func TestRPCCacheEthGetBlockByNumberInvalidRequest(t *testing.T) {
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
req
:=
&
RPCReq
{
req
:=
&
RPCReq
{
...
@@ -347,7 +362,7 @@ func TestRPCCacheEthGetBlockByNumberInvalidRequest(t *testing.T) {
...
@@ -347,7 +362,7 @@ func TestRPCCacheEthGetBlockByNumberInvalidRequest(t *testing.T) {
ID
:
ID
,
ID
:
ID
,
}
}
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
)
require
.
Error
(
t
,
err
)
require
.
Error
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
...
@@ -358,46 +373,51 @@ func TestRPCCacheEthGetBlockByNumberInvalidRequest(t *testing.T) {
...
@@ -358,46 +373,51 @@ func TestRPCCacheEthGetBlockByNumberInvalidRequest(t *testing.T) {
func
TestRPCCacheEthGetBlockRange
(
t
*
testing
.
T
)
{
func
TestRPCCacheEthGetBlockRange
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ctx
:=
context
.
Background
()
var
blockHead
uint64
=
0x1000
var
blockHead
uint64
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
makeCache
:=
func
()
RPCCache
{
return
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
}
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
req
:=
&
RPCReq
{
t
.
Run
(
"finalized block"
,
func
(
t
*
testing
.
T
)
{
JSONRPC
:
"2.0"
,
req
:=
&
RPCReq
{
Method
:
"eth_getBlockRange"
,
JSONRPC
:
"2.0"
,
Params
:
[]
byte
(
`["0x1", "0x10", false]`
),
Method
:
"eth_getBlockRange"
,
ID
:
ID
,
Params
:
[]
byte
(
`["0x1", "0x10", false]`
),
}
ID
:
ID
,
res
:=
&
RPCRes
{
}
JSONRPC
:
"2.0"
,
res
:=
&
RPCRes
{
Result
:
`[{"number": "0x1"}, {"number": "0x10"}]`
,
JSONRPC
:
"2.0"
,
ID
:
ID
,
Result
:
`[{"number": "0x1"}, {"number": "0x10"}]`
,
}
ID
:
ID
,
}
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
))
blockHead
=
0x1000
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cache
:=
makeCache
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
require
.
Equal
(
t
,
res
,
cachedRes
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
// scenario: input references block that has not yet finalized
require
.
Equal
(
t
,
res
,
cachedRes
)
req
=
&
RPCReq
{
})
JSONRPC
:
"2.0"
,
Method
:
"eth_getBlockRange"
,
t
.
Run
(
"unconfirmed block"
,
func
(
t
*
testing
.
T
)
{
Params
:
[]
byte
(
`["0x1", "0x1000", false]`
),
cache
:=
makeCache
()
ID
:
ID
,
req
:=
&
RPCReq
{
}
JSONRPC
:
"2.0"
,
res
=
&
RPCRes
{
Method
:
"eth_getBlockRange"
,
JSONRPC
:
"2.0"
,
Params
:
[]
byte
(
`["0x1", "0x1000", false]`
),
Result
:
`[{"number": "0x1"}, {"number": "0x2"}]`
,
ID
:
ID
,
ID
:
ID
,
}
}
res
:=
&
RPCRes
{
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
))
JSONRPC
:
"2.0"
,
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
Result
:
`[{"number": "0x1"}, {"number": "0x2"}]`
,
require
.
NoError
(
t
,
err
)
ID
:
ID
,
require
.
Nil
(
t
,
cachedRes
)
}
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
Nil
(
t
,
cachedRes
)
})
}
}
func
TestRPCCacheEthGetBlockRangeForRecentBlocks
(
t
*
testing
.
T
)
{
func
TestRPCCacheEthGetBlockRangeForRecentBlocks
(
t
*
testing
.
T
)
{
...
@@ -407,7 +427,7 @@ func TestRPCCacheEthGetBlockRangeForRecentBlocks(t *testing.T) {
...
@@ -407,7 +427,7 @@ func TestRPCCacheEthGetBlockRangeForRecentBlocks(t *testing.T) {
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
rpcs
:=
[]
struct
{
rpcs
:=
[]
struct
{
...
@@ -461,7 +481,7 @@ func TestRPCCacheEthGetBlockRangeForRecentBlocks(t *testing.T) {
...
@@ -461,7 +481,7 @@ func TestRPCCacheEthGetBlockRangeForRecentBlocks(t *testing.T) {
for
_
,
rpc
:=
range
rpcs
{
for
_
,
rpc
:=
range
rpcs
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
...
@@ -478,7 +498,7 @@ func TestRPCCacheEthGetBlockRangeInvalidRequest(t *testing.T) {
...
@@ -478,7 +498,7 @@ func TestRPCCacheEthGetBlockRangeInvalidRequest(t *testing.T) {
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
rpcs
:=
[]
struct
{
rpcs
:=
[]
struct
{
...
@@ -518,7 +538,7 @@ func TestRPCCacheEthGetBlockRangeInvalidRequest(t *testing.T) {
...
@@ -518,7 +538,7 @@ func TestRPCCacheEthGetBlockRangeInvalidRequest(t *testing.T) {
for
_
,
rpc
:=
range
rpcs
{
for
_
,
rpc
:=
range
rpcs
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
rpc
.
name
,
func
(
t
*
testing
.
T
)
{
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
,
blockHead
)
err
:=
cache
.
PutRPC
(
ctx
,
rpc
.
req
,
rpc
.
res
)
require
.
Error
(
t
,
err
)
require
.
Error
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
rpc
.
req
)
...
@@ -531,18 +551,18 @@ func TestRPCCacheEthGetBlockRangeInvalidRequest(t *testing.T) {
...
@@ -531,18 +551,18 @@ func TestRPCCacheEthGetBlockRangeInvalidRequest(t *testing.T) {
func
TestRPCCacheEthCall
(
t
*
testing
.
T
)
{
func
TestRPCCacheEthCall
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ctx
:=
context
.
Background
()
var
blockHead
uint64
=
0x1000
var
blockHead
uint64
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
fn
:=
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
return
blockHead
,
nil
return
blockHead
,
nil
}
}
cache
:=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
resetCache
:=
func
()
{
cache
=
newRPCCache
(
newMemoryCache
(),
fn
,
nil
)
}
makeCache
:=
func
()
RPCCache
{
return
newRPCCache
(
newMemoryCache
(),
fn
,
nil
,
numBlockConfirmations
)
}
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
ID
:=
[]
byte
(
strconv
.
Itoa
(
1
))
req
:=
&
RPCReq
{
req
:=
&
RPCReq
{
JSONRPC
:
"2.0"
,
JSONRPC
:
"2.0"
,
Method
:
"eth_call"
,
Method
:
"eth_call"
,
Params
:
[]
byte
(
`[{"to": "0xDEADBEEF", "data": "0x1"}, "
latest
"]`
),
Params
:
[]
byte
(
`[{"to": "0xDEADBEEF", "data": "0x1"}, "
0x10
"]`
),
ID
:
ID
,
ID
:
ID
,
}
}
res
:=
&
RPCRes
{
res
:=
&
RPCRes
{
...
@@ -551,38 +571,52 @@ func TestRPCCacheEthCall(t *testing.T) {
...
@@ -551,38 +571,52 @@ func TestRPCCacheEthCall(t *testing.T) {
ID
:
ID
,
ID
:
ID
,
}
}
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
t
.
Run
(
"finalized block"
,
func
(
t
*
testing
.
T
)
{
require
.
NoError
(
t
,
err
)
blockHead
=
0x100
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
cache
:=
makeCache
()
require
.
NoError
(
t
,
err
)
err
:=
cache
.
PutRPC
(
ctx
,
req
,
res
)
require
.
Equal
(
t
,
res
,
cachedRes
)
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
// scenario: no new block, but we've exceeded cacheTTL
require
.
NoError
(
t
,
err
)
resetCache
()
require
.
Equal
(
t
,
res
,
cachedRes
)
cacheTTL
=
24
*
time
.
Hour
})
err
=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
require
.
NoError
(
t
,
err
)
t
.
Run
(
"unconfirmed block"
,
func
(
t
*
testing
.
T
)
{
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
blockHead
=
0x10
require
.
NoError
(
t
,
err
)
cache
:=
makeCache
()
require
.
Equal
(
t
,
res
,
cachedRes
)
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
// scenario: new block, but cached TTL is live
require
.
NoError
(
t
,
err
)
resetCache
()
require
.
Nil
(
t
,
cachedRes
)
cacheTTL
=
24
*
time
.
Hour
})
err
=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
require
.
NoError
(
t
,
err
)
t
.
Run
(
"latest block"
,
func
(
t
*
testing
.
T
)
{
blockHead
+=
1
// new block
blockHead
=
0x100
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
req
:=
&
RPCReq
{
require
.
NoError
(
t
,
err
)
JSONRPC
:
"2.0"
,
require
.
Equal
(
t
,
res
,
cachedRes
)
Method
:
"eth_call"
,
Params
:
[]
byte
(
`[{"to": "0xDEADBEEF", "data": "0x1"}, "latest"]`
),
// scenario: new block, cache TTL exceeded; cache invalidation
ID
:
ID
,
resetCache
()
}
cacheTTL
=
0
*
time
.
Second
cache
:=
makeCache
()
err
=
cache
.
PutRPC
(
ctx
,
req
,
res
,
blockHead
)
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
require
.
NoError
(
t
,
err
)
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
blockHead
+=
1
// new block
require
.
NoError
(
t
,
err
)
cachedRes
,
err
=
cache
.
GetRPC
(
ctx
,
req
)
require
.
Nil
(
t
,
cachedRes
)
require
.
NoError
(
t
,
err
)
})
require
.
Nil
(
t
,
cachedRes
)
t
.
Run
(
"pending block"
,
func
(
t
*
testing
.
T
)
{
blockHead
=
0x100
req
:=
&
RPCReq
{
JSONRPC
:
"2.0"
,
Method
:
"eth_call"
,
Params
:
[]
byte
(
`[{"to": "0xDEADBEEF", "data": "0x1"}, "pending"]`
),
ID
:
ID
,
}
cache
:=
makeCache
()
require
.
NoError
(
t
,
cache
.
PutRPC
(
ctx
,
req
,
res
))
cachedRes
,
err
:=
cache
.
GetRPC
(
ctx
,
req
)
require
.
NoError
(
t
,
err
)
require
.
Nil
(
t
,
cachedRes
)
})
}
}
go/proxyd/config.go
View file @
2b34a1ee
...
@@ -15,8 +15,9 @@ type ServerConfig struct {
...
@@ -15,8 +15,9 @@ type ServerConfig struct {
}
}
type
CacheConfig
struct
{
type
CacheConfig
struct
{
Enabled
bool
`toml:"enabled"`
Enabled
bool
`toml:"enabled"`
BlockSyncRPCURL
string
`toml:"block_sync_rpc_url"`
BlockSyncRPCURL
string
`toml:"block_sync_rpc_url"`
NumBlockConfirmations
int
`toml:"num_block_confirmations"`
}
}
type
RedisConfig
struct
{
type
RedisConfig
struct
{
...
...
go/proxyd/lvc.go
View file @
2b34a1ee
...
@@ -2,7 +2,6 @@ package proxyd
...
@@ -2,7 +2,6 @@ package proxyd
import
(
import
(
"context"
"context"
"sync"
"time"
"time"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient"
...
@@ -11,20 +10,21 @@ import (
...
@@ -11,20 +10,21 @@ import (
const
cacheSyncRate
=
1
*
time
.
Second
const
cacheSyncRate
=
1
*
time
.
Second
type
lvcUpdateFn
func
(
context
.
Context
,
*
ethclient
.
Client
)
(
interface
{}
,
error
)
type
lvcUpdateFn
func
(
context
.
Context
,
*
ethclient
.
Client
)
(
string
,
error
)
type
EthLastValueCache
struct
{
type
EthLastValueCache
struct
{
client
*
ethclient
.
Client
client
*
ethclient
.
Client
cache
Cache
key
string
updater
lvcUpdateFn
updater
lvcUpdateFn
quit
chan
struct
{}
quit
chan
struct
{}
mutex
sync
.
RWMutex
value
interface
{}
}
}
func
newLVC
(
client
*
ethclient
.
Client
,
updater
lvcUpdateFn
)
*
EthLastValueCache
{
func
newLVC
(
client
*
ethclient
.
Client
,
cache
Cache
,
cacheKey
string
,
updater
lvcUpdateFn
)
*
EthLastValueCache
{
return
&
EthLastValueCache
{
return
&
EthLastValueCache
{
client
:
client
,
client
:
client
,
cache
:
cache
,
key
:
cacheKey
,
updater
:
updater
,
updater
:
updater
,
quit
:
make
(
chan
struct
{}),
quit
:
make
(
chan
struct
{}),
}
}
...
@@ -38,15 +38,18 @@ func (h *EthLastValueCache) Start() {
...
@@ -38,15 +38,18 @@ func (h *EthLastValueCache) Start() {
for
{
for
{
select
{
select
{
case
<-
ticker
.
C
:
case
<-
ticker
.
C
:
lvcPollTimeGauge
.
WithLabelValues
(
h
.
key
)
.
SetToCurrentTime
()
value
,
err
:=
h
.
getUpdate
()
value
,
err
:=
h
.
getUpdate
()
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Error
(
"error retrieving latest value"
,
"error"
,
err
)
log
.
Error
(
"error retrieving latest value"
,
"
key"
,
h
.
key
,
"
error"
,
err
)
continue
continue
}
}
log
.
Trace
(
"polling latest value"
,
"value"
,
value
)
log
.
Trace
(
"polling latest value"
,
"value"
,
value
)
h
.
mutex
.
Lock
()
h
.
value
=
value
if
err
:=
h
.
cache
.
Put
(
context
.
Background
(),
h
.
key
,
value
);
err
!=
nil
{
h
.
mutex
.
Unlock
()
log
.
Error
(
"error writing last value to cache"
,
"key"
,
h
.
key
,
"error"
,
err
)
}
case
<-
h
.
quit
:
case
<-
h
.
quit
:
return
return
...
@@ -55,31 +58,30 @@ func (h *EthLastValueCache) Start() {
...
@@ -55,31 +58,30 @@ func (h *EthLastValueCache) Start() {
}()
}()
}
}
func
(
h
*
EthLastValueCache
)
getUpdate
()
(
interface
{}
,
error
)
{
func
(
h
*
EthLastValueCache
)
getUpdate
()
(
string
,
error
)
{
const
maxRetries
=
5
const
maxRetries
=
5
var
err
error
var
err
error
for
i
:=
0
;
i
<=
maxRetries
;
i
++
{
for
i
:=
0
;
i
<=
maxRetries
;
i
++
{
var
value
interface
{}
var
value
string
value
,
err
=
h
.
updater
(
context
.
Background
(),
h
.
client
)
value
,
err
=
h
.
updater
(
context
.
Background
(),
h
.
client
)
if
err
!=
nil
{
if
err
!=
nil
{
backoff
:=
calcBackoff
(
i
)
backoff
:=
calcBackoff
(
i
)
log
.
Warn
(
"http operation failed. retrying..."
,
"error"
,
err
,
"backoff"
,
backoff
)
log
.
Warn
(
"http operation failed. retrying..."
,
"error"
,
err
,
"backoff"
,
backoff
)
lvcErrorsTotal
.
WithLabelValues
(
h
.
key
)
.
Inc
()
time
.
Sleep
(
backoff
)
time
.
Sleep
(
backoff
)
continue
continue
}
}
return
value
,
nil
return
value
,
nil
}
}
return
0
,
wrapErr
(
err
,
"exceeded retries"
)
return
""
,
wrapErr
(
err
,
"exceeded retries"
)
}
}
func
(
h
*
EthLastValueCache
)
Stop
()
{
func
(
h
*
EthLastValueCache
)
Stop
()
{
close
(
h
.
quit
)
close
(
h
.
quit
)
}
}
func
(
h
*
EthLastValueCache
)
Read
()
interface
{}
{
func
(
h
*
EthLastValueCache
)
Read
(
ctx
context
.
Context
)
(
string
,
error
)
{
h
.
mutex
.
RLock
()
return
h
.
cache
.
Get
(
ctx
,
h
.
key
)
defer
h
.
mutex
.
RUnlock
()
return
h
.
value
}
}
go/proxyd/methods.go
View file @
2b34a1ee
...
@@ -6,23 +6,17 @@ import (
...
@@ -6,23 +6,17 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"sync"
"sync"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/snappy"
)
)
const
numBlockConfirmations
=
50
var
(
var
(
cacheTTL
=
5
*
time
.
Second
errInvalidRPCParams
=
errors
.
New
(
"invalid RPC params"
)
errInvalidRPCParams
=
errors
.
New
(
"invalid RPC params"
)
)
)
type
RPCMethodHandler
interface
{
type
RPCMethodHandler
interface
{
GetRPCMethod
(
context
.
Context
,
*
RPCReq
)
(
*
RPCRes
,
error
)
GetRPCMethod
(
context
.
Context
,
*
RPCReq
)
(
*
RPCRes
,
error
)
PutRPCMethod
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
,
uint64
)
error
PutRPCMethod
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
)
error
}
}
type
StaticMethodHandler
struct
{
type
StaticMethodHandler
struct
{
...
@@ -42,7 +36,7 @@ func (e *StaticMethodHandler) GetRPCMethod(ctx context.Context, req *RPCReq) (*R
...
@@ -42,7 +36,7 @@ func (e *StaticMethodHandler) GetRPCMethod(ctx context.Context, req *RPCReq) (*R
return
cache
,
nil
return
cache
,
nil
}
}
func
(
e
*
StaticMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
,
blockNumSync
uint64
)
error
{
func
(
e
*
StaticMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
e
.
m
.
Lock
()
e
.
m
.
Lock
()
if
e
.
cache
==
nil
{
if
e
.
cache
==
nil
{
e
.
cache
=
copyRes
(
res
)
e
.
cache
=
copyRes
(
res
)
...
@@ -52,8 +46,9 @@ func (e *StaticMethodHandler) PutRPCMethod(ctx context.Context, req *RPCReq, res
...
@@ -52,8 +46,9 @@ func (e *StaticMethodHandler) PutRPCMethod(ctx context.Context, req *RPCReq, res
}
}
type
EthGetBlockByNumberMethodHandler
struct
{
type
EthGetBlockByNumberMethodHandler
struct
{
cache
Cache
cache
Cache
getLatestBlockNumFn
GetLatestBlockNumFn
getLatestBlockNumFn
GetLatestBlockNumFn
numBlockConfirmations
int
}
}
func
(
e
*
EthGetBlockByNumberMethodHandler
)
cacheKey
(
req
*
RPCReq
)
string
{
func
(
e
*
EthGetBlockByNumberMethodHandler
)
cacheKey
(
req
*
RPCReq
)
string
{
...
@@ -80,7 +75,7 @@ func (e *EthGetBlockByNumberMethodHandler) GetRPCMethod(ctx context.Context, req
...
@@ -80,7 +75,7 @@ func (e *EthGetBlockByNumberMethodHandler) GetRPCMethod(ctx context.Context, req
return
getImmutableRPCResponse
(
ctx
,
e
.
cache
,
key
,
req
)
return
getImmutableRPCResponse
(
ctx
,
e
.
cache
,
key
,
req
)
}
}
func
(
e
*
EthGetBlockByNumberMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
,
blockNumberSync
uint64
)
error
{
func
(
e
*
EthGetBlockByNumberMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
if
ok
,
err
:=
e
.
cacheable
(
req
);
!
ok
||
err
!=
nil
{
if
ok
,
err
:=
e
.
cacheable
(
req
);
!
ok
||
err
!=
nil
{
return
err
return
err
}
}
...
@@ -101,7 +96,7 @@ func (e *EthGetBlockByNumberMethodHandler) PutRPCMethod(ctx context.Context, req
...
@@ -101,7 +96,7 @@ func (e *EthGetBlockByNumberMethodHandler) PutRPCMethod(ctx context.Context, req
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
curBlock
<=
blockNum
+
numBlockConfirmations
{
if
curBlock
<=
blockNum
+
uint64
(
e
.
numBlockConfirmations
)
{
return
nil
return
nil
}
}
}
}
...
@@ -111,8 +106,9 @@ func (e *EthGetBlockByNumberMethodHandler) PutRPCMethod(ctx context.Context, req
...
@@ -111,8 +106,9 @@ func (e *EthGetBlockByNumberMethodHandler) PutRPCMethod(ctx context.Context, req
}
}
type
EthGetBlockRangeMethodHandler
struct
{
type
EthGetBlockRangeMethodHandler
struct
{
cache
Cache
cache
Cache
getLatestBlockNumFn
GetLatestBlockNumFn
getLatestBlockNumFn
GetLatestBlockNumFn
numBlockConfirmations
int
}
}
func
(
e
*
EthGetBlockRangeMethodHandler
)
cacheKey
(
req
*
RPCReq
)
string
{
func
(
e
*
EthGetBlockRangeMethodHandler
)
cacheKey
(
req
*
RPCReq
)
string
{
...
@@ -140,7 +136,7 @@ func (e *EthGetBlockRangeMethodHandler) GetRPCMethod(ctx context.Context, req *R
...
@@ -140,7 +136,7 @@ func (e *EthGetBlockRangeMethodHandler) GetRPCMethod(ctx context.Context, req *R
return
getImmutableRPCResponse
(
ctx
,
e
.
cache
,
key
,
req
)
return
getImmutableRPCResponse
(
ctx
,
e
.
cache
,
key
,
req
)
}
}
func
(
e
*
EthGetBlockRangeMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
,
blockNumberSync
uint64
)
error
{
func
(
e
*
EthGetBlockRangeMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
if
ok
,
err
:=
e
.
cacheable
(
req
);
!
ok
||
err
!=
nil
{
if
ok
,
err
:=
e
.
cacheable
(
req
);
!
ok
||
err
!=
nil
{
return
err
return
err
}
}
...
@@ -158,7 +154,7 @@ func (e *EthGetBlockRangeMethodHandler) PutRPCMethod(ctx context.Context, req *R
...
@@ -158,7 +154,7 @@ func (e *EthGetBlockRangeMethodHandler) PutRPCMethod(ctx context.Context, req *R
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
curBlock
<=
startNum
+
numBlockConfirmations
{
if
curBlock
<=
startNum
+
uint64
(
e
.
numBlockConfirmations
)
{
return
nil
return
nil
}
}
}
}
...
@@ -167,7 +163,7 @@ func (e *EthGetBlockRangeMethodHandler) PutRPCMethod(ctx context.Context, req *R
...
@@ -167,7 +163,7 @@ func (e *EthGetBlockRangeMethodHandler) PutRPCMethod(ctx context.Context, req *R
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
if
curBlock
<=
endNum
+
numBlockConfirmations
{
if
curBlock
<=
endNum
+
uint64
(
e
.
numBlockConfirmations
)
{
return
nil
return
nil
}
}
}
}
...
@@ -177,58 +173,66 @@ func (e *EthGetBlockRangeMethodHandler) PutRPCMethod(ctx context.Context, req *R
...
@@ -177,58 +173,66 @@ func (e *EthGetBlockRangeMethodHandler) PutRPCMethod(ctx context.Context, req *R
}
}
type
EthCallMethodHandler
struct
{
type
EthCallMethodHandler
struct
{
cache
Cache
cache
Cache
getLatestBlockNumFn
GetLatestBlockNumFn
getLatestBlockNumFn
GetLatestBlockNumFn
numBlockConfirmations
int
}
}
func
(
e
*
EthCallMethodHandler
)
cacheKey
(
req
*
RPCReq
)
string
{
func
(
e
*
EthCallMethodHandler
)
cacheable
(
params
*
ethCallParams
,
blockTag
string
)
bool
{
type
ethCallParams
struct
{
if
isBlockDependentParam
(
blockTag
)
{
From
string
`json:"from"`
return
false
To
string
`json:"to"`
Gas
string
`json:"gas"`
GasPrice
string
`json:"gasPrice"`
Value
string
`json:"value"`
Data
string
`json:"data"`
}
var
input
[]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
req
.
Params
,
&
input
);
err
!=
nil
{
return
""
}
if
len
(
input
)
!=
2
{
return
""
}
var
blockTag
string
if
err
:=
json
.
Unmarshal
(
input
[
1
],
&
blockTag
);
err
!=
nil
{
return
""
}
// The eth_call cache is used as a LVC. Only the latest calls are cached as these are used the most
if
blockTag
!=
"latest"
{
return
""
}
var
params
ethCallParams
if
err
:=
json
.
Unmarshal
(
input
[
0
],
&
params
);
err
!=
nil
{
return
""
}
}
if
params
.
From
!=
""
||
params
.
Gas
!=
""
{
if
params
.
From
!=
""
||
params
.
Gas
!=
""
{
return
""
return
false
}
}
if
params
.
Value
!=
""
&&
params
.
Value
!=
"0x0"
{
if
params
.
Value
!=
""
&&
params
.
Value
!=
"0x0"
{
return
""
return
false
}
}
// ensure the order is consistent
return
true
keyParams
:=
fmt
.
Sprintf
(
"%s:%s"
,
params
.
To
,
params
.
Data
)
}
func
(
e
*
EthCallMethodHandler
)
cacheKey
(
params
*
ethCallParams
,
blockTag
string
)
string
{
keyParams
:=
fmt
.
Sprintf
(
"%s:%s:%s"
,
params
.
To
,
params
.
Data
,
blockTag
)
return
fmt
.
Sprintf
(
"method:eth_call:%s"
,
keyParams
)
return
fmt
.
Sprintf
(
"method:eth_call:%s"
,
keyParams
)
}
}
func
(
e
*
EthCallMethodHandler
)
GetRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
func
(
e
*
EthCallMethodHandler
)
GetRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
key
:=
e
.
cacheKey
(
req
)
params
,
blockTag
,
err
:=
decodeEthCallParams
(
req
)
return
getBlockDependentCachedRPCResponse
(
ctx
,
e
.
cache
,
e
.
getLatestBlockNumFn
,
key
,
req
)
if
err
!=
nil
{
return
nil
,
err
}
if
!
e
.
cacheable
(
params
,
blockTag
)
{
return
nil
,
nil
}
key
:=
e
.
cacheKey
(
params
,
blockTag
)
return
getImmutableRPCResponse
(
ctx
,
e
.
cache
,
key
,
req
)
}
}
func
(
e
*
EthCallMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
,
blockNumberSync
uint64
)
error
{
func
(
e
*
EthCallMethodHandler
)
PutRPCMethod
(
ctx
context
.
Context
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
key
:=
e
.
cacheKey
(
req
)
params
,
blockTag
,
err
:=
decodeEthCallParams
(
req
)
return
putBlockDependentCachedRPCResponse
(
ctx
,
e
.
cache
,
key
,
res
,
blockNumberSync
)
if
err
!=
nil
{
return
err
}
if
!
e
.
cacheable
(
params
,
blockTag
)
{
return
nil
}
if
blockTag
!=
"earliest"
{
curBlock
,
err
:=
e
.
getLatestBlockNumFn
(
ctx
)
if
err
!=
nil
{
return
err
}
blockNum
,
err
:=
decodeBlockInput
(
blockTag
)
if
err
!=
nil
{
return
err
}
if
curBlock
<=
blockNum
+
uint64
(
e
.
numBlockConfirmations
)
{
return
nil
}
}
key
:=
e
.
cacheKey
(
params
,
blockTag
)
return
putImmutableRPCResponse
(
ctx
,
e
.
cache
,
key
,
req
,
res
)
}
}
type
EthBlockNumberMethodHandler
struct
{
type
EthBlockNumberMethodHandler
struct
{
...
@@ -243,7 +247,7 @@ func (e *EthBlockNumberMethodHandler) GetRPCMethod(ctx context.Context, req *RPC
...
@@ -243,7 +247,7 @@ func (e *EthBlockNumberMethodHandler) GetRPCMethod(ctx context.Context, req *RPC
return
makeRPCRes
(
req
,
hexutil
.
EncodeUint64
(
blockNum
)),
nil
return
makeRPCRes
(
req
,
hexutil
.
EncodeUint64
(
blockNum
)),
nil
}
}
func
(
e
*
EthBlockNumberMethodHandler
)
PutRPCMethod
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
,
uint64
)
error
{
func
(
e
*
EthBlockNumberMethodHandler
)
PutRPCMethod
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
)
error
{
return
nil
return
nil
}
}
...
@@ -259,7 +263,7 @@ func (e *EthGasPriceMethodHandler) GetRPCMethod(ctx context.Context, req *RPCReq
...
@@ -259,7 +263,7 @@ func (e *EthGasPriceMethodHandler) GetRPCMethod(ctx context.Context, req *RPCReq
return
makeRPCRes
(
req
,
hexutil
.
EncodeUint64
(
gasPrice
)),
nil
return
makeRPCRes
(
req
,
hexutil
.
EncodeUint64
(
gasPrice
)),
nil
}
}
func
(
e
*
EthGasPriceMethodHandler
)
PutRPCMethod
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
,
uint64
)
error
{
func
(
e
*
EthGasPriceMethodHandler
)
PutRPCMethod
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
)
error
{
return
nil
return
nil
}
}
...
@@ -319,6 +323,34 @@ func decodeBlockInput(input string) (uint64, error) {
...
@@ -319,6 +323,34 @@ func decodeBlockInput(input string) (uint64, error) {
return
hexutil
.
DecodeUint64
(
input
)
return
hexutil
.
DecodeUint64
(
input
)
}
}
type
ethCallParams
struct
{
From
string
`json:"from"`
To
string
`json:"to"`
Gas
string
`json:"gas"`
GasPrice
string
`json:"gasPrice"`
Value
string
`json:"value"`
Data
string
`json:"data"`
}
func
decodeEthCallParams
(
req
*
RPCReq
)
(
*
ethCallParams
,
string
,
error
)
{
var
input
[]
json
.
RawMessage
if
err
:=
json
.
Unmarshal
(
req
.
Params
,
&
input
);
err
!=
nil
{
return
nil
,
""
,
err
}
if
len
(
input
)
!=
2
{
return
nil
,
""
,
fmt
.
Errorf
(
"invalid eth_call parameters"
)
}
params
:=
new
(
ethCallParams
)
if
err
:=
json
.
Unmarshal
(
input
[
0
],
params
);
err
!=
nil
{
return
nil
,
""
,
err
}
var
blockTag
string
if
err
:=
json
.
Unmarshal
(
input
[
1
],
&
blockTag
);
err
!=
nil
{
return
nil
,
""
,
err
}
return
params
,
blockTag
,
nil
}
func
validBlockInput
(
input
string
)
bool
{
func
validBlockInput
(
input
string
)
bool
{
if
input
==
"earliest"
||
input
==
"pending"
||
input
==
"latest"
{
if
input
==
"earliest"
||
input
==
"pending"
||
input
==
"latest"
{
return
true
return
true
...
@@ -355,100 +387,30 @@ func copyRes(res *RPCRes) *RPCRes {
...
@@ -355,100 +387,30 @@ func copyRes(res *RPCRes) *RPCRes {
}
}
}
}
type
CachedRPC
struct
{
BlockNum
uint64
`json:"blockNum"`
Res
*
RPCRes
`json:"res"`
Expiration
int64
`json:"expiration"`
// in millis since epoch
}
func
(
c
*
CachedRPC
)
Encode
()
[]
byte
{
return
mustMarshalJSON
(
c
)
}
func
(
c
*
CachedRPC
)
Decode
(
b
[]
byte
)
error
{
return
json
.
Unmarshal
(
b
,
c
)
}
func
(
c
*
CachedRPC
)
ExpirationTime
()
time
.
Time
{
return
time
.
Unix
(
0
,
c
.
Expiration
*
int64
(
time
.
Millisecond
))
}
func
getImmutableRPCResponse
(
ctx
context
.
Context
,
cache
Cache
,
key
string
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
func
getImmutableRPCResponse
(
ctx
context
.
Context
,
cache
Cache
,
key
string
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
encodedV
al
,
err
:=
cache
.
Get
(
ctx
,
key
)
v
al
,
err
:=
cache
.
Get
(
ctx
,
key
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
if
encodedV
al
==
""
{
if
v
al
==
""
{
return
nil
,
nil
return
nil
,
nil
}
}
val
,
err
:=
snappy
.
Decode
(
nil
,
[]
byte
(
encodedVal
))
if
err
!=
nil
{
return
nil
,
err
}
res
:=
new
(
RPCRes
)
var
result
interface
{}
if
err
:=
json
.
Unmarshal
(
val
,
res
);
err
!=
nil
{
if
err
:=
json
.
Unmarshal
(
[]
byte
(
val
),
&
result
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
res
.
ID
=
req
.
ID
return
&
RPCRes
{
return
res
,
nil
JSONRPC
:
req
.
JSONRPC
,
Result
:
result
,
ID
:
req
.
ID
,
},
nil
}
}
func
putImmutableRPCResponse
(
ctx
context
.
Context
,
cache
Cache
,
key
string
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
func
putImmutableRPCResponse
(
ctx
context
.
Context
,
cache
Cache
,
key
string
,
req
*
RPCReq
,
res
*
RPCRes
)
error
{
if
key
==
""
{
if
key
==
""
{
return
nil
return
nil
}
}
val
:=
mustMarshalJSON
(
res
)
val
:=
mustMarshalJSON
(
res
.
Result
)
encodedVal
:=
snappy
.
Encode
(
nil
,
val
)
return
cache
.
Put
(
ctx
,
key
,
string
(
val
))
return
cache
.
Put
(
ctx
,
key
,
string
(
encodedVal
))
}
func
getBlockDependentCachedRPCResponse
(
ctx
context
.
Context
,
cache
Cache
,
getLatestBlockNumFn
GetLatestBlockNumFn
,
key
string
,
req
*
RPCReq
)
(
*
RPCRes
,
error
)
{
encodedVal
,
err
:=
cache
.
Get
(
ctx
,
key
)
if
err
!=
nil
{
return
nil
,
err
}
if
encodedVal
==
""
{
return
nil
,
nil
}
val
,
err
:=
snappy
.
Decode
(
nil
,
[]
byte
(
encodedVal
))
if
err
!=
nil
{
return
nil
,
err
}
item
:=
new
(
CachedRPC
)
if
err
:=
json
.
Unmarshal
(
val
,
item
);
err
!=
nil
{
return
nil
,
err
}
curBlockNum
,
err
:=
getLatestBlockNumFn
(
ctx
)
if
err
!=
nil
{
return
nil
,
err
}
expired
:=
time
.
Now
()
.
After
(
item
.
ExpirationTime
())
if
curBlockNum
>
item
.
BlockNum
&&
expired
{
// Remove the key now to avoid stale entries from biasing the LRU list
// TODO: be careful removing keys once there are multiple proxyd instances
return
nil
,
cache
.
Remove
(
ctx
,
key
)
}
else
if
curBlockNum
<
item
.
BlockNum
{
/* desync: reorgs, backend failover, slow sequencer I/O, etc */
return
nil
,
nil
}
res
:=
item
.
Res
res
.
ID
=
req
.
ID
return
res
,
nil
}
func
putBlockDependentCachedRPCResponse
(
ctx
context
.
Context
,
cache
Cache
,
key
string
,
res
*
RPCRes
,
blockNumberSync
uint64
)
error
{
if
key
==
""
{
return
nil
}
item
:=
CachedRPC
{
BlockNum
:
blockNumberSync
,
Res
:
res
,
Expiration
:
time
.
Now
()
.
Add
(
cacheTTL
)
.
UnixNano
()
/
int64
(
time
.
Millisecond
),
}
val
:=
item
.
Encode
()
encodedVal
:=
snappy
.
Encode
(
nil
,
val
)
return
cache
.
Put
(
ctx
,
key
,
string
(
encodedVal
))
}
}
go/proxyd/metrics.go
View file @
2b34a1ee
...
@@ -145,7 +145,7 @@ var (
...
@@ -145,7 +145,7 @@ var (
requestPayloadSizesGauge
=
promauto
.
NewHistogramVec
(
prometheus
.
HistogramOpts
{
requestPayloadSizesGauge
=
promauto
.
NewHistogramVec
(
prometheus
.
HistogramOpts
{
Namespace
:
MetricsNamespace
,
Namespace
:
MetricsNamespace
,
Name
:
"request_payload_sizes"
,
Name
:
"request_payload_sizes"
,
Help
:
"
Gauge
of client request payload sizes."
,
Help
:
"
Histogram
of client request payload sizes."
,
Buckets
:
PayloadSizeBuckets
,
Buckets
:
PayloadSizeBuckets
,
},
[]
string
{
},
[]
string
{
"auth"
,
"auth"
,
...
@@ -154,7 +154,7 @@ var (
...
@@ -154,7 +154,7 @@ var (
responsePayloadSizesGauge
=
promauto
.
NewHistogramVec
(
prometheus
.
HistogramOpts
{
responsePayloadSizesGauge
=
promauto
.
NewHistogramVec
(
prometheus
.
HistogramOpts
{
Namespace
:
MetricsNamespace
,
Namespace
:
MetricsNamespace
,
Name
:
"response_payload_sizes"
,
Name
:
"response_payload_sizes"
,
Help
:
"
Gauge
of client response payload sizes."
,
Help
:
"
Histogram
of client response payload sizes."
,
Buckets
:
PayloadSizeBuckets
,
Buckets
:
PayloadSizeBuckets
,
},
[]
string
{
},
[]
string
{
"auth"
,
"auth"
,
...
@@ -176,6 +176,22 @@ var (
...
@@ -176,6 +176,22 @@ var (
"method"
,
"method"
,
})
})
lvcErrorsTotal
=
promauto
.
NewCounterVec
(
prometheus
.
CounterOpts
{
Namespace
:
MetricsNamespace
,
Name
:
"lvc_errors_total"
,
Help
:
"Count of lvc errors."
,
},
[]
string
{
"key"
,
})
lvcPollTimeGauge
=
promauto
.
NewGaugeVec
(
prometheus
.
GaugeOpts
{
Namespace
:
MetricsNamespace
,
Name
:
"lvc_poll_time_gauge"
,
Help
:
"Gauge of lvc poll time."
,
},
[]
string
{
"key"
,
})
rpcSpecialErrors
=
[]
string
{
rpcSpecialErrors
=
[]
string
{
"nonce too low"
,
"nonce too low"
,
"gas price too high"
,
"gas price too high"
,
...
...
go/proxyd/proxyd.go
View file @
2b34a1ee
...
@@ -5,9 +5,9 @@ import (
...
@@ -5,9 +5,9 @@ import (
"crypto/tls"
"crypto/tls"
"errors"
"errors"
"fmt"
"fmt"
"math/big"
"net/http"
"net/http"
"os"
"os"
"strconv"
"time"
"time"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient"
...
@@ -111,12 +111,7 @@ func Start(config *Config) (func(), error) {
...
@@ -111,12 +111,7 @@ func Start(config *Config) (func(), error) {
opts
=
append
(
opts
,
WithStrippedTrailingXFF
())
opts
=
append
(
opts
,
WithStrippedTrailingXFF
())
}
}
opts
=
append
(
opts
,
WithProxydIP
(
os
.
Getenv
(
"PROXYD_IP"
)))
opts
=
append
(
opts
,
WithProxydIP
(
os
.
Getenv
(
"PROXYD_IP"
)))
back
,
err
:=
NewBackend
(
name
,
rpcURL
,
wsURL
,
lim
,
opts
...
)
back
:=
NewBackend
(
name
,
rpcURL
,
wsURL
,
lim
,
opts
...
)
if
err
!=
nil
{
return
nil
,
err
}
back
.
Start
()
defer
back
.
Stop
()
backendNames
=
append
(
backendNames
,
name
)
backendNames
=
append
(
backendNames
,
name
)
backendsByName
[
name
]
=
back
backendsByName
[
name
]
=
back
log
.
Info
(
"configured backend"
,
"name"
,
name
,
"rpc_url"
,
rpcURL
,
"ws_url"
,
wsURL
)
log
.
Info
(
"configured backend"
,
"name"
,
name
,
"rpc_url"
,
rpcURL
,
"ws_url"
,
wsURL
)
...
@@ -169,9 +164,18 @@ func Start(config *Config) (func(), error) {
...
@@ -169,9 +164,18 @@ func Start(config *Config) (func(), error) {
}
}
}
}
var
rpcCache
RPCCache
var
(
stopLVCs
:=
make
(
chan
struct
{})
rpcCache
RPCCache
blockNumLVC
*
EthLastValueCache
gasPriceLVC
*
EthLastValueCache
)
if
config
.
Cache
.
Enabled
{
if
config
.
Cache
.
Enabled
{
var
(
cache
Cache
blockNumFn
GetLatestBlockNumFn
gasPriceFn
GetLatestGasPriceFn
)
if
config
.
Cache
.
BlockSyncRPCURL
==
""
{
if
config
.
Cache
.
BlockSyncRPCURL
==
""
{
return
nil
,
fmt
.
Errorf
(
"block sync node required for caching"
)
return
nil
,
fmt
.
Errorf
(
"block sync node required for caching"
)
}
}
...
@@ -180,7 +184,6 @@ func Start(config *Config) (func(), error) {
...
@@ -180,7 +184,6 @@ func Start(config *Config) (func(), error) {
return
nil
,
err
return
nil
,
err
}
}
var
cache
Cache
if
redisURL
!=
""
{
if
redisURL
!=
""
{
if
cache
,
err
=
newRedisCache
(
redisURL
);
err
!=
nil
{
if
cache
,
err
=
newRedisCache
(
redisURL
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -195,9 +198,10 @@ func Start(config *Config) (func(), error) {
...
@@ -195,9 +198,10 @@ func Start(config *Config) (func(), error) {
return
nil
,
err
return
nil
,
err
}
}
defer
ethClient
.
Close
()
defer
ethClient
.
Close
()
blockNumFn
:=
makeGetLatestBlockNumFn
(
ethClient
,
stopLVCs
)
gasPriceFn
:=
makeGetLatestGasPriceFn
(
ethClient
,
stopLVCs
)
blockNumLVC
,
blockNumFn
=
makeGetLatestBlockNumFn
(
ethClient
,
cache
)
rpcCache
=
newRPCCache
(
cache
,
blockNumFn
,
gasPriceFn
)
gasPriceLVC
,
gasPriceFn
=
makeGetLatestGasPriceFn
(
ethClient
,
cache
)
rpcCache
=
newRPCCache
(
newCacheWithCompression
(
cache
),
blockNumFn
,
gasPriceFn
,
config
.
Cache
.
NumBlockConfirmations
)
}
}
srv
:=
NewServer
(
srv
:=
NewServer
(
...
@@ -250,8 +254,12 @@ func Start(config *Config) (func(), error) {
...
@@ -250,8 +254,12 @@ func Start(config *Config) (func(), error) {
return
func
()
{
return
func
()
{
log
.
Info
(
"shutting down proxyd"
)
log
.
Info
(
"shutting down proxyd"
)
// TODO(inphi): Stop LVCs here
if
blockNumLVC
!=
nil
{
close
(
stopLVCs
)
blockNumLVC
.
Stop
()
}
if
gasPriceLVC
!=
nil
{
gasPriceLVC
.
Stop
()
}
srv
.
Shutdown
()
srv
.
Shutdown
()
if
err
:=
lim
.
FlushBackendWSConns
(
backendNames
);
err
!=
nil
{
if
err
:=
lim
.
FlushBackendWSConns
(
backendNames
);
err
!=
nil
{
log
.
Error
(
"error flushing backend ws conns"
,
"err"
,
err
)
log
.
Error
(
"error flushing backend ws conns"
,
"err"
,
err
)
...
@@ -285,38 +293,38 @@ func configureBackendTLS(cfg *BackendConfig) (*tls.Config, error) {
...
@@ -285,38 +293,38 @@ func configureBackendTLS(cfg *BackendConfig) (*tls.Config, error) {
return
tlsConfig
,
nil
return
tlsConfig
,
nil
}
}
func
makeGetLatestBlockNumFn
(
client
*
ethclient
.
Client
,
quit
<-
chan
struct
{})
GetLatestBlockNumFn
{
func
makeUint64LastValueFn
(
client
*
ethclient
.
Client
,
cache
Cache
,
key
string
,
updater
lvcUpdateFn
)
(
*
EthLastValueCache
,
func
(
context
.
Context
)
(
uint64
,
error
))
{
lvc
:=
newLVC
(
client
,
func
(
ctx
context
.
Context
,
c
*
ethclient
.
Client
)
(
interface
{},
error
)
{
lvc
:=
newLVC
(
client
,
cache
,
key
,
updater
)
return
c
.
BlockNumber
(
ctx
)
})
lvc
.
Start
()
lvc
.
Start
()
go
func
()
{
return
lvc
,
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
<-
quit
value
,
err
:=
lvc
.
Read
(
ctx
)
lvc
.
Stop
()
if
err
!=
nil
{
}()
return
0
,
err
return
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
}
value
:=
lvc
.
Read
()
if
value
==
""
{
if
value
==
nil
{
return
0
,
fmt
.
Errorf
(
"%s is unavailable"
,
key
)
return
0
,
fmt
.
Errorf
(
"block number is unavailable"
)
}
}
valueUint
,
err
:=
strconv
.
ParseUint
(
value
,
10
,
64
)
return
value
.
(
uint64
),
nil
if
err
!=
nil
{
return
0
,
err
}
return
valueUint
,
nil
}
}
}
}
func
makeGetLatestGasPriceFn
(
client
*
ethclient
.
Client
,
quit
<-
chan
struct
{})
GetLatestGasPriceFn
{
func
makeGetLatestBlockNumFn
(
client
*
ethclient
.
Client
,
cache
Cache
)
(
*
EthLastValueCache
,
GetLatestBlockNumFn
)
{
lvc
:=
newLVC
(
client
,
func
(
ctx
context
.
Context
,
c
*
ethclient
.
Client
)
(
interface
{},
error
)
{
return
makeUint64LastValueFn
(
client
,
cache
,
"lvc:block_number"
,
func
(
ctx
context
.
Context
,
c
*
ethclient
.
Client
)
(
string
,
error
)
{
return
c
.
SuggestGasPrice
(
ctx
)
blockNum
,
err
:=
c
.
BlockNumber
(
ctx
)
return
strconv
.
FormatUint
(
blockNum
,
10
),
err
})
}
func
makeGetLatestGasPriceFn
(
client
*
ethclient
.
Client
,
cache
Cache
)
(
*
EthLastValueCache
,
GetLatestGasPriceFn
)
{
return
makeUint64LastValueFn
(
client
,
cache
,
"lvc:gas_price"
,
func
(
ctx
context
.
Context
,
c
*
ethclient
.
Client
)
(
string
,
error
)
{
gasPrice
,
err
:=
c
.
SuggestGasPrice
(
ctx
)
if
err
!=
nil
{
return
""
,
err
}
return
gasPrice
.
String
(),
nil
})
})
lvc
.
Start
()
go
func
()
{
<-
lvc
.
quit
lvc
.
Stop
()
}()
return
func
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
value
:=
lvc
.
Read
()
if
value
==
nil
{
return
0
,
fmt
.
Errorf
(
"gas price is unavailable"
)
}
return
value
.
(
*
big
.
Int
)
.
Uint64
(),
nil
}
}
}
go/proxyd/server.go
View file @
2b34a1ee
...
@@ -218,9 +218,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
...
@@ -218,9 +218,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
return
backendRes
return
backendRes
}
}
// NOTE: We call into the specific backend here to ensure that the RPCRes is synchronized with the blockNum.
backendRes
,
err
=
s
.
backendGroups
[
group
]
.
Forward
(
ctx
,
req
)
var
blockNum
uint64
backendRes
,
blockNum
,
err
=
s
.
backendGroups
[
group
]
.
Forward
(
ctx
,
req
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Error
(
log
.
Error
(
"error forwarding RPC request"
,
"error forwarding RPC request"
,
...
@@ -232,7 +230,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
...
@@ -232,7 +230,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
}
}
if
backendRes
.
Error
==
nil
{
if
backendRes
.
Error
==
nil
{
if
err
=
s
.
cache
.
PutRPC
(
ctx
,
req
,
backendRes
,
blockNum
);
err
!=
nil
{
if
err
=
s
.
cache
.
PutRPC
(
ctx
,
req
,
backendRes
);
err
!=
nil
{
log
.
Warn
(
log
.
Warn
(
"cache put error"
,
"cache put error"
,
"req_id"
,
GetReqID
(
ctx
),
"req_id"
,
GetReqID
(
ctx
),
...
@@ -427,6 +425,6 @@ func (n *NoopRPCCache) GetRPC(context.Context, *RPCReq) (*RPCRes, error) {
...
@@ -427,6 +425,6 @@ func (n *NoopRPCCache) GetRPC(context.Context, *RPCReq) (*RPCRes, error) {
return
nil
,
nil
return
nil
,
nil
}
}
func
(
n
*
NoopRPCCache
)
PutRPC
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
,
uint64
)
error
{
func
(
n
*
NoopRPCCache
)
PutRPC
(
context
.
Context
,
*
RPCReq
,
*
RPCRes
)
error
{
return
nil
return
nil
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment