aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/lint.sh8
-rw-r--r--.github/workflows/ci.yml14
-rw-r--r--.golangci.yml48
-rw-r--r--accounts/keystore/account_cache.go2
-rw-r--r--consensus/dummy/consensus.go3
-rw-r--r--core/state/statedb.go1
-rw-r--r--core/types/block.go2
-rw-r--r--core/vm/instructions.go1
-rw-r--r--coreth.go4
-rw-r--r--eth/backend.go25
-rw-r--r--eth/gasprice/gasprice.go2
-rw-r--r--eth/protocol.go221
-rw-r--r--ethclient/ethclient.go14
-rw-r--r--ethstats/ethstats.go785
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--miner/worker.go15
-rw-r--r--node/api.go1
-rw-r--r--node/config.go2
-rw-r--r--node/node.go1
-rw-r--r--notes/copied-list.txt3
-rw-r--r--notes/hacked-list.txt2
-rw-r--r--plugin/evm/block.go5
-rw-r--r--plugin/evm/error.go2
-rw-r--r--plugin/evm/export_tx_test.go4
-rw-r--r--plugin/evm/import_tx_test.go8
-rw-r--r--plugin/evm/service.go5
-rw-r--r--plugin/evm/vm.go181
-rw-r--r--plugin/evm/vm_test.go400
29 files changed, 639 insertions, 1126 deletions
diff --git a/.ci/lint.sh b/.ci/lint.sh
new file mode 100755
index 0000000..e5363a8
--- /dev/null
+++ b/.ci/lint.sh
@@ -0,0 +1,8 @@
+# binary will be $(go env GOPATH)/bin/golangci-lint
+curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0
+
+export PATH=$PATH:$(go env GOPATH)/bin
+
+golangci-lint --version
+
+golangci-lint run --max-same-issues 0
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 31ae101..c5ef50b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,6 +2,20 @@ name: CI
on: [pull_request, push]
jobs:
+ lint:
+ name: Lint Golang v${{ matrix.go }} (${{ matrix.os }})
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ go: ['1.15']
+ os: [ubuntu-18.04]
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-go@v1
+ with:
+ go-version: ${{ matrix.go }}
+ - run: .ci/lint.sh
+ shell: bash
test:
name: Golang v${{ matrix.go }} (${{ matrix.os }})
runs-on: ${{ matrix.os }}
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..d2a5e38
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,48 @@
+# This file configures github.com/golangci/golangci-lint.
+
+run:
+ timeout: 3m
+ tests: true
+ # default is true. Enables skipping of directories:
+ # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
+ skip-dirs-use-default: true
+ skip-files:
+ - core/genesis_alloc.go
+
+linters:
+ disable-all: true
+ enable:
+ - deadcode
+ - goconst
+ - goimports
+ - gosimple
+ - govet
+ - ineffassign
+ - misspell
+ - unconvert
+ - varcheck
+
+linters-settings:
+ gofmt:
+ simplify: true
+ goconst:
+ min-len: 3 # minimum length of string constant
+ min-occurrences: 6 # minimum number of occurrences
+
+issues:
+ exclude-rules:
+ - path: crypto/blake2b/
+ linters:
+ - deadcode
+ - path: crypto/bn256/cloudflare
+ linters:
+ - deadcode
+ - path: p2p/discv5/
+ linters:
+ - deadcode
+ - path: core/vm/instructions_test.go
+ linters:
+ - goconst
+ - path: cmd/faucet/
+ linters:
+ - deadcode
diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go
index 76bd552..7ae4d37 100644
--- a/accounts/keystore/account_cache.go
+++ b/accounts/keystore/account_cache.go
@@ -28,9 +28,9 @@ import (
"time"
"github.com/ava-labs/coreth/accounts"
+ mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
- mapset "github.com/deckarep/golang-set"
)
// Minimum amount of time between cache reloads. This limit applies if the platform does
diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go
index da63673..866ff97 100644
--- a/consensus/dummy/consensus.go
+++ b/consensus/dummy/consensus.go
@@ -3,11 +3,12 @@ package dummy
import (
"errors"
"fmt"
- "golang.org/x/crypto/sha3"
"math/big"
"runtime"
"time"
+ "golang.org/x/crypto/sha3"
+
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
diff --git a/core/state/statedb.go b/core/state/statedb.go
index c4d926d..f75428b 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -43,7 +43,6 @@ type revision struct {
var (
// emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
- zeroRoot = common.HexToHash("0000000000000000000000000000000000000000000000000000000000000000")
)
type proofList [][]byte
diff --git a/core/types/block.go b/core/types/block.go
index 8e23488..b293ae7 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -86,7 +86,7 @@ type Header struct {
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash"`
Nonce BlockNonce `json:"nonce"`
- ExtDataHash common.Hash `json:"extDataHash" gencodec:"required"`
+ ExtDataHash common.Hash `json:"extDataHash" gencodec:"required"`
}
// field type overrides for gencodec
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index e1eb25e..3e1da26 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -18,6 +18,7 @@ package vm
import (
"errors"
+
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ethereum/go-ethereum/common"
diff --git a/coreth.go b/coreth.go
index 351a14d..25e9da2 100644
--- a/coreth.go
+++ b/coreth.go
@@ -40,10 +40,6 @@ type ETHChain struct {
bcb *eth.BackendCallbacks
}
-func isLocalBlock(block *types.Block) bool {
- return false
-}
-
// NewETHChain creates an Ethereum blockchain with the given configs.
func NewETHChain(config *eth.Config, nodecfg *node.Config, etherBase *common.Address, chainDB ethdb.Database) *ETHChain {
if config == nil {
diff --git a/eth/backend.go b/eth/backend.go
index 728ec4d..9fb8f38 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -23,7 +23,6 @@ import (
"math/big"
"runtime"
"sync"
- //"sync/atomic"
"github.com/ava-labs/coreth/accounts"
"github.com/ava-labs/coreth/consensus"
@@ -53,6 +52,16 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
+// ProtocolVersions are the supported versions of the eth protocol (first is primary).
+var ProtocolVersions = []uint{eth65, eth64, eth63}
+
+// Constants to match up protocol versions and messages
+const (
+ eth63 = 63
+ eth64 = 64
+ eth65 = 65
+)
+
type BackendCallbacks struct {
OnQueryAcceptedBlock func() *types.Block
}
@@ -498,13 +507,13 @@ func (s *Ethereum) Start() error {
s.startBloomHandlers(params.BloomBitsBlocks)
// Figure out a max peers count based on the server limits
- maxPeers := s.p2pServer.MaxPeers
- if s.config.LightServ > 0 {
- if s.config.LightPeers >= s.p2pServer.MaxPeers {
- return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
- }
- maxPeers -= s.config.LightPeers
- }
+ // maxPeers := s.p2pServer.MaxPeers
+ // if s.config.LightServ > 0 {
+ // if s.config.LightPeers >= s.p2pServer.MaxPeers {
+ // return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
+ // }
+ // maxPeers -= s.config.LightPeers
+ // }
// Start the networking layer and the light server if requested
//s.protocolManager.Start(maxPeers)
return nil
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index 14476ab..33810b2 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -29,8 +29,6 @@ import (
"github.com/ethereum/go-ethereum/log"
)
-const sampleNumber = 3 // Number of transactions sampled in a block
-
var DefaultMaxPrice = big.NewInt(500 * params.GWei)
type Config struct {
diff --git a/eth/protocol.go b/eth/protocol.go
deleted file mode 100644
index ef5dcde..0000000
--- a/eth/protocol.go
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2014 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 eth
-
-import (
- "fmt"
- "io"
- "math/big"
-
- "github.com/ava-labs/coreth/core"
- "github.com/ava-labs/coreth/core/forkid"
- "github.com/ava-labs/coreth/core/types"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// Constants to match up protocol versions and messages
-const (
- eth63 = 63
- eth64 = 64
- eth65 = 65
-)
-
-// protocolName is the official short name of the protocol used during capability negotiation.
-const protocolName = "eth"
-
-// ProtocolVersions are the supported versions of the eth protocol (first is primary).
-var ProtocolVersions = []uint{eth65, eth64, eth63}
-
-// protocolLengths are the number of implemented message corresponding to different protocol versions.
-var protocolLengths = map[uint]uint64{eth65: 17, eth64: 17, eth63: 17}
-
-const protocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
-
-// eth protocol message codes
-const (
- StatusMsg = 0x00
- NewBlockHashesMsg = 0x01
- TransactionMsg = 0x02
- GetBlockHeadersMsg = 0x03
- BlockHeadersMsg = 0x04
- GetBlockBodiesMsg = 0x05
- BlockBodiesMsg = 0x06
- NewBlockMsg = 0x07
- GetNodeDataMsg = 0x0d
- NodeDataMsg = 0x0e
- GetReceiptsMsg = 0x0f
- ReceiptsMsg = 0x10
-
- // New protocol message codes introduced in eth65
- //
- // Previously these message ids were used by some legacy and unsupported
- // eth protocols, reown them here.
- NewPooledTransactionHashesMsg = 0x08
- GetPooledTransactionsMsg = 0x09
- PooledTransactionsMsg = 0x0a
-)
-
-type errCode int
-
-const (
- ErrMsgTooLarge = iota
- ErrDecode
- ErrInvalidMsgCode
- ErrProtocolVersionMismatch
- ErrNetworkIDMismatch
- ErrGenesisMismatch
- ErrForkIDRejected
- ErrNoStatusMsg
- ErrExtraStatusMsg
-)
-
-func (e errCode) String() string {
- return errorToString[int(e)]
-}
-
-// XXX change once legacy code is out
-var errorToString = map[int]string{
- ErrMsgTooLarge: "Message too long",
- ErrDecode: "Invalid message",
- ErrInvalidMsgCode: "Invalid message code",
- ErrProtocolVersionMismatch: "Protocol version mismatch",
- ErrNetworkIDMismatch: "Network ID mismatch",
- ErrGenesisMismatch: "Genesis mismatch",
- ErrForkIDRejected: "Fork ID rejected",
- ErrNoStatusMsg: "No status message",
- ErrExtraStatusMsg: "Extra status message",
-}
-
-type txPool interface {
- // Has returns an indicator whether txpool has a transaction
- // cached with the given hash.
- Has(hash common.Hash) bool
-
- // Get retrieves the transaction from local txpool with given
- // tx hash.
- Get(hash common.Hash) *types.Transaction
-
- // AddRemotes should add the given transactions to the pool.
- AddRemotes([]*types.Transaction) []error
-
- // Pending should return pending transactions.
- // The slice should be modifiable by the caller.
- Pending() (map[common.Address]types.Transactions, error)
-
- // SubscribeNewTxsEvent should return an event subscription of
- // NewTxsEvent and send events to the given channel.
- SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
-}
-
-// statusData63 is the network packet for the status message for eth/63.
-type statusData63 struct {
- ProtocolVersion uint32
- NetworkId uint64
- TD *big.Int
- CurrentBlock common.Hash
- GenesisBlock common.Hash
-}
-
-// statusData is the network packet for the status message for eth/64 and later.
-type statusData struct {
- ProtocolVersion uint32
- NetworkID uint64
- TD *big.Int
- Head common.Hash
- Genesis common.Hash
- ForkID forkid.ID
-}
-
-// newBlockHashesData is the network packet for the block announcements.
-type newBlockHashesData []struct {
- Hash common.Hash // Hash of one particular block being announced
- Number uint64 // Number of one particular block being announced
-}
-
-// getBlockHeadersData represents a block header query.
-type getBlockHeadersData struct {
- Origin hashOrNumber // Block from which to retrieve headers
- Amount uint64 // Maximum number of headers to retrieve
- Skip uint64 // Blocks to skip between consecutive headers
- Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
-}
-
-// hashOrNumber is a combined field for specifying an origin block.
-type hashOrNumber struct {
- Hash common.Hash // Block hash from which to retrieve headers (excludes Number)
- Number uint64 // Block hash from which to retrieve headers (excludes Hash)
-}
-
-// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
-// two contained union fields.
-func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
- if hn.Hash == (common.Hash{}) {
- return rlp.Encode(w, hn.Number)
- }
- if hn.Number != 0 {
- return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
- }
- return rlp.Encode(w, hn.Hash)
-}
-
-// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
-// into either a block hash or a block number.
-func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
- _, size, _ := s.Kind()
- origin, err := s.Raw()
- if err == nil {
- switch {
- case size == 32:
- err = rlp.DecodeBytes(origin, &hn.Hash)
- case size <= 8:
- err = rlp.DecodeBytes(origin, &hn.Number)
- default:
- err = fmt.Errorf("invalid input size %d for origin", size)
- }
- }
- return err
-}
-
-// newBlockData is the network packet for the block propagation message.
-type newBlockData struct {
- Block *types.Block
- TD *big.Int
-}
-
-// sanityCheck verifies that the values are reasonable, as a DoS protection
-func (request *newBlockData) sanityCheck() error {
- if err := request.Block.SanityCheck(); err != nil {
- return err
- }
- //TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times
- // larger, it will still fit within 100 bits
- if tdlen := request.TD.BitLen(); tdlen > 100 {
- return fmt.Errorf("too large block TD: bitlen %d", tdlen)
- }
- return nil
-}
-
-// blockBody represents the data content of a single block.
-type blockBody struct {
- Transactions []*types.Transaction // Transactions contained within a block
- Uncles []*types.Header // Uncles contained within a block
-}
-
-// blockBodiesData is the network packet for block content distribution.
-type blockBodiesData []*blockBody
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 6dd4df8..8470d64 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -324,13 +324,13 @@ func toBlockNumArg(number *big.Int) string {
return hexutil.EncodeBig(number)
}
-type rpcProgress struct {
- StartingBlock hexutil.Uint64
- CurrentBlock hexutil.Uint64
- HighestBlock hexutil.Uint64
- PulledStates hexutil.Uint64
- KnownStates hexutil.Uint64
-}
+// type rpcProgress struct {
+// StartingBlock hexutil.Uint64
+// CurrentBlock hexutil.Uint64
+// HighestBlock hexutil.Uint64
+// PulledStates hexutil.Uint64
+// KnownStates hexutil.Uint64
+// }
// SyncProgress retrieves the current progress of the sync algorithm. If there's
// no sync currently running, it returns nil.
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
deleted file mode 100644
index 3dd06e3..0000000
--- a/ethstats/ethstats.go
+++ /dev/null
@@ -1,785 +0,0 @@
-// Copyright 2016 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 ethstats implements the network stats reporting service.
-package ethstats
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "math/big"
- "net/http"
- "regexp"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "github.com/ava-labs/coreth/consensus"
- "github.com/ava-labs/coreth/core"
- "github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/eth"
- "github.com/ava-labs/coreth/miner"
- "github.com/ava-labs/coreth/node"
- "github.com/ava-labs/coreth/rpc"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/mclock"
- "github.com/ethereum/go-ethereum/eth/downloader"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/les"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/gorilla/websocket"
-)
-
-const (
- // historyUpdateRange is the number of blocks a node should report upon login or
- // history request.
- historyUpdateRange = 50
-
- // txChanSize is the size of channel listening to NewTxsEvent.
- // The number is referenced from the size of tx pool.
- txChanSize = 4096
- // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
- chainHeadChanSize = 10
-)
-
-// backend encompasses the bare-minimum functionality needed for ethstats reporting
-type backend interface {
- SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
- SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription
- CurrentHeader() *types.Header
- HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
- GetTd(ctx context.Context, hash common.Hash) *big.Int
- Stats() (pending int, queued int)
- Downloader() *downloader.Downloader
-}
-
-// fullNodeBackend encompasses the functionality necessary for a full node
-// reporting to ethstats
-type fullNodeBackend interface {
- backend
- Miner() *miner.Miner
- BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
- CurrentBlock() *types.Block
- SuggestPrice(ctx context.Context) (*big.Int, error)
-}
-
-// Service implements an Ethereum netstats reporting daemon that pushes local
-// chain statistics up to a monitoring server.
-type Service struct {
- server *p2p.Server // Peer-to-peer server to retrieve networking infos
- backend backend
- engine consensus.Engine // Consensus engine to retrieve variadic block fields
-
- node string // Name of the node to display on the monitoring page
- pass string // Password to authorize access to the monitoring page
- host string // Remote address of the monitoring service
-
- pongCh chan struct{} // Pong notifications are fed into this channel
- histCh chan []uint64 // History request block numbers are fed into this channel
-
-}
-
-// connWrapper is a wrapper to prevent concurrent-write or concurrent-read on the
-// websocket.
-//
-// From Gorilla websocket docs:
-// Connections support one concurrent reader and one concurrent writer.
-// Applications are responsible for ensuring that no more than one goroutine calls the write methods
-// - NextWriter, SetWriteDeadline, WriteMessage, WriteJSON, EnableWriteCompression, SetCompressionLevel
-// concurrently and that no more than one goroutine calls the read methods
-// - NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler
-// concurrently.
-// The Close and WriteControl methods can be called concurrently with all other methods.
-type connWrapper struct {
- conn *websocket.Conn
-
- rlock sync.Mutex
- wlock sync.Mutex
-}
-
-func newConnectionWrapper(conn *websocket.Conn) *connWrapper {
- return &connWrapper{conn: conn}
-}
-
-// WriteJSON wraps corresponding method on the websocket but is safe for concurrent calling
-func (w *connWrapper) WriteJSON(v interface{}) error {
- w.wlock.Lock()
- defer w.wlock.Unlock()
-
- return w.conn.WriteJSON(v)
-}
-
-// ReadJSON wraps corresponding method on the websocket but is safe for concurrent calling
-func (w *connWrapper) ReadJSON(v interface{}) error {
- w.rlock.Lock()
- defer w.rlock.Unlock()
-
- return w.conn.ReadJSON(v)
-}
-
-// Close wraps corresponding method on the websocket but is safe for concurrent calling
-func (w *connWrapper) Close() error {
- // The Close and WriteControl methods can be called concurrently with all other methods,
- // so the mutex is not used here
- return w.conn.Close()
-}
-
-// New returns a monitoring service ready for stats reporting.
-func New(node *node.Node, backend backend, engine consensus.Engine, url string) error {
- // Parse the netstats connection url
- re := regexp.MustCompile("([^:@]*)(:([^@]*))?@(.+)")
- parts := re.FindStringSubmatch(url)
- if len(parts) != 5 {
- return fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url)
- }
- ethstats := &Service{
- backend: backend,
- engine: engine,
- server: node.Server(),
- node: parts[1],
- pass: parts[3],
- host: parts[4],
- pongCh: make(chan struct{}),
- histCh: make(chan []uint64, 1),
- }
-
- node.RegisterLifecycle(ethstats)
- return nil
-}
-
-// Start implements node.Lifecycle, starting up the monitoring and reporting daemon.
-func (s *Service) Start() error {
- go s.loop()
-
- log.Info("Stats daemon started")
- return nil
-}
-
-// Stop implements node.Lifecycle, terminating the monitoring and reporting daemon.
-func (s *Service) Stop() error {
- log.Info("Stats daemon stopped")
- return nil
-}
-
-// loop keeps trying to connect to the netstats server, reporting chain events
-// until termination.
-func (s *Service) loop() {
- // Subscribe to chain events to execute updates on
- chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize)
- headSub := s.backend.SubscribeChainHeadEvent(chainHeadCh)
- defer headSub.Unsubscribe()
-
- txEventCh := make(chan core.NewTxsEvent, txChanSize)
- txSub := s.backend.SubscribeNewTxsEvent(txEventCh)
- defer txSub.Unsubscribe()
-
- // Start a goroutine that exhausts the subscriptions to avoid events piling up
- var (
- quitCh = make(chan struct{})
- headCh = make(chan *types.Block, 1)
- txCh = make(chan struct{}, 1)
- )
- go func() {
- var lastTx mclock.AbsTime
-
- HandleLoop:
- for {
- select {
- // Notify of chain head events, but drop if too frequent
- case head := <-chainHeadCh:
- select {
- case headCh <- head.Block:
- default:
- }
-
- // Notify of new transaction events, but drop if too frequent
- case <-txEventCh:
- if time.Duration(mclock.Now()-lastTx) < time.Second {
- continue
- }
- lastTx = mclock.Now()
-
- select {
- case txCh <- struct{}{}:
- default:
- }
-
- // node stopped
- case <-txSub.Err():
- break HandleLoop
- case <-headSub.Err():
- break HandleLoop
- }
- }
- close(quitCh)
- }()
-
- // Resolve the URL, defaulting to TLS, but falling back to none too
- path := fmt.Sprintf("%s/api", s.host)
- urls := []string{path}
-
- // url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779)
- if !strings.Contains(path, "://") {
- urls = []string{"wss://" + path, "ws://" + path}
- }
-
- errTimer := time.NewTimer(0)
- defer errTimer.Stop()
- // Loop reporting until termination
- for {
- select {
- case <-quitCh:
- return
- case <-errTimer.C:
- // Establish a websocket connection to the server on any supported URL
- var (
- conn *connWrapper
- err error
- )
- dialer := websocket.Dialer{HandshakeTimeout: 5 * time.Second}
- header := make(http.Header)
- header.Set("origin", "http://localhost")
- for _, url := range urls {
- c, _, e := dialer.Dial(url, header)
- err = e
- if err == nil {
- conn = newConnectionWrapper(c)
- break
- }
- }
- if err != nil {
- log.Warn("Stats server unreachable", "err", err)
- errTimer.Reset(10 * time.Second)
- continue
- }
- // Authenticate the client with the server
- if err = s.login(conn); err != nil {
- log.Warn("Stats login failed", "err", err)
- conn.Close()
- errTimer.Reset(10 * time.Second)
- continue
- }
- go s.readLoop(conn)
-
- // Send the initial stats so our node looks decent from the get go
- if err = s.report(conn); err != nil {
- log.Warn("Initial stats report failed", "err", err)
- conn.Close()
- errTimer.Reset(0)
- continue
- }
- // Keep sending status updates until the connection breaks
- fullReport := time.NewTicker(15 * time.Second)
-
- for err == nil {
- select {
- case <-quitCh:
- fullReport.Stop()
- // Make sure the connection is closed
- conn.Close()
- return
-
- case <-fullReport.C:
- if err = s.report(conn); err != nil {
- log.Warn("Full stats report failed", "err", err)
- }
- case list := <-s.histCh:
- if err = s.reportHistory(conn, list); err != nil {
- log.Warn("Requested history report failed", "err", err)
- }
- case head := <-headCh:
- if err = s.reportBlock(conn, head); err != nil {
- log.Warn("Block stats report failed", "err", err)
- }
- if err = s.reportPending(conn); err != nil {
- log.Warn("Post-block transaction stats report failed", "err", err)
- }
- case <-txCh:
- if err = s.reportPending(conn); err != nil {
- log.Warn("Transaction stats report failed", "err", err)
- }
- }
- }
- fullReport.Stop()
-
- // Close the current connection and establish a new one
- conn.Close()
- errTimer.Reset(0)
- }
- }
-}
-
-// readLoop loops as long as the connection is alive and retrieves data packets
-// from the network socket. If any of them match an active request, it forwards
-// it, if they themselves are requests it initiates a reply, and lastly it drops
-// unknown packets.
-func (s *Service) readLoop(conn *connWrapper) {
- // If the read loop exists, close the connection
- defer conn.Close()
-
- for {
- // Retrieve the next generic network packet and bail out on error
- var blob json.RawMessage
- if err := conn.ReadJSON(&blob); err != nil {
- log.Warn("Failed to retrieve stats server message", "err", err)
- return
- }
- // If the network packet is a system ping, respond to it directly
- var ping string
- if err := json.Unmarshal(blob, &ping); err == nil && strings.HasPrefix(ping, "primus::ping::") {
- if err := conn.WriteJSON(strings.Replace(ping, "ping", "pong", -1)); err != nil {
- log.Warn("Failed to respond to system ping message", "err", err)
- return
- }
- continue
- }
- // Not a system ping, try to decode an actual state message
- var msg map[string][]interface{}
- if err := json.Unmarshal(blob, &msg); err != nil {
- log.Warn("Failed to decode stats server message", "err", err)
- return
- }
- log.Trace("Received message from stats server", "msg", msg)
- if len(msg["emit"]) == 0 {
- log.Warn("Stats server sent non-broadcast", "msg", msg)
- return
- }
- command, ok := msg["emit"][0].(string)
- if !ok {
- log.Warn("Invalid stats server message type", "type", msg["emit"][0])
- return
- }
- // If the message is a ping reply, deliver (someone must be listening!)
- if len(msg["emit"]) == 2 && command == "node-pong" {
- select {
- case s.pongCh <- struct{}{}:
- // Pong delivered, continue listening
- continue
- default:
- // Ping routine dead, abort
- log.Warn("Stats server pinger seems to have died")
- return
- }
- }
- // If the message is a history request, forward to the event processor
- if len(msg["emit"]) == 2 && command == "history" {
- // Make sure the request is valid and doesn't crash us
- request, ok := msg["emit"][1].(map[string]interface{})
- if !ok {
- log.Warn("Invalid stats history request", "msg", msg["emit"][1])
- select {
- case s.histCh <- nil: // Treat it as an no indexes request
- default:
- }
- continue
- }
- list, ok := request["list"].([]interface{})
- if !ok {
- log.Warn("Invalid stats history block list", "list", request["list"])
- return
- }
- // Convert the block number list to an integer list
- numbers := make([