Skip to content

Commit

Permalink
server/asset/eth: increase poll interval for public hosts
Browse files Browse the repository at this point in the history
  • Loading branch information
chappjc committed Jan 26, 2023
1 parent e538e9f commit 6ae961e
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 8 deletions.
2 changes: 1 addition & 1 deletion dex/testing/eth/create-node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ chmod +x "${NODES_ROOT}/harness-ctl/${NAME}"

HTTP_OPT=""
if [ "$NAME" = "delta" ]; then
HTTP_OPT="--http --http.port ${DELTA_HTTP_PORT} --ws --ws.port ${DELTA_WS_PORT} --ws.api \"db,eth,net,web3,personal,txpool,admin\""
HTTP_OPT="--http --http.port ${DELTA_HTTP_PORT} --ws --ws.port ${DELTA_WS_PORT} --ws.api \"eth,net,web3,personal,txpool,admin\""
fi

# Write mine script if CHAIN_ADDRESS is present.
Expand Down
58 changes: 52 additions & 6 deletions server/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"errors"
"fmt"
"math/big"
"net"
"net/url"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -95,6 +97,8 @@ const (
BipID = 60
ethContractVersion = 0
version = 0

defaultBlockPollInterval = time.Second
)

var (
Expand All @@ -111,7 +115,7 @@ var (
// blockPollInterval is the delay between calls to bestBlockHash to check
// for new blocks. Modify at compile time via blockPollIntervalStr:
// go build -tags lgpl -ldflags "-X 'decred.org/dcrdex/server/asset/eth.blockPollIntervalStr=10s'"
blockPollInterval = time.Second
blockPollInterval = defaultBlockPollInterval
blockPollIntervalStr string
)

Expand Down Expand Up @@ -193,6 +197,10 @@ type baseBackend struct {
ctx context.Context
net dex.Network
node ethFetcher
// endpoint is supplied when creating the ethFetcher with newRPCClient, and
// it may be used for logging or modifying behavior such as RPC block
// polling frequency.
endpoint string

// bestHash caches the last know best block hash and height and is used to
// detect reorgs. Only accessed in Connect and poll which is synchronous so
Expand Down Expand Up @@ -248,7 +256,7 @@ var _ asset.AccountBalancer = (*ETHBackend)(nil)

// unconnectedETH returns a Backend without a node. The node should be set
// before use.
func unconnectedETH(logger dex.Logger, net dex.Network) (*ETHBackend, error) {
func unconnectedETH(logger dex.Logger, net dex.Network, endpoint string) (*ETHBackend, error) {
// TODO: At some point multiple contracts will need to be used, at
// least for transitory periods when updating the contract, and
// possibly a random contract setup, and so this section will need to
Expand All @@ -262,6 +270,7 @@ func unconnectedETH(logger dex.Logger, net dex.Network) (*ETHBackend, error) {
net: net,
baseLogger: logger,
tokens: make(map[uint32]*TokenBackend),
endpoint: endpoint,
},
log: logger.SubLogger("ETH"),
contractAddr: contractAddr,
Expand All @@ -280,7 +289,7 @@ func NewBackend(endpoint string, logger dex.Logger, net dex.Network) (*ETHBacken
endpoint = defaultIPC
}

eth, err := unconnectedETH(logger, net)
eth, err := unconnectedETH(logger, net, endpoint)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -744,9 +753,16 @@ func (eth *ETHBackend) run(ctx context.Context) {
// Shut down the RPC client on ctx.Done().
defer eth.shutdown()

eth.baseLogger.Infof("Starting ETH block polling with interval of %v",
blockPollInterval)
blockPoll := time.NewTicker(blockPollInterval)
// If blockPollInterval is the default, and the endpoint is likely a remote
// host or public RPC provider (with limits), increase the polling interval.
pollInterval := blockPollInterval
if pollInterval == defaultBlockPollInterval && !isPrivateHost(eth.endpoint) {
pollInterval *= 5
}
eth.baseLogger.Infof("Starting ETH block polling from %v with interval of %v",
eth.endpoint, pollInterval)

blockPoll := time.NewTicker(pollInterval)
defer blockPoll.Stop()

for {
Expand All @@ -758,3 +774,33 @@ func (eth *ETHBackend) run(ctx context.Context) {
}
}
}

// isPrivateHost indicates if the host in the provided link, hostport, or
// address is either a loopback or private (non-routable) IP address. If any
// parsing and lookups fail, it returns false. This helper may be used to
// determine if an endpoint is likely to be remote or a public provider.
func isPrivateHost(link string) bool {
if strings.HasSuffix(strings.ToLower(link), ".ipc") {
return true
}
addr := link
if u, err := url.Parse(link); err == nil {
if u.Host != "" { // not opaque
addr = u.Host
}
} // otherwise treat link as an address

host, _, err := net.SplitHostPort(addr)
if err != nil { // many possible reasons including no port, so let LookupIP try
host = addr
}

ips, err := net.LookupIP(host)
if err != nil {
return false
}
if len(ips) == 0 {
return false
}
return ips[0].IsPrivate() || ips[0].IsLoopback()
}
92 changes: 91 additions & 1 deletion server/asset/eth/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ func TestDecodeCoinID(t *testing.T) {

func TestRun(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
backend, err := unconnectedETH(tLogger, dex.Simnet)
backend, err := unconnectedETH(tLogger, dex.Simnet, "/local/geth.ipc")
if err != nil {
t.Fatalf("unconnectedETH error: %v", err)
}
Expand Down Expand Up @@ -925,3 +925,93 @@ func TestValidateSignature(t *testing.T) {
}
}
}

func Test_isPrivateHost(t *testing.T) {
tests := []struct {
name string
link string
want bool
}{
{
"ipc",
"/opt/geth/geth.ipc",
true,
},
{
"loopback",
"127.0.0.1",
true,
},
{
"localhost",
"localhost",
true,
},
{
"loopback url",
"http://127.0.0.1",
true,
},
{
"loopback url port",
"http://127.0.0.1:8555",
true,
},
{
"loopback v6 url port",
"http://[::1]:8555",
true,
},
{
"non-routable 10",
"http://10.0.0.1:8555",
true,
},
{
"non-routable 192",
"http://192.168.0.33:8555",
true,
},
{
"http routable",
"http://25.25.25.25:8555",
false,
},
{
"ws routable",
"ws://25.25.25.25:8555",
false,
}, {
"non-routable v6",
"fc00:ab::",
true,
},
{
"non-routable v6",
"http://[fc00:ab::]:8555",
true,
},
{
"routable v6",
"http://[2600:ab::]:8555",
false,
},
{
"routable real",
"https://rpc.ankr.com/eth_goerli",
false,
},
{
"lookup fail",
"https://asdfsadfsadfsadfsadfsadf.decred.org/adsf",
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isPrivateHost(tt.link); got != tt.want {
t.Errorf("isPrivateHost() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 6ae961e

Please sign in to comment.