diff --git a/.gitignore b/.gitignore index 30ff6af..6082f5c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,14 @@ *.dll *.so *.dylib -.idea +.idea/ bin/ +datadir/ +ssl/ +logs/ + +# helper file for docker build process +.gittag # Test binary, built with `go test -c` *.test diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..748d444 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,76 @@ +service: + golangci-lint-version: 1.52.x + +# full list: https://golangci-lint.run/usage/linters/ +linters: + disable-all: true + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - unused + - bodyclose + - containedctx + - contextcheck + - decorder + - dupl + - goconst + - gocritic + - gofmt + - goimports + - gosec + - noctx + - nolintlint + - prealloc + - revive + - stylecheck + - unconvert + - usestdlibvars + +# full list: https://golangci-lint.run/usage/configuration/ +linters-settings: + staticcheck: + checks: [ "all" ] + stylecheck: + # ignore package comment and naming recommendations + checks: [ "all", "-ST1000", "-ST1003", "-ST10016"] + errcheck: + check-blank: true + govet: + check-shadowing: true + +issues: + new: true + new-from-rev: origin/develop + + # Validates whole file instead of changed lines only, but produces many errors + # on large files. We might consider enabling this flag to start improving the + # source code file by file. Also, with `whole-files` turned off validators don't + # check unchanged lines, even if the change leaded to an issue in other place. + whole-files: false + + exclude-rules: + - path: _test\.go + linters: + - errcheck + - gosec + - govet + - gomnd + - bodyclose + - ineffassign + + include: + - EXC0011 + - EXC0012 + - EXC0014 + +run: + go: "1.19" + skip-files: + - ".*\\.pb\\.go$" # skip protobuf generated code + +output: + format: tab diff --git a/Dockerfile b/Dockerfile index 3b6080d..c79016e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG GO_VERSION=alpine -ARG BASE=golang:1.18-alpine +ARG BASE=golang:1.19-alpine FROM ${BASE} as builder diff --git a/Makefile b/Makefile index 849ae46..dff11b5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ MODULE = $(shell env GO111MODULE=on $(GO) list -m) DATE ?= $(shell date +%FT%T%z) -VERSION ?= $(shell git describe --tags --always --dirty --match=v2* 2> /dev/null || \ +VERSION ?= $(shell git describe --tags --always --dirty --match='v2*' 2> /dev/null || \ cat .gittag || \ sed '5!d' $(CURDIR)/version/version.go | grep -o '".*"' | sed 's/"//g' 2> /dev/null || echo v2.1.1.2) PKGS = $(or $(PKG),$(shell env GO111MODULE=on $(GO) list ./...)) @@ -79,7 +79,7 @@ test-coverage: fmt lint test-coverage-tools ; $(info $(M) running coverage tests $Q $(GO) tool cover -html=$(COVERAGE_PROFILE) -o $(COVERAGE_HTML) $Q $(GOCOV) convert $(COVERAGE_PROFILE) | $(GOCOVXML) > $(COVERAGE_XML) -GOLANGCI_VERSION=v1.49.0 +GOLANGCI_VERSION=v1.52.2 GOLANGCI_REV=8cdecc968bf7b87ce85e09e42cabb3e3540e9344 .PHONY: lint diff --git a/blockchain/beacon/block.go b/blockchain/beacon/block.go deleted file mode 100644 index 2ad69e2..0000000 --- a/blockchain/beacon/block.go +++ /dev/null @@ -1,223 +0,0 @@ -package beacon - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - - "github.com/bloXroute-Labs/gateway/v2/blockchain" - "github.com/bloXroute-Labs/gateway/v2/blockchain/eth" - "github.com/bloXroute-Labs/gateway/v2/blockchain/network" - log "github.com/bloXroute-Labs/gateway/v2/logger" - "github.com/bloXroute-Labs/gateway/v2/types" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" -) - -type broadcastFunc func(interfaces.ReadOnlySignedBeaconBlock) error - -type blockProcessor struct { - config *network.EthConfig - chain *Chain - log *log.Entry - bridge blockchain.Bridge - broadcast broadcastFunc -} - -func newBlockProcessor(ctx context.Context, config *network.EthConfig, chain *Chain, bridge blockchain.Bridge, broadcast broadcastFunc, log *log.Entry) *blockProcessor { - return &blockProcessor{ - config: config, - chain: chain, - bridge: bridge, - broadcast: broadcast, - log: log, - } -} - -func (n *blockProcessor) ProcessBDNBlock(bdnBlock *types.BxBlock) { - beaconBlock, err := n.storeBDNBlock(bdnBlock) - if err != nil { - logBlockConverterFailure(err, bdnBlock) - return - } - - // nil if block is a duplicate and does not need processing - if beaconBlock == nil { - return - } - - hash, err := beaconBlock.Block().HashTreeRoot() - if err != nil { - n.log.Errorf("could not extract hash of beacon block slot %v: %s", beaconBlock.Block().Slot(), err) - return - } - - if n.broadcast != nil { - go func() { - if err := n.broadcast(beaconBlock); err != nil { - n.log.Errorf("could not broadcast block %s: %v", ethcommon.Hash(hash), err) - } - }() - } -} - -// storeBDNBlock will return a nil block and no error if block is a duplicate -func (n *blockProcessor) storeBDNBlock(bdnBlock *types.BxBlock) (interfaces.ReadOnlySignedBeaconBlock, error) { - blockHash := ethcommon.BytesToHash(bdnBlock.BeaconHash().Bytes()) - if n.chain.HasBlock(blockHash) { - n.log.Debugf("duplicate block %v from BDN, skipping", blockHash) - return nil, nil - } - - blockchainBlock, err := n.bridge.BlockBDNtoBlockchain(bdnBlock) - if err != nil { - logBlockConverterFailure(err, bdnBlock) - return nil, errors.New("could not convert BDN block to beacon block") - } - - beaconBlock, ok := blockchainBlock.(interfaces.ReadOnlySignedBeaconBlock) - if !ok { - logBlockConverterFailure(err, bdnBlock) - return nil, errors.New("could not convert BDN block to beacon block") - } - - if _, err := n.chain.AddBlock(beaconBlock, BSBDN); err != nil { - return nil, fmt.Errorf("could not add block %v: %v", blockHash, err) - } - return beaconBlock, nil -} - -func (n *blockProcessor) ProcessBlockchainBlock(log *log.Entry, endpoint types.NodeEndpoint, block interfaces.ReadOnlySignedBeaconBlock) error { - blockHash, err := block.Block().HashTreeRoot() - if err != nil { - return fmt.Errorf("could not get block hash: %v", err) - } - - blockHashString := ethcommon.BytesToHash(blockHash[:]) - blockHeight := uint64(block.Block().Slot()) - - if err := n.chain.ValidateBlock(block); err != nil { - if err == ErrAlreadySeen { - log.Debugf("skipping block %v (height %v): %v", blockHashString, blockHeight, err) - } else { - log.Warnf("skipping block %v (height %v): %v", blockHashString, blockHeight, err) - } - return nil - } - - log.Debugf("processing new block %v (height %v)", blockHashString, blockHeight) - newHeadCount, err := n.chain.AddBlock(block, BSBlockchain) - if err != nil { - return fmt.Errorf("could not add block %v: %v", blockHashString, err) - } - - n.sendConfirmedBlocksToBDN(log, newHeadCount, endpoint) - - // TODO: send to _others_ peers once we support multiple nodes - - return nil -} - -func (n *blockProcessor) sendConfirmedBlocksToBDN(log *log.Entry, count int, peerEndpoint types.NodeEndpoint) { - newHeads, err := n.chain.GetNewHeadsForBDN(count) - if err != nil { - log.Errorf("could not fetch chainstate: %v", err) - } - - if len(newHeads) > 1 { - log.Debugf("sending %d blocks from blockchain", len(newHeads)) - } - - // iterate in reverse to send all new heads in ascending order to BDN - for i := len(newHeads) - 1; i >= 0; i-- { - beaconBlock := newHeads[i] - - bdnBeaconBlock, err := n.bridge.BlockBlockchainToBDN(beaconBlock) - if err != nil { - log.Errorf("could not convert beacon block %v: %v", beaconBlockHash(beaconBlock), err) - continue - } - - if err := n.bridge.SendBlockToBDN(bdnBeaconBlock, peerEndpoint); err != nil { - log.Errorf("could not send block to BDN: %v", err) - continue - } - - n.chain.MarkSentToBDN(ethcommon.Hash(bdnBeaconBlock.BeaconHash())) - - // convert and send ETH block for back compatibility - ethBlock, err := eth.BeaconBlockToEthBlock(beaconBlock) - if err != nil { - log.Errorf("could not convert beacon block %v eth block: %v", bdnBeaconBlock.BeaconHash(), err) - return - } - - bdnEthBlock, err := n.bridge.BlockBlockchainToBDN(ethBlock) - if err != nil { - log.Errorf("could not convert eth block: %v", err) - return - } - - if err := n.bridge.SendBlockToBDN(bdnEthBlock, peerEndpoint); err != nil { - log.Errorf("could not send block to BDN: %v", err) - return - } - } - - beaconBlock, err := n.chain.BlockAtDepth(n.config.BlockConfirmationsCount) - if err != nil { - log.Debugf("cannot retrieve block with chain depth %v, %v", n.config.BlockConfirmationsCount, err) - return - } - - bdnBeaconBlock, err := n.bridge.BlockBlockchainToBDN(beaconBlock) - if err != nil { - log.Debugf("cannot convert beacon block to BDN block at the chain depth %v with hash %v, %v", n.config.BlockConfirmationsCount, beaconBlockHash(beaconBlock), err) - return - } - - blockHash := ethcommon.Hash(bdnBeaconBlock.BeaconHash()) - - if n.chain.HasConfirmationSentToBDN(blockHash) { - log.Debugf("block %v has already been sent in a block confirm message to gateway", blockHash) - return - } - log.Tracef("sending beacon block (%v) confirm message to gateway from backend", blockHash) - err = n.bridge.SendConfirmedBlockToGateway(bdnBeaconBlock, peerEndpoint) - if err != nil { - log.Debugf("failed sending beacon block(%v) confirmation message to gateway, %v", blockHash, err) - return - } - n.chain.MarkConfirmationSentToBDN(blockHash) - - // convert and send ETH block confirmation for back compatibility - ethBlock, err := eth.BeaconBlockToEthBlock(beaconBlock) - if err != nil { - log.Errorf("could not convert beacon block %v eth block: %v", bdnBeaconBlock.BeaconHash(), err) - return - } - - bdnEthBlock, err := n.bridge.BlockBlockchainToBDN(ethBlock) - if err != nil { - log.Debugf("cannot convert eth block to BDN block at the chain depth %v with hash %v, %v", n.config.BlockConfirmationsCount, bdnBeaconBlock.BeaconHash(), err) - return - } - log.Tracef("sending eth block (%v) confirm message to gateway from backend", blockHash) - err = n.bridge.SendConfirmedBlockToGateway(bdnEthBlock, peerEndpoint) - if err != nil { - log.Debugf("failed sending eth block(%v) confirmation message to gateway, %v", blockHash, err) - return - } -} - -func beaconBlockHash(block interfaces.ReadOnlySignedBeaconBlock) string { - // takes some time, but fine if use in rare errors - hash, err := block.Block().HashTreeRoot() - if err != nil { - log.Errorf("cannot retrieve beacon block hash: %v", err) - return "" - } - - return hex.EncodeToString(hash[:]) -} diff --git a/blockchain/beacon/chain.go b/blockchain/beacon/chain.go deleted file mode 100644 index 3253df2..0000000 --- a/blockchain/beacon/chain.go +++ /dev/null @@ -1,963 +0,0 @@ -package beacon - -import ( - "bytes" - "context" - "errors" - "fmt" - "strconv" - "sync" - "time" - - log "github.com/bloXroute-Labs/gateway/v2/logger" - "github.com/bloXroute-Labs/gateway/v2/utils" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth/protocols/eth" - cmap "github.com/orcaman/concurrent-map" - "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v3/runtime/version" -) - -const ( - maxReorgLength = 20 - minValidChainLength = 10 - defaultMaxSize = 100 - defaultCleanInterval = 10 * time.Minute -) - -// blockRef represents block info used for storing best block -type blockRef struct { - height uint64 // TODO: rename here and everywhere height to slot incl func definitions - hash ethcommon.Hash -} - -// String formats blockRef for concise printing -func (b blockRef) String() string { - return fmt.Sprintf("%v[%v]", b.height, b.hash.TerminalString()) -} - -type blockRefChain []blockRef - -func (bc blockRefChain) head() *blockRef { - if len(bc) == 0 { - return &blockRef{} - } - return &bc[0] -} - -func (bc blockRefChain) tail() *blockRef { - if len(bc) == 0 { - return nil - } - return &bc[len(bc)-1] -} - -// String formats blockRefChain for concise printing -func (bc blockRefChain) String() string { - return fmt.Sprintf("chainstate(best: %v, oldest: %v)", bc.head(), bc.tail()) -} - -// Chain represents and stores blockchain state info in memory -type Chain struct { - chainLock sync.RWMutex // lock for updating chainstate - headerLock sync.RWMutex // lock for block headers/heights - - genesisTime uint64 - - // if reconciling a fork takes longer than this value, then trim the chain to this length - maxReorg int - - // if a missing block is preventing updating the chain head, once a valid chain of this length is possible, discard the old chain - minValidChain int - - // if a missing block too old discard to add it - ignoreSlotCount int - - heightToBlockHeaders cmap.ConcurrentMap - blockHashMetadata cmap.ConcurrentMap - blockHashToBody cmap.ConcurrentMap - - chainState blockRefChain - - clock utils.RealClock -} - -// BlockSource indicates the origin of a block message in the blockchain -type BlockSource string - -// enumerate types of BlockSource -const ( - BSBDN BlockSource = "BDN" - BSBlockchain BlockSource = "Blockchain" -) - -type blockMetadata struct { - height uint64 - sentToBDN bool - confirmed bool - cnfMsgSent bool -} - -type ethBeaconHeader struct { - Header *ethpb.SignedBeaconBlockHeader - hash ethcommon.Hash - version int -} - -// NewChain returns a new chainstate struct for usage -func NewChain(ctx context.Context, genesisTime uint64, ignoreSlotCount int) *Chain { - return newChain(ctx, genesisTime, ignoreSlotCount, maxReorgLength, minValidChainLength, defaultCleanInterval, defaultMaxSize) -} - -func newChain(ctx context.Context, genesisTime uint64, ignoreSlotCount int, maxReorg, minValidChain int, cleanInterval time.Duration, maxSize int) *Chain { - c := &Chain{ - chainLock: sync.RWMutex{}, - headerLock: sync.RWMutex{}, - genesisTime: genesisTime, - heightToBlockHeaders: cmap.New(), - blockHashMetadata: cmap.New(), - blockHashToBody: cmap.New(), - chainState: make([]blockRef, 0), - maxReorg: maxReorg, - minValidChain: minValidChain, - ignoreSlotCount: ignoreSlotCount, - clock: utils.RealClock{}, - } - go c.cleanBlockStorage(ctx, cleanInterval, maxSize) - return c -} - -func (c *Chain) cleanBlockStorage(ctx context.Context, cleanInterval time.Duration, maxSize int) { - ticker := c.clock.Ticker(cleanInterval) - for { - select { - case <-ticker.Alert(): - c.clean(maxSize) - case <-ctx.Done(): - return - } - } -} - -// AddBlock adds the provided block from the source into storage, updating the chainstate if the block comes from a reliable source. AddBlock returns the number of new canonical hashes added to the head if a reorganization happened. TODO: consider computing difficulty in here? -func (c *Chain) AddBlock(b interfaces.ReadOnlySignedBeaconBlock, source BlockSource) (int, error) { - c.chainLock.Lock() - defer c.chainLock.Unlock() - - height := uint64(b.Block().Slot()) - h, err := b.Block().HashTreeRoot() - if err != nil { - return 0, fmt.Errorf("could not get hash: %v", err) - } - - blockHash := ethcommon.BytesToHash(h[:]) - - parentRoot := b.Block().ParentRoot() - parentHash := ethcommon.BytesToHash(parentRoot[:]) - - // update metadata if block already stored, otherwise update all block info - if c.HasBlock(blockHash) { - c.storeBlockMetadata(blockHash, height, source == BSBlockchain, false) - } else { - if err := c.storeBlock(blockHash, b, source); err != nil { - log.Errorf("could not store block %v, %v", blockHash, err) - } - } - - // if source is BDN, then no authority to update chainstate and indicate no new heads to return - if source == BSBDN { - return 0, nil - } - - return c.updateChainState(height, blockHash, parentHash), nil -} - -// ConfirmBlock marks a block as confirmed by a trustworthy source, updating the chain state if possible and returning the number of new canonical hashes added to the head if an update happened. -func (c *Chain) ConfirmBlock(hash ethcommon.Hash) int { - c.chainLock.Lock() - defer c.chainLock.Unlock() - - // update metadata - bm, ok := c.getBlockMetadata(hash) - if !ok { - return 0 - } - - bm.confirmed = true - c.blockHashMetadata.Set(hash.String(), bm) - - header, ok := c.getBlockHeader(bm.height, hash) - if !ok { - return 0 - } - - return c.updateChainState(bm.height, hash, ethcommon.BytesToHash(header.Header.GetHeader().GetParentRoot())) -} - -// GetNewHeadsForBDN fetches the newest blocks on the chainstate that have not previously been sent to the BDN. In cases of error, as many entries are still returned along with the error. Entries are returned in descending order. -func (c *Chain) GetNewHeadsForBDN(count int) ([]interfaces.ReadOnlySignedBeaconBlock, error) { - c.chainLock.RLock() - defer c.chainLock.RUnlock() - - heads := make([]interfaces.ReadOnlySignedBeaconBlock, 0, count) - - for i := 0; i < count; i++ { - if len(c.chainState) <= i { - return heads, errors.New("chain state insufficient length") - } - - head := c.chainState[i] - - // !ok blocks should never be triggered, as any state cleanup should also cleanup the chain state - bm, ok := c.getBlockMetadata(head.hash) - if !ok { - return heads, fmt.Errorf("inconsistent chainstate: no metadata stored for %v", head.hash) - } - - // blocks have previously been sent to BDN, ok to stop here - if bm.sentToBDN { - break - } - - header, ok := c.getBlockHeader(head.height, head.hash) - if !ok { - return heads, fmt.Errorf("inconsistent chainstate: no header stored for %v", head.hash) - } - - body, ok := c.getBlockBody(head.hash) - if !ok { - return heads, fmt.Errorf("inconsistent chainstate: no body stored for %v", head.hash) - } - - block, err := newSignedBeaconBlock(header.version, header.Header, body) - if err != nil { - return heads, fmt.Errorf("inconsistent chainstate: cannot merge head and body for %v: %v", head.hash, err) - } - - heads = append(heads, block) - } - - return heads, nil -} - -// ValidateBlock determines if block can potentially be added to the chain -func (c *Chain) ValidateBlock(block interfaces.ReadOnlySignedBeaconBlock) error { - hash, err := block.Block().HashTreeRoot() - if err != nil { - return fmt.Errorf("could not get block hash: %v", err) - } - blockSlot := block.Block().Slot() - - if c.HasBlock(hash) && c.HasSentToBDN(hash) && c.HasConfirmedBlock(hash) { - return ErrAlreadySeen - } - - currentSlot := currentSlot(c.genesisTime) - - maxSlot := currentSlot + types.Slot(c.ignoreSlotCount) - if blockSlot > maxSlot { - return fmt.Errorf("too far in future, current slot: %d, max slot: %d", currentSlot, maxSlot) - } - - minSlot := currentSlot - types.Slot(c.ignoreSlotCount) - if blockSlot < minSlot { - return fmt.Errorf("too old, current slot: %d, min slot: %d", currentSlot, minSlot) - } - - return nil -} - -// HasBlock indicates if block has been stored locally -func (c *Chain) HasBlock(hash ethcommon.Hash) bool { - return c.hasHeader(hash) && c.hasBody(hash) -} - -// HasSentToBDN indicates if the block has been sent to the BDN -func (c *Chain) HasSentToBDN(hash ethcommon.Hash) bool { - bm, ok := c.getBlockMetadata(hash) - if !ok { - return false - } - return bm.sentToBDN -} - -// HasConfirmedBlock indicates if the block has been confirmed by a reliable source -func (c *Chain) HasConfirmedBlock(hash ethcommon.Hash) bool { - bm, ok := c.getBlockMetadata(hash) - if !ok { - return false - } - return bm.confirmed -} - -// HasConfirmationSentToBDN indicates if block confirmation has been sent to the BDN -func (c *Chain) HasConfirmationSentToBDN(hash ethcommon.Hash) bool { - c.chainLock.Lock() - defer c.chainLock.Unlock() - - bm, ok := c.getBlockMetadata(hash) - if !ok { - return false - } - - return bm.cnfMsgSent -} - -// MarkSentToBDN marks a block as having been sent to the BDN, so it does not need to be sent again in the future -func (c *Chain) MarkSentToBDN(hash ethcommon.Hash) { - c.chainLock.Lock() - defer c.chainLock.Unlock() - - bm, ok := c.getBlockMetadata(hash) - if !ok { - return - } - - bm.sentToBDN = true - c.blockHashMetadata.Set(hash.String(), bm) -} - -// MarkConfirmationSentToBDN marks a block confirmation as having been sent to the BDN, so it does not need to be sent again in the future -func (c *Chain) MarkConfirmationSentToBDN(hash ethcommon.Hash) { - c.chainLock.Lock() - defer c.chainLock.Unlock() - - bm, ok := c.getBlockMetadata(hash) - if !ok { - return - } - - bm.cnfMsgSent = true - c.blockHashMetadata.Set(hash.String(), bm) -} - -// GetBodies assembles and returns a set of block bodies -func (c *Chain) GetBodies(hashes []ethcommon.Hash) ([]interfaces.ReadOnlyBeaconBlockBody, error) { - bodies := make([]interfaces.ReadOnlyBeaconBlockBody, 0, len(hashes)) - for _, hash := range hashes { - body, ok := c.getBlockBody(hash) - if !ok { - return nil, ErrBodyNotFound - } - bodies = append(bodies, body) - } - return bodies, nil -} - -// GetHeaders assembles and returns a set of headers -func (c *Chain) GetHeaders(start eth.HashOrNumber, count int, skip int, reverse bool) ([]*ethpb.SignedBeaconBlockHeader, error) { - c.chainLock.RLock() - defer c.chainLock.RUnlock() - - if count < 0 { - return nil, ErrQueryAmountIsNotValid - } - requestedHeaders := make([]*ethpb.SignedBeaconBlockHeader, 0, count) - - var ( - originHash ethcommon.Hash - originHeight uint64 - ) - - // figure out query scheme, then initialize requested headers with the first entry - if start.Number > 0 { - originHeight = start.Number - - if originHeight > c.chainState.head().height { - return nil, ErrFutureHeaders - } - - tail := c.chainState.tail() - if tail != nil && originHeight < tail.height { - return nil, ErrAncientHeaders - } - - originHeader, err := c.getHeaderAtHeight(originHeight) - if err != nil { - return nil, err - } - - // originHeader may be nil if a block in the future is requested, return empty headers in that case - if originHeader != nil { - requestedHeaders = append(requestedHeaders, originHeader.Header) - } - } else if start.Hash != (ethcommon.Hash{}) { - originHash = start.Hash - bm, ok := c.getBlockMetadata(originHash) - originHeight = bm.height - - if !ok { - return nil, fmt.Errorf("could not retrieve a corresponding height for block: %v", originHash) - } - originHeader, ok := c.getBlockHeader(originHeight, originHash) - if !ok { - return nil, fmt.Errorf("no header was with slot %v and hash %v", originHeight, originHash) - } - requestedHeaders = append(requestedHeaders, originHeader.Header) - } else { - return nil, ErrInvalidRequest - } - - // if only 1 header was requested, return result - if count == 1 { - return requestedHeaders, nil - } - - directionalMultiplier := 1 - increment := skip + 1 - if reverse { - directionalMultiplier = -1 - } - increment *= directionalMultiplier - - nextHeight := int(originHeight) + increment - if len(c.chainState) == 0 { - return nil, fmt.Errorf("no entries stored at slot: %v", nextHeight) - } - - // iterate through all requested headers and fetch results - for height := nextHeight; len(requestedHeaders) < count; height += increment { - header, err := c.getHeaderAtHeight(uint64(height)) - if err != nil { - return nil, err - } - - if header == nil { - log.Tracef("requested slot %v is beyond best slot: ok", height) - break - } - - requestedHeaders = append(requestedHeaders, header.Header) - } - - return requestedHeaders, nil -} - -// BlockAtDepth returns the blockRefChain with depth from the head of the chain -func (c *Chain) BlockAtDepth(chainDepth int) (interfaces.ReadOnlySignedBeaconBlock, error) { - if len(c.chainState) <= chainDepth { - return nil, fmt.Errorf("not enough block in the chain state with length %v for depth lookup with depth of %v", len(c.chainState), chainDepth) - } - ref := c.chainState[chainDepth] - header, err := c.getHeaderAtHeight(ref.height) - if err != nil { - return nil, err - } - body, ok := c.getBlockBody(ref.hash) - if !ok { - return nil, fmt.Errorf("cannot get block body for block %v with slot %v in the chain state ", ref.hash, ref.height) - } - block, err := newSignedBeaconBlock(header.version, header.Header, body) - if err != nil { - return nil, fmt.Errorf("cannot merge head and body for block %v", ref.hash) - } - - return block, nil -} - -// HeadHeight returns head height -func (c *Chain) HeadHeight() uint64 { - return c.chainState.head().height -} - -// should be called with c.chainLock held -func (c *Chain) updateChainState(height uint64, hash ethcommon.Hash, parentHash ethcommon.Hash) int { - if len(c.chainState) == 0 { - c.chainState = append(c.chainState, blockRef{ - height: height, - hash: hash, - }) - return 1 - } - - chainHead := c.chainState[0] - - // canonical block, append immediately - if chainHead.height+1 == height && chainHead.hash == parentHash { - c.chainState = append([]blockRef{{height, hash}}, c.chainState...) - return 1 - } - - // non-canonical block in the past, ignore for now - if height <= chainHead.height { - return 0 - } - - // better block than current head, try reorganizing with new best block - missingEntries := make([]blockRef, 0, height-chainHead.height) - - headHeight := height - headHash := hash - - // build chainstate from previous head to the latest block - // e.g. suppose we had 10 on our head, and we just received block 14; we try to fill in entries 11-13 and check if that's all ok - for ; headHeight > chainHead.height; headHeight-- { - headHeader, ok := c.getBlockHeader(headHeight, headHash) - if !ok { - log.Debugf("cannot update chainstate, missing %d blocks", c.minValidChain-len(missingEntries)) - return 0 - } - - missingEntries = append(missingEntries, blockRef{height: headHeight, hash: headHash}) - headHash = ethcommon.BytesToHash(headHeader.Header.GetHeader().GetParentRoot()) - - // suppose our head is 10, and we receive block 15-100 (for some reason 11-14 are never received), then we'll switch over the chain to be 15-100 as soon as the valid chain is >= c.minValidChain length - if len(missingEntries) >= c.minValidChain { - c.chainState = missingEntries - return len(missingEntries) - } - } - - // chainstate was successfully reconciled (some entries were just missing), nothing else needed - if headHeight == chainHead.height && headHash == chainHead.hash { - c.chainState = append(missingEntries, c.chainState...) - return len(missingEntries) - } - - // reorganization is required, look back until c.maxReorg - // e.g. suppose we had 10 on our head, and we just received block 14 - // - we filled 11-13, but 10b is 11's parent, so we iterate backward until we find a common ancestor - // - for example, suppose 9 doesn't match 10b's parent, but 8 matches 9b's parent, then we stop there - // - if this takes too long (> c.maxReorg needed), then we just trim the chainstate to c.maxReorg - - i := 0 - for ; i < len(c.chainState); i++ { - chainRef := c.chainState[i] - if headHash == chainRef.hash { - // common ancestor found, break and recombine chains - break - } - - headHeader, ok := c.getBlockHeader(headHeight, headHash) - if !ok { - // TODO: log anything? chainstate can't be reconciled - return 0 - } - - missingEntries = append(missingEntries, blockRef{height: headHeight, hash: headHash}) - headHash = ethcommon.BytesToHash(headHeader.Header.GetHeader().GetParentRoot()) - headHeight-- - - // exceeded c.maxReorg, trim the chainstate - if i+1 >= c.maxReorg { - c.chainState = missingEntries - return len(missingEntries) - } - } - - c.chainState = append(missingEntries, c.chainState[i:]...) - return len(missingEntries) -} - -// fetches correct header from chain, not store (require lock?) -func (c *Chain) getHeaderAtHeight(height uint64) (*ethBeaconHeader, error) { - if len(c.chainState) == 0 { - return nil, fmt.Errorf("%v: no header at slot %v", c.chainState, height) - } - - head := c.chainState[0] - requestedIndex := int(head.height - height) - - // requested block in the future, ok to break with no header - if requestedIndex < 0 { - return nil, nil - } - - // requested block too far in the past, fail out - if requestedIndex >= len(c.chainState) { - return nil, fmt.Errorf("%v: no header at slot %v", c.chainState, height) - } - - header, ok := c.getBlockHeader(height, c.chainState[requestedIndex].hash) - - // block in chainstate seems to no longer be in storage, error out - if !ok { - return nil, fmt.Errorf("%v: no header at slot %v", c.chainState, height) - } - return header, nil -} - -func (c *Chain) storeBlock(hash ethcommon.Hash, block interfaces.ReadOnlySignedBeaconBlock, source BlockSource) error { - header, err := block.Header() - if err != nil { - return fmt.Errorf("could not get block header: %v", err) - } - - c.storeBlockHeader(block.Version(), hash, header, source) - c.storeBlockBody(hash, block.Block().Body()) - - return nil -} - -func (c *Chain) getBlockHeader(height uint64, hash ethcommon.Hash) (*ethBeaconHeader, bool) { - headers, ok := c.getHeadersAtHeight(height) - if !ok { - return nil, ok - } - for _, header := range headers { - if bytes.Equal(header.hash.Bytes(), hash.Bytes()) { - return &header, true - } - } - return nil, false -} - -func (c *Chain) storeBlockHeader(version int, hash ethcommon.Hash, header *ethpb.SignedBeaconBlockHeader, source BlockSource) { - height := uint64(header.GetHeader().Slot) - c.storeHeaderAtHeight(hash, version, height, header) - c.storeBlockMetadata(hash, height, source == BSBlockchain, false) -} - -func (c *Chain) getBlockMetadata(hash ethcommon.Hash) (blockMetadata, bool) { - bm, ok := c.blockHashMetadata.Get(hash.String()) - if !ok { - return blockMetadata{}, ok - } - return bm.(blockMetadata), ok -} - -func (c *Chain) storeBlockMetadata(hash ethcommon.Hash, height uint64, confirmed bool, cnfMsgSent bool) { - set := c.blockHashMetadata.SetIfAbsent(hash.String(), blockMetadata{height, false, confirmed, cnfMsgSent}) - if !set { - bm, _ := c.getBlockMetadata(hash) - bm.confirmed = bm.confirmed || confirmed - bm.cnfMsgSent = bm.cnfMsgSent || cnfMsgSent - c.blockHashMetadata.Set(hash.String(), bm) - } -} - -func (c *Chain) removeBlockMetadata(hash ethcommon.Hash) { - c.blockHashMetadata.Remove(hash.String()) -} - -func (c *Chain) hasHeader(hash ethcommon.Hash) bool { - // always corresponds to a header stored at c.heightToBlockHeaders - return c.blockHashMetadata.Has(hash.String()) -} - -func (c *Chain) getHeadersAtHeight(height uint64) ([]ethBeaconHeader, bool) { - rawHeaders, ok := c.heightToBlockHeaders.Get(strconv.FormatUint(height, 10)) - if !ok { - return nil, ok - } - - return rawHeaders.([]ethBeaconHeader), ok -} - -func (c *Chain) storeHeaderAtHeight(hash ethcommon.Hash, version int, height uint64, header *ethpb.SignedBeaconBlockHeader) { - eh := ethBeaconHeader{ - Header: header, - hash: hash, - version: version, - } - c.storeEthHeaderAtHeight(height, eh) -} - -// generally avoid calling this function directly -func (c *Chain) storeEthHeaderAtHeight(height uint64, eh ethBeaconHeader) { - // concurrent calls to this function are ok, only needs to be exclusionary with clean - c.headerLock.RLock() - defer c.headerLock.RUnlock() - - heightStr := strconv.FormatUint(height, 10) - - ok := c.heightToBlockHeaders.SetIfAbsent(heightStr, []ethBeaconHeader{eh}) - if !ok { - rawHeaders, _ := c.heightToBlockHeaders.Get(heightStr) - ethHeaders := rawHeaders.([]ethBeaconHeader) - ethHeaders = append(ethHeaders, eh) - c.heightToBlockHeaders.Set(heightStr, ethHeaders) - } -} - -func (c *Chain) storeBlockBody(hash ethcommon.Hash, body interfaces.ReadOnlyBeaconBlockBody) { - c.blockHashToBody.Set(hash.String(), body) -} - -func (c *Chain) getBlockBody(hash ethcommon.Hash) (interfaces.ReadOnlyBeaconBlockBody, bool) { - body, ok := c.blockHashToBody.Get(hash.String()) - if !ok { - return nil, ok - } - - return body.(interfaces.ReadOnlyBeaconBlockBody), ok -} - -func (c *Chain) hasBody(hash ethcommon.Hash) bool { - return c.blockHashToBody.Has(hash.String()) -} - -func (c *Chain) removeBlockBody(hash ethcommon.Hash) { - c.blockHashToBody.Remove(hash.String()) -} - -// removes all info corresponding to a given block in storage -func (c *Chain) pruneHash(hash ethcommon.Hash) { - c.removeBlockMetadata(hash) - c.removeBlockBody(hash) -} - -func (c *Chain) clean(maxSize int) (lowestCleaned int, highestCleaned int, numCleaned int) { - c.headerLock.Lock() - defer c.headerLock.Unlock() - - c.chainLock.Lock() - defer c.chainLock.Unlock() - - // Find largest height from headers if chainState is empty - // This may happened if no connection to node was established but we receiving blocks from BDN - if len(c.chainState) == 0 { - var maxHeight int - for elem := range c.heightToBlockHeaders.IterBuffered() { - heightStr := elem.Key - height, err := strconv.Atoi(heightStr) - if err != nil { - log.Errorf("failed to convert slot %v from string to integer: %v", heightStr, err) - continue - } - - if height > maxHeight { - maxHeight = height - } - } - - lowestCleaned = maxHeight - } else { - lowestCleaned = int(c.chainState[0].height) - } - - numCleaned = 0 - highestCleaned = 0 - - // minimum height to not be cleaned - minHeight := lowestCleaned - maxSize + 1 - numHeadersStored := c.heightToBlockHeaders.Count() - - if numHeadersStored >= maxSize { - for elem := range c.heightToBlockHeaders.IterBuffered() { - heightStr := elem.Key - height, err := strconv.Atoi(heightStr) - if err != nil { - log.Errorf("failed to convert slot %v from string to integer: %v", heightStr, err) - continue - } - if height < minHeight { - headers := elem.Val.([]ethBeaconHeader) - c.heightToBlockHeaders.Remove(heightStr) - for _, header := range headers { - hash := header.hash - c.pruneHash(hash) - - numCleaned++ - if height < lowestCleaned { - lowestCleaned = height - } - if height > highestCleaned { - highestCleaned = height - } - } - } - } - - chainStatePruned := 0 - if len(c.chainState) > maxSize { - chainStatePruned = len(c.chainState) - maxSize - c.chainState = c.chainState[:maxSize] - } - - log.Debugf("cleaned block storage (previous size %v out of max %v): %v block headers from %v to %v, pruning %v elements off of chainstate", numHeadersStored, maxSize, numCleaned, lowestCleaned, highestCleaned, chainStatePruned) - } else { - log.Debugf("skipping block storage cleanup, only had %v block headers out of a limit of %v", numHeadersStored, maxSize) - } - return -} - -func newSignedBeaconBlock(ver int, header *ethpb.SignedBeaconBlockHeader, body interfaces.ReadOnlyBeaconBlockBody) (interfaces.ReadOnlySignedBeaconBlock, error) { - var err error - - var sb interface{} - switch ver { - case version.Phase0: - sb, err = newSignedBeaconBlockPhase0(header, body) - if err != nil { - return nil, err - } - case version.Altair: - sb, err = newSignedBeaconBlockAltair(header, body) - if err != nil { - return nil, err - } - case version.Bellatrix: - sb, err = newSignedBeaconBlockBellatrix(header, body) - if err != nil { - return nil, err - } - case version.Capella: - sb, err = newSignedBeaconBlockCapella(header, body) - if err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("block version %v is not recognized", ver) - } - - return blocks.NewSignedBeaconBlock(sb) -} - -func newSignedBeaconBlockPhase0(header *ethpb.SignedBeaconBlockHeader, body interfaces.ReadOnlyBeaconBlockBody) (*ethpb.SignedBeaconBlock, error) { - randaoReveal := body.RandaoReveal() - graffiti := body.Graffiti() - - return ðpb.SignedBeaconBlock{ - Block: ðpb.BeaconBlock{ - Slot: header.GetHeader().GetSlot(), - ProposerIndex: header.GetHeader().GetProposerIndex(), - ParentRoot: header.GetHeader().GetParentRoot(), - StateRoot: header.GetHeader().GetStateRoot(), - Body: ðpb.BeaconBlockBody{ - RandaoReveal: randaoReveal[:], - Eth1Data: body.Eth1Data(), - Graffiti: graffiti[:], - ProposerSlashings: body.ProposerSlashings(), - AttesterSlashings: body.AttesterSlashings(), - Attestations: body.Attestations(), - Deposits: body.Deposits(), - VoluntaryExits: body.VoluntaryExits(), - }, - }, - Signature: header.GetSignature(), - }, nil -} - -func newSignedBeaconBlockAltair(header *ethpb.SignedBeaconBlockHeader, body interfaces.ReadOnlyBeaconBlockBody) (*ethpb.SignedBeaconBlockAltair, error) { - hash, err := body.HashTreeRoot() - if err != nil { - return nil, errors.New("could not calculate hash") - } - - syncAggregate, err := body.SyncAggregate() - if err != nil { - return nil, fmt.Errorf("could not get block %v sync aggregate: %v", hash, err) - } - - randaoReveal := body.RandaoReveal() - graffiti := body.Graffiti() - - return ðpb.SignedBeaconBlockAltair{ - Block: ðpb.BeaconBlockAltair{ - Slot: header.GetHeader().GetSlot(), - ProposerIndex: header.GetHeader().GetProposerIndex(), - ParentRoot: header.GetHeader().GetParentRoot(), - StateRoot: header.GetHeader().GetStateRoot(), - Body: ðpb.BeaconBlockBodyAltair{ - RandaoReveal: randaoReveal[:], - Eth1Data: body.Eth1Data(), - Graffiti: graffiti[:], - ProposerSlashings: body.ProposerSlashings(), - AttesterSlashings: body.AttesterSlashings(), - Attestations: body.Attestations(), - Deposits: body.Deposits(), - VoluntaryExits: body.VoluntaryExits(), - SyncAggregate: syncAggregate, - }, - }, - Signature: header.GetSignature(), - }, nil -} - -func newSignedBeaconBlockBellatrix(header *ethpb.SignedBeaconBlockHeader, body interfaces.ReadOnlyBeaconBlockBody) (*ethpb.SignedBeaconBlockBellatrix, error) { - hash, err := body.HashTreeRoot() - if err != nil { - return nil, errors.New("could not calculate hash") - } - - syncAggregate, err := body.SyncAggregate() - if err != nil { - return nil, fmt.Errorf("could not get block %v sync aggregate: %v", hash, err) - } - - execution, err := body.Execution() - if err != nil { - return nil, fmt.Errorf("could not get block %v execution: %v", hash, err) - } - - bellatrixExecution, err := execution.PbBellatrix() - if err != nil { - return nil, fmt.Errorf("could not get block %v execution payload: %v", hash, err) - } - - randaoReveal := body.RandaoReveal() - graffiti := body.Graffiti() - - return ðpb.SignedBeaconBlockBellatrix{ - Block: ðpb.BeaconBlockBellatrix{ - Slot: header.GetHeader().GetSlot(), - ProposerIndex: header.GetHeader().GetProposerIndex(), - ParentRoot: header.GetHeader().GetParentRoot(), - StateRoot: header.GetHeader().GetStateRoot(), - Body: ðpb.BeaconBlockBodyBellatrix{ - RandaoReveal: randaoReveal[:], - Eth1Data: body.Eth1Data(), - Graffiti: graffiti[:], - ProposerSlashings: body.ProposerSlashings(), - AttesterSlashings: body.AttesterSlashings(), - Attestations: body.Attestations(), - Deposits: body.Deposits(), - VoluntaryExits: body.VoluntaryExits(), - SyncAggregate: syncAggregate, - ExecutionPayload: bellatrixExecution, - }, - }, - Signature: header.GetSignature(), - }, nil -} - -func newSignedBeaconBlockCapella(header *ethpb.SignedBeaconBlockHeader, body interfaces.ReadOnlyBeaconBlockBody) (*ethpb.SignedBeaconBlockCapella, error) { - hash, err := body.HashTreeRoot() - if err != nil { - return nil, errors.New("could not calculate hash") - } - - syncAggregate, err := body.SyncAggregate() - if err != nil { - return nil, fmt.Errorf("could not get block %v sync aggregate: %v", hash, err) - } - - execution, err := body.Execution() - if err != nil { - return nil, fmt.Errorf("could not get block %v execution: %v", hash, err) - } - - capellaExecution, err := execution.PbCapella() - if err != nil { - return nil, fmt.Errorf("could not get block %v capella execution: %v", hash, err) - } - - randaoReveal := body.RandaoReveal() - graffiti := body.Graffiti() - - return ðpb.SignedBeaconBlockCapella{ - Block: ðpb.BeaconBlockCapella{ - Slot: header.GetHeader().GetSlot(), - ProposerIndex: header.GetHeader().GetProposerIndex(), - ParentRoot: header.GetHeader().GetParentRoot(), - StateRoot: header.GetHeader().GetStateRoot(), - Body: ðpb.BeaconBlockBodyCapella{ - RandaoReveal: randaoReveal[:], - Eth1Data: body.Eth1Data(), - Graffiti: graffiti[:], - ProposerSlashings: body.ProposerSlashings(), - AttesterSlashings: body.AttesterSlashings(), - Attestations: body.Attestations(), - Deposits: body.Deposits(), - VoluntaryExits: body.VoluntaryExits(), - SyncAggregate: syncAggregate, - ExecutionPayload: capellaExecution, - }, - }, - Signature: header.GetSignature(), - }, nil -} diff --git a/blockchain/beacon/chain_test.go b/blockchain/beacon/chain_test.go deleted file mode 100644 index b2d4aa6..0000000 --- a/blockchain/beacon/chain_test.go +++ /dev/null @@ -1,362 +0,0 @@ -package beacon - -import ( - "context" - "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestChain_AddBlock(t *testing.T) { - c := newChain(context.Background(), 0, 10, 5, 5, time.Hour, 1000) - - block1 := newBeaconBlock(t, 1, nil) - block2 := newBeaconBlock(t, 2, block1) - block3a := newBeaconBlock(t, 3, block2) - block3b := newBeaconBlock(t, 3, block2) - block4a := newBeaconBlock(t, 4, block3a) - block4b := newBeaconBlock(t, 4, block3b) - block5a := newBeaconBlock(t, 5, block4a) - block5b := newBeaconBlock(t, 5, block4b) - block6 := newBeaconBlock(t, 6, block5b) - - newHeads := addBlock(t, c, block1) - assert.Equal(t, 1, newHeads) - assertChainState(t, c, block1, 0, 1) - - newHeads = addBlock(t, c, block2) - assert.Equal(t, 1, newHeads) - assertChainState(t, c, block2, 0, 2) - assertChainState(t, c, block1, 1, 2) - - newHeads = addBlock(t, c, block3a) - assert.Equal(t, 1, newHeads) - assertChainState(t, c, block3a, 0, 3) - assertChainState(t, c, block2, 1, 3) - assertChainState(t, c, block1, 2, 3) - - newHeads = addBlock(t, c, block3b) - assert.Equal(t, 0, newHeads) - assertChainState(t, c, block3a, 0, 3) - assertChainState(t, c, block2, 1, 3) - assertChainState(t, c, block1, 2, 3) - - newHeads = addBlock(t, c, block4a) - assert.Equal(t, 1, newHeads) - assertChainState(t, c, block4a, 0, 4) - assertChainState(t, c, block3a, 1, 4) - assertChainState(t, c, block2, 2, 4) - assertChainState(t, c, block1, 3, 4) - - newHeads = addBlock(t, c, block4b) - assert.Equal(t, 0, newHeads) - assertChainState(t, c, block4a, 0, 4) - assertChainState(t, c, block3a, 1, 4) - assertChainState(t, c, block2, 2, 4) - assertChainState(t, c, block1, 3, 4) - - newHeads = addBlock(t, c, block5a) - assert.Equal(t, 1, newHeads) - assertChainState(t, c, block5a, 0, 5) - assertChainState(t, c, block4a, 1, 5) - assertChainState(t, c, block3a, 2, 5) - assertChainState(t, c, block2, 3, 5) - assertChainState(t, c, block1, 4, 5) - - newHeads = addBlock(t, c, block5b) - assert.Equal(t, 0, newHeads) - assertChainState(t, c, block5a, 0, 5) - assertChainState(t, c, block4a, 1, 5) - assertChainState(t, c, block3a, 2, 5) - assertChainState(t, c, block2, 3, 5) - assertChainState(t, c, block1, 4, 5) - - newHeads = addBlock(t, c, block6) - assert.Equal(t, 4, newHeads) - assertChainState(t, c, block6, 0, 6) - assertChainState(t, c, block5b, 1, 6) - assertChainState(t, c, block4b, 2, 6) - assertChainState(t, c, block3b, 3, 6) - assertChainState(t, c, block2, 4, 6) - assertChainState(t, c, block1, 5, 6) -} - -func TestChain_AddBlock_MissingBlocks(t *testing.T) { - c := newChain(context.Background(), 0, 10, 5, 3, time.Hour, 1000) - - block1 := newBeaconBlock(t, 1, nil) - block2 := newBeaconBlock(t, 2, block1) - block3 := newBeaconBlock(t, 3, block2) - block4a := newBeaconBlock(t, 4, block3) - block4b := newBeaconBlock(t, 4, block3) - block5 := newBeaconBlock(t, 5, block4b) - block6 := newBeaconBlock(t, 6, block5) - block7 := newBeaconBlock(t, 7, block6) - - addBlock(t, c, block1) - - newHeads := addBlock(t, c, block3) - assert.Equal(t, 0, newHeads) - assertChainState(t, c, block1, 0, 1) - - newHeads = addBlock(t, c, block2) - assert.Equal(t, 1, newHeads) - assertChainState(t, c, block2, 0, 2) - assertChainState(t, c, block1, 1, 2) - - // found block 3 and filled it in - newHeads = addBlock(t, c, block4a) - assert.Equal(t, 2, newHeads) - assertChainState(t, c, block4a, 0, 4) - - // add a bunch of entries that can't be added to chain (for some reason 4b is missing) - newHeads = addBlock(t, c, block5) - assert.Equal(t, 0, newHeads) - newHeads = addBlock(t, c, block6) - assert.Equal(t, 0, newHeads) - - // chain is long enough, don't care about 4b anymore - newHeads = addBlock(t, c, block7) - assert.Equal(t, 3, newHeads) - assertChainState(t, c, block7, 0, 3) - assertChainState(t, c, block6, 1, 3) - assertChainState(t, c, block5, 2, 3) -} - -func TestChain_AddBlock_LongFork(t *testing.T) { - c := newChain(context.Background(), 0, 10, 2, 5, time.Hour, 1000) - - block1 := newBeaconBlock(t, 1, nil) - block2a := newBeaconBlock(t, 2, block1) - block2b := newBeaconBlock(t, 2, block1) - block3a := newBeaconBlock(t, 3, block2a) - block3b := newBeaconBlock(t, 3, block2b) - block4a := newBeaconBlock(t, 4, block3a) - block4b := newBeaconBlock(t, 4, block3b) - block5a := newBeaconBlock(t, 5, block4a) - block5b := newBeaconBlock(t, 5, block4b) - block6 := newBeaconBlock(t, 6, block5b) - - addBlock(t, c, block1) - addBlock(t, c, block2a) - addBlock(t, c, block2b) - addBlock(t, c, block3a) - addBlock(t, c, block3b) - addBlock(t, c, block4a) - addBlock(t, c, block4b) - addBlock(t, c, block5a) - addBlock(t, c, block5b) - - assertChainState(t, c, block5a, 0, 5) - assertChainState(t, c, block4a, 1, 5) - assertChainState(t, c, block3a, 2, 5) - assertChainState(t, c, block2a, 3, 5) - assertChainState(t, c, block1, 4, 5) - - newHeads := addBlock(t, c, block6) - assert.Equal(t, 3, newHeads) - - assertChainState(t, c, block6, 0, 3) - assertChainState(t, c, block5b, 1, 3) - assertChainState(t, c, block4b, 2, 3) -} - -func TestChain_GetNewHeadsForBDN(t *testing.T) { - c := newChain(context.Background(), 0, 10, 5, 5, time.Hour, 1000) - - block1 := newBeaconBlock(t, 1, nil) - block2 := newBeaconBlock(t, 2, block1) - block3 := newBeaconBlock(t, 3, block2) - - addBlock(t, c, block1) - addBlock(t, c, block2) - - blocks, err := c.GetNewHeadsForBDN(2) - assert.Nil(t, err) - assert.Equal(t, blockHash(t, block2), blockHash(t, blocks[0])) - assert.Equal(t, blockHash(t, block1), blockHash(t, blocks[1])) - - _, err = c.GetNewHeadsForBDN(3) - assert.NotNil(t, err) - - c.MarkSentToBDN(blockHash(t, block2)) - addBlock(t, c, block3) - - blocks, err = c.GetNewHeadsForBDN(2) - assert.Nil(t, err) - assert.Equal(t, blockHash(t, block3), blockHash(t, blocks[0])) -} - -func TestChain_clean(t *testing.T) { - cleanInterval := 15 * time.Millisecond - c := newChain(context.Background(), 0, 10, 5, 5, cleanInterval, 3) - - block1 := newBeaconBlock(t, 1, nil) - block2 := newBeaconBlock(t, 2, block1) - block3 := newBeaconBlock(t, 3, block2) - block4 := newBeaconBlock(t, 4, block3) - block5 := newBeaconBlock(t, 5, block4) - block6 := newBeaconBlock(t, 6, block5) - - addBDNBlock(t, c, block1) // remove - addBDNBlock(t, c, block2) // remove - addBDNBlock(t, c, block3) - addBDNBlock(t, c, block4) - addBlock(t, c, block5) // chainstate head - addBDNBlock(t, c, block6) - - expectedHashes := map[common.Hash]struct{}{ - blockHash(t, block3): {}, - blockHash(t, block4): {}, - blockHash(t, block5): {}, - blockHash(t, block6): {}, - } - - assert.Equal(t, 6, c.heightToBlockHeaders.Count()) - - // Clean can block last block adding - // So better to use (blockCount + 1) * cleanInterval - time.Sleep(7 * cleanInterval) - - assert.Equal(t, 4, c.heightToBlockHeaders.Count()) - - for elem := range c.heightToBlockHeaders.IterBuffered() { - headers := elem.Val.([]ethBeaconHeader) - for _, header := range headers { - if _, ok := expectedHashes[header.hash]; !ok { - assert.Fail(t, "unexpected block", "height: %v", header.Header.Header.Slot) - } - delete(expectedHashes, header.hash) - } - c.heightToBlockHeaders.Remove(elem.Key) - } - - assert.Empty(t, expectedHashes) - assert.Zero(t, c.heightToBlockHeaders.Count()) -} - -func TestChain_cleanNoChainstate(t *testing.T) { - cleanInterval := 15 * time.Millisecond - c := newChain(context.Background(), 0, 10, 5, 5, cleanInterval, 3) - - block1 := newBeaconBlock(t, 1, nil) - block2 := newBeaconBlock(t, 2, block1) - block3 := newBeaconBlock(t, 3, block2) - block4 := newBeaconBlock(t, 4, block3) - - // No Blockchain block = no chainstate - addBDNBlock(t, c, block1) // remove - addBDNBlock(t, c, block2) - addBDNBlock(t, c, block3) - addBDNBlock(t, c, block4) // taking last BDN block as base - - expectedHashes := map[common.Hash]struct{}{ - blockHash(t, block2): {}, - blockHash(t, block3): {}, - blockHash(t, block4): {}, - } - - assert.Equal(t, 4, c.heightToBlockHeaders.Count()) - - // Clean can block last block adding - // So better to use (blockCount + 1) * cleanInterval - time.Sleep(5 * cleanInterval) - - assert.Equal(t, 3, c.heightToBlockHeaders.Count()) - - for elem := range c.heightToBlockHeaders.IterBuffered() { - headers := elem.Val.([]ethBeaconHeader) - for _, header := range headers { - if _, ok := expectedHashes[header.hash]; !ok { - assert.Fail(t, "unexpected block", "height: %v", header.Header.Header.Slot) - } - delete(expectedHashes, header.hash) - } - c.heightToBlockHeaders.Remove(elem.Key) - } - - assert.Empty(t, expectedHashes) - assert.Zero(t, c.heightToBlockHeaders.Count()) -} - -func newBeaconBlock(t *testing.T, slot int, prevBlock interfaces.ReadOnlySignedBeaconBlock) interfaces.ReadOnlySignedBeaconBlock { - // Blocks should not be the same - randaoReveal := make([]byte, 96) - rand.Read(randaoReveal) - - var parentRoot [32]byte - if prevBlock != nil { - var err error - parentRoot, err = prevBlock.Block().HashTreeRoot() - require.NoError(t, err) - } - - block := ðpb.SignedBeaconBlock{ - Block: ðpb.BeaconBlock{ - Slot: types.Slot(slot), - ParentRoot: parentRoot[:], - StateRoot: make([]byte, 32), - Body: ðpb.BeaconBlockBody{ - RandaoReveal: randaoReveal, - Eth1Data: ðpb.Eth1Data{ - DepositRoot: make([]byte, 32), - BlockHash: make([]byte, 32), - }, - Graffiti: make([]byte, 32), - Attestations: []*ethpb.Attestation{}, - AttesterSlashings: []*ethpb.AttesterSlashing{}, - Deposits: []*ethpb.Deposit{}, - ProposerSlashings: []*ethpb.ProposerSlashing{}, - VoluntaryExits: []*ethpb.SignedVoluntaryExit{}, - }, - }, - Signature: make([]byte, 96), - } - - blk, err := blocks.NewSignedBeaconBlock(block) - assert.NoError(t, err) - - return blk -} - -func addBDNBlock(t *testing.T, c *Chain, block interfaces.ReadOnlySignedBeaconBlock) int { - newHeads, err := c.AddBlock(block, BSBDN) - assert.NoError(t, err) - - return newHeads -} - -func addBlock(t *testing.T, c *Chain, block interfaces.ReadOnlySignedBeaconBlock) int { - newHeads, err := c.AddBlock(block, BSBlockchain) - assert.NoError(t, err) - - return newHeads -} - -func assertChainState(t *testing.T, c *Chain, block interfaces.ReadOnlySignedBeaconBlock, index int, length int) { - assert.Equal(t, length, len(c.chainState)) - assert.Equal(t, uint64(block.Block().Slot()), c.chainState[index].height) - assert.Equal(t, blockHash(t, block), c.chainState[index].hash) -} - -func blockHeader(t *testing.T, block interfaces.ReadOnlySignedBeaconBlock) *ethpb.SignedBeaconBlockHeader { - header, err := block.Header() - assert.NoError(t, err) - - return header -} - -func blockHash(t *testing.T, block interfaces.ReadOnlySignedBeaconBlock) common.Hash { - hash, err := block.Block().HashTreeRoot() - assert.NoError(t, err) - - return hash -} diff --git a/blockchain/beacon/deadlines.go b/blockchain/beacon/deadlines.go index ec40cf1..8a12118 100644 --- a/blockchain/beacon/deadlines.go +++ b/blockchain/beacon/deadlines.go @@ -6,7 +6,7 @@ import ( log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/libp2p/go-libp2p/core/network" - "github.com/prysmaticlabs/prysm/v3/config/params" + "github.com/prysmaticlabs/prysm/v4/config/params" ) var defaultReadDuration = params.BeaconNetworkConfig().TtfbTimeout diff --git a/blockchain/beacon/node.go b/blockchain/beacon/node.go index 8a7354f..e8cc95c 100644 --- a/blockchain/beacon/node.go +++ b/blockchain/beacon/node.go @@ -14,6 +14,7 @@ import ( log "github.com/bloXroute-Labs/gateway/v2/logger" bxTypes "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/libp2p/go-libp2p" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -27,17 +28,19 @@ import ( "github.com/libp2p/go-libp2p/p2p/transport/tcp" "github.com/pkg/errors" fastssz "github.com/prysmaticlabs/fastssz" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/encoder" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" - state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native" - "github.com/prysmaticlabs/prysm/v3/config/params" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - ecdsaprysm "github.com/prysmaticlabs/prysm/v3/crypto/ecdsa" - "github.com/prysmaticlabs/prysm/v3/network/forks" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/encoder" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/state" + state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native" + "github.com/prysmaticlabs/prysm/v4/config/params" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + prysmTypes "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" + ecdsaprysm "github.com/prysmaticlabs/prysm/v4/crypto/ecdsa" + "github.com/prysmaticlabs/prysm/v4/network/forks" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/time/slots" "google.golang.org/protobuf/proto" ) @@ -83,48 +86,28 @@ const ( var networkInitMapping = map[string]func(){ // Mainnet is default and has required values "Mainnet": func() {}, - "Prater": func() { + "Goerli": func() { params.UsePraterNetworkConfig() params.SetActive(params.PraterConfig()) + types.InitializeDataMaps() }, "Zhejiang": func() { cfg := params.MainnetConfig().Copy() - cfg.MinGenesisTime = 1675263480 - cfg.GenesisDelay = 120 + cfg.MinGenesisTime = 1680523200 + cfg.GenesisDelay = 60 cfg.ConfigName = "Zhejiang" - cfg.GenesisForkVersion = []byte{0x00, 0x00, 0x00, 0x69} - cfg.SecondsPerETH1Block = 14 - cfg.DepositChainID = 1337803 - cfg.DepositNetworkID = 1337803 + cfg.GenesisForkVersion = []byte{0x10, 0x00, 0x00, 0x48} + cfg.SecondsPerETH1Block = 12 + cfg.DepositChainID = 1 + cfg.DepositNetworkID = 1 cfg.AltairForkEpoch = 0 - cfg.AltairForkVersion = []byte{0x0, 0x0, 0x00, 0x70} + cfg.AltairForkVersion = []byte{0x20, 0x00, 0x00, 0x48} cfg.BellatrixForkEpoch = 0 - cfg.BellatrixForkVersion = []byte{0x0, 0x0, 0x00, 0x71} - cfg.CapellaForkEpoch = 1350 - cfg.CapellaForkVersion = []byte{0x0, 0x0, 0x00, 0x72} + cfg.BellatrixForkVersion = []byte{0x30, 0x00, 0x00, 0x48} + cfg.CapellaForkEpoch = 675 + cfg.CapellaForkVersion = []byte{0x40, 0x00, 0x00, 0x48} cfg.TerminalTotalDifficulty = "0" - cfg.DepositContractAddress = "0x4242424242424242424242424242424242424242" - cfg.InitializeForkSchedule() - params.SetActive(cfg) - types.InitializeDataMaps() - }, - "Goerli": func() { - cfg := params.MainnetConfig().Copy() - cfg.MinGenesisTime = 1614588812 - cfg.GenesisDelay = 1919188 - cfg.ConfigName = "Goerli" - cfg.GenesisForkVersion = []byte{0x00, 0x00, 0x10, 0x20} - cfg.SecondsPerETH1Block = 14 - cfg.DepositChainID = 5 - cfg.DepositNetworkID = 5 - cfg.AltairForkEpoch = 0 - cfg.AltairForkVersion = []byte{0x0, 0x0, 0x10, 0x20} - cfg.BellatrixForkEpoch = 112260 - cfg.BellatrixForkVersion = []byte{0x02, 0x0, 0x10, 0x20} - cfg.CapellaForkEpoch = 162304 - cfg.CapellaForkVersion = []byte{0x03, 0x0, 0x10, 0x20} - cfg.TerminalTotalDifficulty = "10790000" - cfg.DepositContractAddress = "0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b" + cfg.DepositContractAddress = "0x6f22fFbC56eFF051aECF839396DD1eD9aD6BBA9D" cfg.InitializeForkSchedule() params.SetActive(cfg) types.InitializeDataMaps() @@ -143,6 +126,20 @@ func initNetwork(networkName string) error { return nil } +type topicSubscription struct { + ctx context.Context + cancel context.CancelFunc + topic *pubsub.Topic + subscription *pubsub.Subscription +} + +func (ts *topicSubscription) close() error { + ts.subscription.Cancel() // cancel subscription + err := ts.topic.Close() // close topic + ts.cancel() // close subscription listener + return err +} + // Node is beacon node type Node struct { ctx context.Context @@ -155,11 +152,10 @@ type Node struct { host host.Host pubSub *pubsub.PubSub - bridge blockchain.Bridge - blockProcessor *blockProcessor + bridge blockchain.Bridge peers peers - topicMap sync.Map + topicMap *syncmap.SyncMap[string, *topicSubscription] encoding encoder.NetworkEncoding @@ -171,11 +167,11 @@ type Node struct { } // NewNode creates beacon node -func NewNode(parent context.Context, networkName string, config *network.EthConfig, chain *Chain, genesisFilePath string, bridge blockchain.Bridge) (*Node, error) { - return newNode(parent, networkName, config, chain, genesisFilePath, bridge, &utils.RealClock{}) +func NewNode(parent context.Context, networkName string, config *network.EthConfig, genesisFilePath string, bridge blockchain.Bridge) (*Node, error) { + return newNode(parent, networkName, config, genesisFilePath, bridge, &utils.RealClock{}) } -func newNode(parent context.Context, networkName string, config *network.EthConfig, chain *Chain, genesisFilePath string, bridge blockchain.Bridge, clock utils.Clock) (*Node, error) { +func newNode(parent context.Context, networkName string, config *network.EthConfig, genesisFilePath string, bridge blockchain.Bridge, clock utils.Clock) (*Node, error) { logCtx := log.WithField("connType", "beacon") if err := initNetwork(networkName); err != nil { @@ -228,6 +224,7 @@ func newNode(parent context.Context, networkName string, config *network.EthConf host: host, bridge: bridge, peers: newPeers(), + topicMap: syncmap.NewStringMapOf[*topicSubscription](), encoding: encoder.SszNetworkEncoder{}, cancel: cancel, log: logCtx, @@ -237,8 +234,6 @@ func newNode(parent context.Context, networkName string, config *network.EthConf return nil, fmt.Errorf("could not add peers %v", err) } - n.blockProcessor = newBlockProcessor(ctx, config, chain, bridge, n.BroadcastBlock, n.log) - psOpts := n.pubsubOptions() n.pubSub, err = pubsub.NewGossipSub(ctx, host, psOpts...) if err != nil { @@ -292,50 +287,90 @@ func newNode(parent context.Context, networkName string, config *network.EthConf }, }) + // todo: do we need to unsubscribe from these as well? n.subscribeRPC(p2p.RPCStatusTopicV1, n.statusRPCHandler) n.subscribeRPC(p2p.RPCGoodByeTopicV1, n.goodbyeRPCHandler) n.subscribeRPC(p2p.RPCPingTopicV1, n.pingRPCHandler) - // TODO: check if Altair update n.subscribeRPC(p2p.RPCMetaDataTopicV2, n.metadataRPCHandler) return n, nil } -// Start starts beacon node -func (n *Node) Start() error { - n.log.Infof("Starting P2P beacon node peer ID: p2p/%v", n.host.ID()) +func (n *Node) scheduleCapellaForkUpdate() error { + // TODO: do for all forks - go n.ensurePeerConnections() - go n.handleBDNBridge() - go n.sendStatusRequests() + currentSlot := slots.CurrentSlot(n.genesisState.GenesisTime()) + currentEpoch := slots.ToEpoch(currentSlot) - if err := n.subscribe(p2p.BlockSubnetTopicFormat, n.blockSubscriber); err != nil { - return err + // Check if we haven't passed capella udpate yet + if currentEpoch >= params.BeaconConfig().CapellaForkEpoch { + return nil } - // Required to be on top gossip score rating and not be disconnected by prysm - dontCare := func(msg *pubsub.Message) {} - - if err := n.subscribe(p2p.AggregateAndProofSubnetTopicFormat, dontCare); err != nil { - return err + capellaTime, err := epochStartTime(n.genesisState.GenesisTime(), params.BeaconConfig().CapellaForkEpoch) + if err != nil { + return fmt.Errorf("could not get capella time: %v", err) } - if err := n.subscribe(p2p.ProposerSlashingSubnetTopicFormat, dontCare); err != nil { - return err + timeInEpoch := time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot*uint64(params.BeaconConfig().SlotsPerEpoch)) + + // Subscribe to Capella topics before update and unsubscribe Bellatrix topics after. + // So we maintain two sets of subscriptions during the merge. + epochBeforeCapellaTime := capellaTime.Add(-timeInEpoch) // 1 full epoch before Capella merge + epochAfterCapellaTime := capellaTime.Add(timeInEpoch) // 1 full epoch after Capella merge + + currentForkDigest, err := n.currentForkDigest() + if err != nil { + return fmt.Errorf("could not get current fork digest: %v", err) } - if err := n.subscribe(p2p.AttesterSlashingSubnetTopicFormat, dontCare); err != nil { - return err + capellaForkDigest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().CapellaForkEpoch, n.genesisState.GenesisValidatorsRoot()) + if err != nil { + return fmt.Errorf("could not get capella fork digest: %v", err) } - if err := n.subscribe(p2p.SyncContributionAndProofSubnetTopicFormat, dontCare); err != nil { - return err + if n.clock.Now().After(epochBeforeCapellaTime) { + // Gateway started in the middle between epochs during the update + if err := n.subscribeAll(capellaForkDigest); err != nil { + n.log.Errorf("could not subscribe after shanghai update: %v", err) + } + } else { + // Gateway started before the update + n.clock.AfterFunc(n.clock.Now().Sub(epochBeforeCapellaTime), func() { + if err := n.subscribeAll(capellaForkDigest); err != nil { + n.log.Errorf("could not subscribe after shanghai update: %v", err) + } + }) } + n.clock.AfterFunc(epochAfterCapellaTime.Sub(n.clock.Now()), func() { + n.unsubscribeAll(currentForkDigest) + }) + return nil } +// Start starts beacon node +func (n *Node) Start() error { + n.log.Infof("Starting P2P beacon node peer ID: p2p/%v", n.host.ID()) + + go n.ensurePeerConnections() + go n.handleBDNBridge() + go n.sendStatusRequests() + + if err := n.scheduleCapellaForkUpdate(); err != nil { + return fmt.Errorf("could not schedule capella fork update: %v", err) + } + + currentForkDigest, err := n.currentForkDigest() + if err != nil { + return fmt.Errorf("could not get current fork digest: %v", err) + } + + return n.subscribeAll(currentForkDigest) +} + func (n *Node) sendStatusRequests() { ticker := n.clock.Ticker(time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot)) @@ -452,11 +487,17 @@ func (n *Node) CanSubscribe(topic string) bool { n.log.Errorf("Could not determine Bellatrix fork digest: %v", err) return false } + capellaForkDigest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().CapellaForkEpoch, n.genesisState.GenesisValidatorsRoot()) + if err != nil { + n.log.Errorf("Could not determine Capella fork digest: %v", err) + return false + } switch parts[2] { case fmt.Sprintf("%x", phase0ForkDigest): case fmt.Sprintf("%x", altairForkDigest): case fmt.Sprintf("%x", bellatrixForkDigest): + case fmt.Sprintf("%x", capellaForkDigest): default: return false } @@ -464,28 +505,79 @@ func (n *Node) CanSubscribe(topic string) bool { return parts[4] == encoder.ProtocolSuffixSSZSnappy } -func (n *Node) subscribe(topic string, handler func(msg *pubsub.Message)) error { - digest, err := n.currentForkDigest() - if err != nil { +func (n *Node) unsubscribeAll(digest [4]byte) { + n.topicMap.Range(func(k string, sub *topicSubscription) bool { + // Skip if the topic does not contain the digest + if !strings.Contains(k, fmt.Sprintf("%x", digest)) { + return true + } + + if err := sub.close(); err != nil { + n.log.Warnf("could not close subscription, topic: %v: %v", k, err) + } else { + n.log.Infof("closed subscription, topic: %v", k) + } + + return true + }) + + n.topicMap.Clear() +} + +func (n *Node) subscribeAll(digest [4]byte) error { + // Required to be on top gossip score rating and not be disconnected by prysm + dontCare := func(msg *pubsub.Message) {} + + if err := n.subscribe(digest, p2p.BlockSubnetTopicFormat, n.blockSubscriber); err != nil { return err } - pbTopic, err := n.pubSub.Join(fmt.Sprintf(topic+n.encoding.ProtocolSuffix(), digest)) - if err != nil { + if err := n.subscribe(digest, p2p.AggregateAndProofSubnetTopicFormat, dontCare); err != nil { + return err + } + + if err := n.subscribe(digest, p2p.ProposerSlashingSubnetTopicFormat, dontCare); err != nil { + return err + } + + if err := n.subscribe(digest, p2p.AttesterSlashingSubnetTopicFormat, dontCare); err != nil { + return err + } + + if err := n.subscribe(digest, p2p.SyncContributionAndProofSubnetTopicFormat, dontCare); err != nil { return err } - n.topicMap.Store(topic, pbTopic) + return nil +} + +func (n *Node) subscribe(digest [4]byte, topic string, handler func(msg *pubsub.Message)) error { + topicWithDigest := fmt.Sprintf(topic+n.encoding.ProtocolSuffix(), digest) + pbTopic, err := n.pubSub.Join(topicWithDigest) + if err != nil { + return err + } sub, err := pbTopic.Subscribe() if err != nil { return err } + ctx, cancel := context.WithCancel(n.ctx) + n.topicMap.Store(topicWithDigest, &topicSubscription{ + ctx: ctx, + cancel: cancel, + topic: pbTopic, + subscription: sub, + }) + go func() { for { select { + case <-ctx.Done(): + return case <-n.ctx.Done(): + return default: msg, err := sub.Next(n.ctx) if err != nil { @@ -549,24 +641,17 @@ func (n *Node) blockSubscriber(msg *pubsub.Message) { } blockHashHex := ethcommon.BytesToHash(blockHash[:]).String() - execution, err := blk.Block().Body().Execution() - if err != nil { - logCtx.Errorf("could not get block[slot=%d,hash=%s] execution: %v", blk.Block().Slot(), blockHashHex, err) - return - } - - // If it pre-merge state execution is empty - if execution.BlockNumber() == 0 { - logCtx.Tracef("skip eth1 block[slot=%d,hash=%s] for pre-merge", blk.Block().Slot(), blockHashHex) + if blk.Block().Slot() <= currentSlot(n.genesisState.GenesisTime())-prysmTypes.Slot(n.config.IgnoreSlotCount) { + logCtx.Errorf("block[slot=%d,hash=%s] is too old to process", blk.Block().Slot(), blockHashHex) return } - if err := n.blockProcessor.ProcessBlockchainBlock(logCtx, *endpoint, blk); err != nil { + if err := sendBlockToBDN(n.clock, n.log, blk, n.bridge, *endpoint); err != nil { logCtx.Errorf("could not process block[slot=%d,hash=%s]: %v", blk.Block().Slot(), blockHashHex, err) return } - logCtx.Debugf("eth2 p2p block[slot=%d,hash=%s] sent to BDN", blk.Block().Slot(), blockHashHex) + logCtx.Tracef("received beacon block[slot=%d,hash=%s]", blk.Block().Slot(), blockHashHex) } func (n *Node) loadNodeEndpointFromPeerID(peerID libp2pPeer.ID) (*bxTypes.NodeEndpoint, error) { @@ -580,14 +665,18 @@ func (n *Node) loadNodeEndpointFromPeerID(peerID libp2pPeer.ID) (*bxTypes.NodeEn } func (n *Node) broadcast(topic string, msg proto.Message) error { - t, ok := n.topicMap.Load(topic) + digest, err := n.currentForkDigest() + if err != nil { + return fmt.Errorf("could not get current fork digest: %v", err) + } + + topicWithDigest := fmt.Sprintf(topic+n.encoding.ProtocolSuffix(), digest) + pbTopic, ok := n.topicMap.Load(topicWithDigest) if !ok { return errors.New("not started") } - pbTopic := t.(*pubsub.Topic) - - if len(pbTopic.ListPeers()) == 0 { + if len(pbTopic.topic.ListPeers()) == 0 { n.log.Warnf("no peers to broadcast") return nil } @@ -602,14 +691,23 @@ func (n *Node) broadcast(topic string, msg proto.Message) error { return fmt.Errorf("could not encode gossip: %v", err) } - return pbTopic.Publish(n.ctx, buf.Bytes()) + return pbTopic.topic.Publish(n.ctx, buf.Bytes()) } func (n *Node) handleBDNBridge() { for { select { case bdnBlock := <-n.bridge.ReceiveBeaconBlockFromBDN(): - n.blockProcessor.ProcessBDNBlock(bdnBlock) + beaconBlock, err := n.bridge.BlockBDNtoBlockchain(bdnBlock) + if err != nil { + n.log.Errorf("could not convert BDN block to beacon block: %v", err) + continue + } + + if err := n.BroadcastBlock(beaconBlock.(interfaces.ReadOnlySignedBeaconBlock)); err != nil { + n.log.Errorf("could not broadcast block: %v", err) + continue + } case <-n.ctx.Done(): return } diff --git a/blockchain/beacon/peer.go b/blockchain/beacon/peer.go index 1d8be69..8c938ec 100644 --- a/blockchain/beacon/peer.go +++ b/blockchain/beacon/peer.go @@ -5,7 +5,7 @@ import ( libp2pPeer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) type peers struct { diff --git a/blockchain/beacon/prysm_client.go b/blockchain/beacon/prysm_client.go index f1135d2..714df2f 100644 --- a/blockchain/beacon/prysm_client.go +++ b/blockchain/beacon/prysm_client.go @@ -8,11 +8,12 @@ import ( "github.com/bloXroute-Labs/gateway/v2/blockchain/network" log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/bloXroute-Labs/gateway/v2/types" + "github.com/bloXroute-Labs/gateway/v2/utils" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" - prysm "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + prysmTypes "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) @@ -21,29 +22,35 @@ const prysmClientTimeout = 10 * time.Second // PrysmClient is gRPC Prysm client type PrysmClient struct { - ctx context.Context - addr string - bridge blockchain.Bridge - endpoint types.NodeEndpoint - blockProcessor *blockProcessor - beaconBlock bool - log *log.Entry + ctx context.Context + clock utils.Clock + config *network.EthConfig + addr string + bridge blockchain.Bridge + endpoint types.NodeEndpoint + beaconBlock bool + log *log.Entry } // NewPrysmClient creates new Prysm gRPC client -func NewPrysmClient(ctx context.Context, config *network.EthConfig, chain *Chain, addr string, bridge blockchain.Bridge, endpoint types.NodeEndpoint) *PrysmClient { +func NewPrysmClient(ctx context.Context, config *network.EthConfig, addr string, bridge blockchain.Bridge, endpoint types.NodeEndpoint) *PrysmClient { + return newPrysmClient(ctx, config, addr, bridge, endpoint, utils.RealClock{}) +} + +func newPrysmClient(ctx context.Context, config *network.EthConfig, addr string, bridge blockchain.Bridge, endpoint types.NodeEndpoint, clock utils.Clock) *PrysmClient { log := log.WithFields(log.Fields{ "connType": "prysm", "remoteAddr": addr, }) return &PrysmClient{ - ctx: ctx, - addr: addr, - bridge: bridge, - endpoint: endpoint, - blockProcessor: newBlockProcessor(ctx, config, chain, bridge, nil, log), - log: log, + ctx: ctx, + clock: clock, + config: config, + addr: addr, + bridge: bridge, + endpoint: endpoint, + log: log, } } @@ -64,9 +71,9 @@ func (c *PrysmClient) run() { } defer conn.Close() - client := prysm.NewBeaconNodeValidatorClient(conn) + client := ethpb.NewBeaconNodeValidatorClient(conn) - stream, err := client.StreamBlocksAltair(context.TODO(), &prysm.StreamBlocksRequest{VerifiedOnly: false}) + stream, err := client.StreamBlocksAltair(c.ctx, ðpb.StreamBlocksRequest{VerifiedOnly: false}) if err != nil { c.log.Errorf("could not subscribe to Prysm: %v, retrying.", err) return @@ -103,24 +110,17 @@ func (c *PrysmClient) run() { } blockHashHex := ethcommon.BytesToHash(blockHash[:]).String() - execution, err := blk.Block().Body().Execution() - if err != nil { - c.log.Errorf("could not get block[slot=%d,hash=%s] execution: %v", blk.Block().Slot(), blockHashHex, err) - continue - } - - // If it pre-merge state execution is empty - if !c.beaconBlock && execution.BlockNumber() == 0 { - c.log.Tracef("skip eth1 block[slot=%d,hash=%s] for pre-merge", blk.Block().Slot(), blockHashHex) - continue + if blk.Block().Slot() <= currentSlot(c.config.GenesisTime)-prysmTypes.Slot(c.config.IgnoreSlotCount) { + c.log.Errorf("block[slot=%d,hash=%s] is too old to process", blk.Block().Slot(), blockHashHex) + return } - if err := c.blockProcessor.ProcessBlockchainBlock(c.log, c.endpoint, blk); err != nil { + if err := sendBlockToBDN(c.clock, c.log, blk, c.bridge, c.endpoint); err != nil { c.log.Errorf("could not proccess beacon block[slot=%d,hash=%s] to eth: %v", blk.Block().Slot(), blockHashHex, err) continue } - c.log.Debugf("eth2 block[slot=%d,hash=%s] sent to BDN", blk.Block().Slot(), blockHashHex) + c.log.Tracef("received beacon block[slot=%d,hash=%s]", blk.Block().Slot(), blockHashHex) } }() diff --git a/blockchain/beacon/rpc.go b/blockchain/beacon/rpc.go index bab8d3e..82f2893 100644 --- a/blockchain/beacon/rpc.go +++ b/blockchain/beacon/rpc.go @@ -3,17 +3,17 @@ package beacon import ( "bytes" "fmt" + "github.com/prysmaticlabs/go-bitfield" log "github.com/bloXroute-Labs/gateway/v2/logger" libp2pNetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/protocol" ssz "github.com/prysmaticlabs/fastssz" - "github.com/prysmaticlabs/go-bitfield" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p" - prysmTypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types" - types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v3/consensus-types/wrapper" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p" + prysmTypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types" + types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v4/consensus-types/wrapper" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) func (n *Node) subscribeRPC(topic string, handler libp2pNetwork.StreamHandler) { diff --git a/blockchain/beacon/utils.go b/blockchain/beacon/utils.go index 3f13a07..08f8216 100644 --- a/blockchain/beacon/utils.go +++ b/blockchain/beacon/utils.go @@ -5,42 +5,70 @@ import ( "fmt" "time" - log "github.com/bloXroute-Labs/gateway/v2/logger" - bxTypes "github.com/bloXroute-Labs/gateway/v2/types" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rlp" - "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing" - p2ptypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types" - "github.com/prysmaticlabs/prysm/v3/config/params" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" + "github.com/bloXroute-Labs/gateway/v2/blockchain" + "github.com/bloXroute-Labs/gateway/v2/blockchain/eth" + "github.com/bloXroute-Labs/gateway/v2/logger" + "github.com/bloXroute-Labs/gateway/v2/types" + "github.com/bloXroute-Labs/gateway/v2/utils" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing" + p2ptypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types" + "github.com/prysmaticlabs/prysm/v4/config/params" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + prysmTypes "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" + "github.com/prysmaticlabs/prysm/v4/time/slots" ) -// special error constant types -var ( - ErrInvalidRequest = errors.New("invalid request") - ErrBodyNotFound = errors.New("block body not stored") - ErrAlreadySeen = errors.New("already seen") - ErrAncientHeaders = errors.New("headers requested are ancient") - ErrFutureHeaders = errors.New("headers requested are in the future") - ErrQueryAmountIsNotValid = errors.New("query amount is not valid") -) +const confirmationDelay = 4 * time.Second -func logBlockConverterFailure(err error, bdnBlock *bxTypes.BxBlock) { - var blockHex string - if log.IsLevelEnabled(log.TraceLevel) { - b, err := rlp.EncodeToBytes(bdnBlock) - if err != nil { - blockHex = fmt.Sprintf("bad block from BDN could not be encoded to RLP bytes: %v", err) - } else { - blockHex = hexutil.Encode(b) - } +func sendBlockToBDN(clock utils.Clock, log *logger.Entry, block interfaces.ReadOnlySignedBeaconBlock, bridge blockchain.Bridge, endpoint types.NodeEndpoint) error { + bdnBeaconBlock, err := bridge.BlockBlockchainToBDN(block) + if err != nil { + return fmt.Errorf("could not convert beacon block: %v", err) + } + + if err := bridge.SendBlockToBDN(bdnBeaconBlock, endpoint); err != nil { + return fmt.Errorf("could not send block to gateway: %v", err) + } + + ethBlock, err := eth.BeaconBlockToEthBlock(block) + if err != nil { + return fmt.Errorf("could not convert block to eth block: %v", err) + } + + bdnEthBlock, err := bridge.BlockBlockchainToBDN(ethBlock) + if err != nil { + return fmt.Errorf("could not convert eth block: %v", err) + } + + if err := bridge.SendBlockToBDN(bdnEthBlock, endpoint); err != nil { + return fmt.Errorf("could not send block %v to gateway: %v", ethBlock.Hash(), err) } - log.Errorf("could not convert block (hash: %v) from BDN to beacon block: %v. contents: %v", bdnBlock.Hash(), err, blockHex) + + clock.AfterFunc(confirmationDelay, func() { + if err := bridge.SendConfirmedBlockToGateway(bdnBeaconBlock, endpoint); err != nil { + log.Errorf("could not send beacon block confirmation to gateway: %v", err) + } + + if err := bridge.SendConfirmedBlockToGateway(bdnEthBlock, endpoint); err != nil { + log.Errorf("could not send eth block confirmation %v to gateway: %v", ethBlock, err) + } + }) + + return nil } -func currentSlot(genesisTime uint64) types.Slot { - return types.Slot(uint64(time.Now().Unix()-int64(genesisTime)) / params.BeaconConfig().SecondsPerSlot) + +func currentSlot(genesisTime uint64) prysmTypes.Slot { + return prysmTypes.Slot(uint64(time.Now().Unix()-int64(genesisTime)) / params.BeaconConfig().SecondsPerSlot) +} + +func epochStartTime(genesisTime uint64, epoch prysmTypes.Epoch) (time.Time, error) { + slot, err := slots.EpochStart(epoch) + if err != nil { + return time.Time{}, err + } + + return slots.ToTime(genesisTime, slot) } func extractBlockDataType(digest []byte, vRoot []byte) (interfaces.ReadOnlySignedBeaconBlock, error) { diff --git a/blockchain/bridge.go b/blockchain/bridge.go index deae2de..396b876 100644 --- a/blockchain/bridge.go +++ b/blockchain/bridge.go @@ -101,6 +101,11 @@ type Bridge interface { SendBlockchainConnectionStatus(ConnectionStatus) error ReceiveBlockchainConnectionStatus() <-chan ConnectionStatus + SendNodeConnectionCheckRequest() error + ReceiveNodeConnectionCheckRequest() <-chan struct{} + SendNodeConnectionCheckResponse(types.NodeEndpoint) error + ReceiveNodeConnectionCheckResponse() <-chan types.NodeEndpoint + SendDisconnectEvent(endpoint types.NodeEndpoint) error ReceiveDisconnectEvent() <-chan types.NodeEndpoint } @@ -135,33 +140,37 @@ type BxBridge struct { noActiveBlockchainPeers chan NoActiveBlockchainPeersAlert - blockchainStatusRequest chan struct{} - blockchainStatusResponse chan []*types.NodeEndpoint - blockchainConnectionStatus chan ConnectionStatus - disconnectEvent chan types.NodeEndpoint - validatorInfo chan *ValidatorListInfo + blockchainStatusRequest chan struct{} + blockchainStatusResponse chan []*types.NodeEndpoint + nodeConnectionCheckRequest chan struct{} + nodeConnectionCheckResponse chan types.NodeEndpoint + blockchainConnectionStatus chan ConnectionStatus + disconnectEvent chan types.NodeEndpoint + validatorInfo chan *ValidatorListInfo } // NewBxBridge returns a BxBridge instance func NewBxBridge(converter Converter, beaconBlock bool) Bridge { return &BxBridge{ - config: make(chan network.EthConfig, 1), - transactionsFromNode: make(chan Transactions, transactionBacklog), - transactionsFromBDN: make(chan Transactions, transactionBacklog), - transactionHashesFromNode: make(chan TransactionAnnouncement, transactionHashesBacklog), - transactionHashesRequests: make(chan TransactionAnnouncement, transactionHashesBacklog), - beaconBlock: beaconBlock, - blocksFromNode: make(chan BlockFromNode, blockBacklog), - ethBlocksFromBDN: make(chan *types.BxBlock, blockBacklog), - beaconBlocksFromBDN: make(chan *types.BxBlock, blockBacklog), - confirmedBlockFromNode: make(chan BlockFromNode, blockBacklog), - noActiveBlockchainPeers: make(chan NoActiveBlockchainPeersAlert), - blockchainStatusRequest: make(chan struct{}, statusBacklog), - blockchainStatusResponse: make(chan []*types.NodeEndpoint, statusBacklog), - blockchainConnectionStatus: make(chan ConnectionStatus, transactionBacklog), - disconnectEvent: make(chan types.NodeEndpoint, statusBacklog), - Converter: converter, - validatorInfo: make(chan *ValidatorListInfo, 1), + config: make(chan network.EthConfig, 1), + transactionsFromNode: make(chan Transactions, transactionBacklog), + transactionsFromBDN: make(chan Transactions, transactionBacklog), + transactionHashesFromNode: make(chan TransactionAnnouncement, transactionHashesBacklog), + transactionHashesRequests: make(chan TransactionAnnouncement, transactionHashesBacklog), + beaconBlock: beaconBlock, + blocksFromNode: make(chan BlockFromNode, blockBacklog), + ethBlocksFromBDN: make(chan *types.BxBlock, blockBacklog), + beaconBlocksFromBDN: make(chan *types.BxBlock, blockBacklog), + confirmedBlockFromNode: make(chan BlockFromNode, blockBacklog), + noActiveBlockchainPeers: make(chan NoActiveBlockchainPeersAlert), + blockchainStatusRequest: make(chan struct{}, statusBacklog), + blockchainStatusResponse: make(chan []*types.NodeEndpoint, statusBacklog), + nodeConnectionCheckRequest: make(chan struct{}, statusBacklog), + nodeConnectionCheckResponse: make(chan types.NodeEndpoint, statusBacklog), + blockchainConnectionStatus: make(chan ConnectionStatus, transactionBacklog), + disconnectEvent: make(chan types.NodeEndpoint, statusBacklog), + Converter: converter, + validatorInfo: make(chan *ValidatorListInfo, 1), } } @@ -348,6 +357,36 @@ func (b BxBridge) ReceiveBlockchainStatusResponse() <-chan []*types.NodeEndpoint return b.blockchainStatusResponse } +// SendNodeConnectionCheckRequest sends a request for node connection check request +func (b BxBridge) SendNodeConnectionCheckRequest() error { + select { + case b.nodeConnectionCheckRequest <- struct{}{}: + return nil + default: + return ErrChannelFull + } +} + +// ReceiveNodeConnectionCheckRequest handles node connection check request from backend +func (b BxBridge) ReceiveNodeConnectionCheckRequest() <-chan struct{} { + return b.nodeConnectionCheckRequest +} + +// SendNodeConnectionCheckResponse sends a response for node connection check request +func (b BxBridge) SendNodeConnectionCheckResponse(endpoints types.NodeEndpoint) error { + select { + case b.nodeConnectionCheckResponse <- endpoints: + return nil + default: + return ErrChannelFull + } +} + +// ReceiveNodeConnectionCheckResponse handles node connection check response from backend +func (b BxBridge) ReceiveNodeConnectionCheckResponse() <-chan types.NodeEndpoint { + return b.nodeConnectionCheckResponse +} + // SendValidatorListInfo sends a validator info to gateway func (b *BxBridge) SendValidatorListInfo(info *ValidatorListInfo) error { select { diff --git a/blockchain/eth/backend.go b/blockchain/eth/backend.go index 7f1a73d..eab0c4b 100644 --- a/blockchain/eth/backend.go +++ b/blockchain/eth/backend.go @@ -232,6 +232,8 @@ func (h *Handler) handleBDNBridge(ctx context.Context) { h.config.Update(config) case <-h.bridge.ReceiveBlockchainStatusRequest(): h.processBlockchainStatusRequest() + case <-h.bridge.ReceiveNodeConnectionCheckRequest(): + h.processNodeConnectionCheckRequest() case endpoint := <-h.bridge.ReceiveDisconnectEvent(): h.processDisconnectEvent(endpoint) case <-ctx.Done(): @@ -330,6 +332,19 @@ func (h *Handler) processBlockchainStatusRequest() { } } +func (h *Handler) processNodeConnectionCheckRequest() { + var endpoint types.NodeEndpoint + for _, peer := range h.peers.getAll() { + endpoint = peer.IPEndpoint() + break + } + + err := h.bridge.SendNodeConnectionCheckResponse(endpoint) + if err != nil { + log.Errorf("send blockchain status response: %v", err) + } +} + func (h *Handler) processDisconnectEvent(endpoint types.NodeEndpoint) { // check if the peer is in the connections for _, peer := range h.peers.getAll() { diff --git a/blockchain/eth/chain.go b/blockchain/eth/chain.go index e8af868..67abc78 100644 --- a/blockchain/eth/chain.go +++ b/blockchain/eth/chain.go @@ -6,16 +6,15 @@ import ( "errors" "fmt" "math/big" - "strconv" "sync" "time" log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/bloXroute-Labs/gateway/v2/utils" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/protocols/eth" - cmap "github.com/orcaman/concurrent-map" ) const ( @@ -39,10 +38,10 @@ type Chain struct { // if a missing block too old discard to add it ignoreBlockTimeout time.Duration - heightToBlockHeaders cmap.ConcurrentMap - blockHashMetadata cmap.ConcurrentMap - blockHashToBody cmap.ConcurrentMap - blockHashToDifficulty cmap.ConcurrentMap + heightToBlockHeaders *syncmap.SyncMap[uint64, []ethHeader] + blockHashMetadata *syncmap.SyncMap[ethcommon.Hash, blockMetadata] + blockHashToBody *syncmap.SyncMap[ethcommon.Hash, *ethtypes.Body] + blockHashToDifficulty *syncmap.SyncMap[ethcommon.Hash, *big.Int] chainState blockRefChain @@ -111,10 +110,10 @@ func newChain(ctx context.Context, ignoreBlockTimeout time.Duration, maxReorg, m c := &Chain{ chainLock: sync.RWMutex{}, headerLock: sync.RWMutex{}, - heightToBlockHeaders: cmap.New(), - blockHashMetadata: cmap.New(), - blockHashToBody: cmap.New(), - blockHashToDifficulty: cmap.New(), + heightToBlockHeaders: syncmap.NewIntegerMapOf[uint64, []ethHeader](), + blockHashMetadata: syncmap.NewTypedMapOf[ethcommon.Hash, blockMetadata](syncmap.EthCommonHasher), + blockHashToBody: syncmap.NewTypedMapOf[ethcommon.Hash, *ethtypes.Body](syncmap.EthCommonHasher), + blockHashToDifficulty: syncmap.NewTypedMapOf[ethcommon.Hash, *big.Int](syncmap.EthCommonHasher), chainState: make([]blockRef, 0), maxReorg: maxReorg, minValidChain: minValidChain, @@ -173,7 +172,7 @@ func (c *Chain) ConfirmBlock(hash ethcommon.Hash) int { } bm.confirmed = true - c.blockHashMetadata.Set(hash.String(), bm) + c.blockHashMetadata.Store(hash, bm) header, ok := c.getBlockHeader(bm.height, hash) if !ok { @@ -297,7 +296,7 @@ func (c *Chain) MarkSentToBDN(hash ethcommon.Hash) { } bm.sentToBDN = true - c.blockHashMetadata.Set(hash.String(), bm) + c.blockHashMetadata.Store(hash, bm) } // MarkConfirmationSentToBDN marks a block confirmation as having been sent to the BDN, so it does not need to be sent again in the future @@ -311,7 +310,7 @@ func (c *Chain) MarkConfirmationSentToBDN(hash ethcommon.Hash) { } bm.cnfMsgSent = true - c.blockHashMetadata.Set(hash.String(), bm) + c.blockHashMetadata.Store(hash, bm) } // InitializeDifficulty stores an initial difficulty if needed to start calculating total difficulties. Only 1 difficulty is stored, since these difficulties are never GC'ed. @@ -607,39 +606,38 @@ func (c *Chain) storeBlockHeader(header *ethtypes.Header, source BlockSource) { } func (c *Chain) getBlockMetadata(hash ethcommon.Hash) (blockMetadata, bool) { - bm, ok := c.blockHashMetadata.Get(hash.String()) + bm, ok := c.blockHashMetadata.Load(hash) if !ok { return blockMetadata{}, ok } - return bm.(blockMetadata), ok + return bm, ok } func (c *Chain) storeBlockMetadata(hash ethcommon.Hash, height uint64, confirmed bool, cnfMsgSent bool) { - set := c.blockHashMetadata.SetIfAbsent(hash.String(), blockMetadata{height, false, confirmed, cnfMsgSent}) - if !set { + _, exists := c.blockHashMetadata.LoadOrStore(hash, blockMetadata{height, false, confirmed, cnfMsgSent}) + if exists { bm, _ := c.getBlockMetadata(hash) bm.confirmed = bm.confirmed || confirmed bm.cnfMsgSent = bm.cnfMsgSent || cnfMsgSent - c.blockHashMetadata.Set(hash.String(), bm) + c.blockHashMetadata.Store(hash, bm) } } func (c *Chain) removeBlockMetadata(hash ethcommon.Hash) { - c.blockHashMetadata.Remove(hash.String()) + c.blockHashMetadata.Delete(hash) } func (c *Chain) hasHeader(hash ethcommon.Hash) bool { // always corresponds to a header stored at c.heightToBlockHeaders - return c.blockHashMetadata.Has(hash.String()) + return c.blockHashMetadata.Has(hash) } func (c *Chain) getHeadersAtHeight(height uint64) ([]*ethtypes.Header, bool) { - rawHeaders, ok := c.heightToBlockHeaders.Get(strconv.FormatUint(height, 10)) + ethHeaders, ok := c.heightToBlockHeaders.Load(height) if !ok { return nil, ok } - ethHeaders := rawHeaders.([]ethHeader) headers := make([]*ethtypes.Header, 0, len(ethHeaders)) for _, eh := range ethHeaders { @@ -662,53 +660,50 @@ func (c *Chain) storeEthHeaderAtHeight(height uint64, eh ethHeader) { c.headerLock.RLock() defer c.headerLock.RUnlock() - heightStr := strconv.FormatUint(height, 10) - - ok := c.heightToBlockHeaders.SetIfAbsent(heightStr, []ethHeader{eh}) - if !ok { - rawHeaders, _ := c.heightToBlockHeaders.Get(heightStr) - ethHeaders := rawHeaders.([]ethHeader) + _, exists := c.heightToBlockHeaders.LoadOrStore(height, []ethHeader{eh}) + if exists { + ethHeaders, _ := c.heightToBlockHeaders.Load(height) ethHeaders = append(ethHeaders, eh) - c.heightToBlockHeaders.Set(heightStr, ethHeaders) + c.heightToBlockHeaders.Store(height, ethHeaders) } } func (c *Chain) storeBlockDifficulty(hash ethcommon.Hash, difficulty *big.Int) { if difficulty != nil { - c.blockHashToDifficulty.Set(hash.String(), difficulty) + c.blockHashToDifficulty.Store(hash, difficulty) } } func (c *Chain) getBlockDifficulty(hash ethcommon.Hash) (*big.Int, bool) { - difficulty, ok := c.blockHashToDifficulty.Get(hash.String()) + difficulty, ok := c.blockHashToDifficulty.Load(hash) if !ok { return nil, ok } - return difficulty.(*big.Int), ok + return difficulty, ok } func (c *Chain) removeBlockDifficulty(hash ethcommon.Hash) { - c.blockHashToDifficulty.Remove(hash.String()) + c.blockHashToDifficulty.Delete(hash) } func (c *Chain) storeBlockBody(hash ethcommon.Hash, body *ethtypes.Body) { - c.blockHashToBody.Set(hash.String(), body) + c.blockHashToBody.Store(hash, body) } func (c *Chain) getBlockBody(hash ethcommon.Hash) (*ethtypes.Body, bool) { - body, ok := c.blockHashToBody.Get(hash.String()) + body, ok := c.blockHashToBody.Load(hash) if !ok { return nil, ok } - return body.(*ethtypes.Body), ok + return body, ok } func (c *Chain) hasBody(hash ethcommon.Hash) bool { - return c.blockHashToBody.Has(hash.String()) + return c.blockHashToBody.Has(hash) } func (c *Chain) removeBlockBody(hash ethcommon.Hash) { - c.blockHashToBody.Remove(hash.String()) + c.blockHashToBody.Delete(hash) } // removes all info corresponding to a given block in storage @@ -728,21 +723,16 @@ func (c *Chain) clean(maxSize int) (lowestCleaned int, highestCleaned int, numCl // Find largest height from headers if chainState is empty // This may happened if no connection to node was established but we receiving blocks from BDN if len(c.chainState) == 0 { - var maxHeight int - for elem := range c.heightToBlockHeaders.IterBuffered() { - heightStr := elem.Key - height, err := strconv.Atoi(heightStr) - if err != nil { - log.Errorf("failed to convert height %v from string to integer: %v", heightStr, err) - continue - } + var maxHeight uint64 + c.heightToBlockHeaders.Range(func(height uint64, value []ethHeader) bool { if height > maxHeight { maxHeight = height } - } + return true + }) - lowestCleaned = maxHeight + lowestCleaned = int(maxHeight) } else { lowestCleaned = int(c.chainState[0].height) } @@ -752,33 +742,32 @@ func (c *Chain) clean(maxSize int) (lowestCleaned int, highestCleaned int, numCl // minimum height to not be cleaned minHeight := lowestCleaned - maxSize + 1 - numHeadersStored := c.heightToBlockHeaders.Count() + numHeadersStored := c.heightToBlockHeaders.Size() if numHeadersStored >= maxSize { - for elem := range c.heightToBlockHeaders.IterBuffered() { - heightStr := elem.Key - height, err := strconv.Atoi(heightStr) - if err != nil { - log.Errorf("failed to convert height %v from string to integer: %v", heightStr, err) - continue - } - if height < minHeight { - headers := elem.Val.([]ethHeader) - c.heightToBlockHeaders.Remove(heightStr) + + c.heightToBlockHeaders.Range(func(height uint64, headers []ethHeader) bool { + + intHeight := int(height) + + if intHeight < minHeight { + c.heightToBlockHeaders.Delete(height) for _, header := range headers { hash := header.hash c.pruneHash(hash) numCleaned++ - if height < lowestCleaned { - lowestCleaned = height + if intHeight < lowestCleaned { + lowestCleaned = intHeight } - if height > highestCleaned { - highestCleaned = height + if intHeight > highestCleaned { + highestCleaned = intHeight } } } - } + + return true + }) chainStatePruned := 0 if len(c.chainState) > maxSize { diff --git a/blockchain/eth/chain_test.go b/blockchain/eth/chain_test.go index 849dbc5..8b21d94 100644 --- a/blockchain/eth/chain_test.go +++ b/blockchain/eth/chain_test.go @@ -327,15 +327,15 @@ func TestChain_InitializeStatus(t *testing.T) { // initialize difficulty creates entries, but doesn't modify any other state c.InitializeDifficulty(initialHash1, initialDifficulty) - assert.Equal(t, 1, c.heightToBlockHeaders.Count()) + assert.Equal(t, 1, c.heightToBlockHeaders.Size()) assert.Equal(t, 0, len(c.chainState)) addBlock(c, bxmock.NewEthBlock(100, common.Hash{})) - assert.Equal(t, 2, c.heightToBlockHeaders.Count()) + assert.Equal(t, 2, c.heightToBlockHeaders.Size()) assert.Equal(t, 1, len(c.chainState)) c.InitializeDifficulty(initialHash2, initialDifficulty) - assert.Equal(t, 2, c.heightToBlockHeaders.Count()) + assert.Equal(t, 2, c.heightToBlockHeaders.Size()) assert.Equal(t, 1, len(c.chainState)) _, ok = c.getBlockDifficulty(initialHash1) @@ -345,7 +345,7 @@ func TestChain_InitializeStatus(t *testing.T) { // after any cleanup call, initialized entries status will be ejected c.clean(1) - assert.Equal(t, 1, c.heightToBlockHeaders.Count()) + assert.Equal(t, 1, c.heightToBlockHeaders.Size()) assert.Equal(t, 1, len(c.chainState)) _, ok = c.getBlockDifficulty(initialHash1) @@ -411,27 +411,28 @@ func TestChain_clean(t *testing.T) { block6.Hash(): {}, } - assert.Equal(t, 6, c.heightToBlockHeaders.Count()) + assert.Equal(t, 6, c.heightToBlockHeaders.Size()) // Clean can block last block adding // So better to use (blockCount + 1) * cleanInterval time.Sleep(7 * cleanInterval) - assert.Equal(t, 4, c.heightToBlockHeaders.Count()) + assert.Equal(t, 4, c.heightToBlockHeaders.Size()) - for elem := range c.heightToBlockHeaders.IterBuffered() { - headers := elem.Val.([]ethHeader) + c.heightToBlockHeaders.Range(func(key uint64, headers []ethHeader) bool { for _, header := range headers { if _, ok := expectedHashes[header.Hash()]; !ok { assert.Fail(t, "unexpected block", "height: %v", header.Number.Int64()) } delete(expectedHashes, header.Hash()) } - c.heightToBlockHeaders.Remove(elem.Key) - } + c.heightToBlockHeaders.Delete(key) + + return true + }) assert.Empty(t, expectedHashes) - assert.Zero(t, c.heightToBlockHeaders.Count()) + assert.Zero(t, c.heightToBlockHeaders.Size()) } func TestChain_cleanNoChainstate(t *testing.T) { @@ -455,27 +456,28 @@ func TestChain_cleanNoChainstate(t *testing.T) { block4.Hash(): {}, } - assert.Equal(t, 4, c.heightToBlockHeaders.Count()) + assert.Equal(t, 4, c.heightToBlockHeaders.Size()) // Clean can block last block adding // So better to use (blockCount + 1) * cleanInterval time.Sleep(5 * cleanInterval) - assert.Equal(t, 3, c.heightToBlockHeaders.Count()) + assert.Equal(t, 3, c.heightToBlockHeaders.Size()) - for elem := range c.heightToBlockHeaders.IterBuffered() { - headers := elem.Val.([]ethHeader) + c.heightToBlockHeaders.Range(func(key uint64, headers []ethHeader) bool { for _, header := range headers { if _, ok := expectedHashes[header.Hash()]; !ok { assert.Fail(t, "unexpected block", "height: %v", header.Number.Int64()) } delete(expectedHashes, header.Hash()) } - c.heightToBlockHeaders.Remove(elem.Key) - } + c.heightToBlockHeaders.Delete(key) + + return true + }) assert.Empty(t, expectedHashes) - assert.Zero(t, c.heightToBlockHeaders.Count()) + assert.Zero(t, c.heightToBlockHeaders.Size()) } func addBDNBlock(c *Chain, block *ethtypes.Block) int { diff --git a/blockchain/eth/converter.go b/blockchain/eth/converter.go index ad88d9b..3d3fce3 100644 --- a/blockchain/eth/converter.go +++ b/blockchain/eth/converter.go @@ -11,11 +11,11 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ssz "github.com/prysmaticlabs/fastssz" - "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v3/runtime/version" + "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/runtime/version" ) type bxBlockRLP struct { diff --git a/blockchain/eth/converter_test.go b/blockchain/eth/converter_test.go index 4c751f0..8354cca 100644 --- a/blockchain/eth/converter_test.go +++ b/blockchain/eth/converter_test.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" "github.com/stretchr/testify/assert" ) diff --git a/blockchain/eth/peer.go b/blockchain/eth/peer.go index 413946b..1937126 100644 --- a/blockchain/eth/peer.go +++ b/blockchain/eth/peer.go @@ -7,7 +7,6 @@ import ( "fmt" "math/big" "math/rand" - "strconv" "time" "github.com/bloXroute-Labs/gateway/v2" @@ -15,11 +14,11 @@ import ( log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/p2p" - cmap "github.com/orcaman/concurrent-map" ) const ( @@ -58,7 +57,7 @@ type Peer struct { checkpointPassed bool responseQueue chan chan eth.Packet // chan is used as a concurrency safe queue - responseQueue66 cmap.ConcurrentMap + responseQueue66 *syncmap.SyncMap[uint64, chan eth.Packet] newHeadCh chan blockRef newBlockCh chan *eth.NewBlockPacket @@ -90,7 +89,7 @@ func newPeer(parent context.Context, p *p2p.Peer, rw p2p.MsgReadWriter, version blockConfirmationCh: make(chan common.Hash, blockConfirmationChannelBacklog), queuedBlocks: make([]*eth.NewBlockPacket, 0), responseQueue: make(chan chan eth.Packet, responseQueueSize), - responseQueue66: cmap.New(), + responseQueue66: syncmap.NewIntegerMapOf[uint64, chan eth.Packet](), RequestConfirmations: true, } peer.endpoint = types.NodeEndpoint{IP: p.Node().IP().String(), Port: p.Node().TCP(), PublicKey: p.Info().Enode, Dynamic: !p.Info().Network.Static, ID: p.ID().String()} @@ -345,12 +344,29 @@ func (ep *Peer) Handshake(version uint32, networkChain uint64, td *big.Int, head func (ep *Peer) readStatus() (*eth.StatusPacket, error) { var status eth.StatusPacket - - msg, err := ep.rw.ReadMsg() - if err != nil { - return nil, err + type messageChanResponse struct { + msg p2p.Msg + err error } + messageChan := make(chan messageChanResponse, 1) + + go func() { + msg, err := ep.rw.ReadMsg() + messageChan <- messageChanResponse{msg, err} + }() + timeout := time.NewTicker(time.Second * 6) + var msg p2p.Msg + select { + case message := <-messageChan: + if message.err != nil { + return nil, message.err + } + msg = message.msg + case <-timeout.C: + timeout.Stop() + return nil, errors.New("failed to get message from peer, deadline exceeded") + } defer func() { _ = msg.Discard() }() @@ -363,7 +379,7 @@ func (ep *Peer) readStatus() (*eth.StatusPacket, error) { return &status, fmt.Errorf("message is too big: %v > %v", msg.Size, maxMessageSize) } - if err = msg.Decode(&status); err != nil { + if err := msg.Decode(&status); err != nil { return &status, fmt.Errorf("could not decode status message: %v", err) } @@ -381,12 +397,11 @@ func (ep *Peer) NotifyResponse(packet eth.Packet) bool { // NotifyResponse66 informs any listeners dependent on a request/response call to this ETH66 peer, indicating if any channels were waiting for the message func (ep *Peer) NotifyResponse66(requestID uint64, packet eth.Packet) (bool, error) { - rawResponseCh, ok := ep.responseQueue66.Pop(convertRequestIDKey(requestID)) + responseCh, ok := ep.responseQueue66.LoadAndDelete(requestID) if !ok { return false, ErrUnknownRequestID } - responseCh := rawResponseCh.(chan eth.Packet) if responseCh != nil { responseCh <- packet } @@ -620,7 +635,7 @@ func (ep *Peer) registerForResponse(responseCh chan eth.Packet) { } func (ep *Peer) registerForResponse66(requestID uint64, responseCh chan eth.Packet) { - ep.responseQueue66.Set(convertRequestIDKey(requestID), responseCh) + ep.responseQueue66.Store(requestID, responseCh) } func (ep *Peer) send(msgCode uint64, data interface{}) error { @@ -629,7 +644,3 @@ func (ep *Peer) send(msgCode uint64, data interface{}) error { } return p2p.Send(ep.rw, msgCode, data) } - -func convertRequestIDKey(requestID uint64) string { - return strconv.FormatUint(requestID, 10) -} diff --git a/blockchain/eth/peer_test.go b/blockchain/eth/peer_test.go index 3cb63da..4feb4de 100644 --- a/blockchain/eth/peer_test.go +++ b/blockchain/eth/peer_test.go @@ -100,6 +100,15 @@ func TestPeer_Handshake(t *testing.T) { }) _, err = peer.Handshake(1, uint64(networkNum), new(big.Int), common.Hash{1, 2, 3}, common.Hash{2, 3, 4}, executionLayerForks) assert.NotNil(t, err) + + go rw.QueueIncomingMessageWithDelay(eth.StatusMsg, peerStatus, time.Second) + ps, err = peer.Handshake(1, uint64(networkNum), new(big.Int), common.Hash{1, 2, 3}, common.Hash{2, 3, 4}, executionLayerForks) + assert.NoError(t, err) + assert.Equal(t, peerStatus, *ps) + + go rw.QueueIncomingMessageWithDelay(eth.StatusMsg, peerStatus, time.Second*7) + _, err = peer.Handshake(1, uint64(networkNum), new(big.Int), common.Hash{1, 2, 3}, common.Hash{2, 3, 4}, executionLayerForks) + assert.Error(t, err) } func TestPeer_SendNewBlock(t *testing.T) { diff --git a/blockchain/eth/protocol.go b/blockchain/eth/protocol.go index 55ad182..30d4fe1 100644 --- a/blockchain/eth/protocol.go +++ b/blockchain/eth/protocol.go @@ -23,6 +23,7 @@ var supportedProtocols = map[uint64][]uint{ network.PolygonMainnetChainID: {ETH65, eth.ETH66}, network.EthMainnetChainID: {eth.ETH66, eth.ETH67, eth.ETH68}, network.GoerliChainID: {eth.ETH66, eth.ETH67, eth.ETH68}, + network.ZhejiangChainID: {eth.ETH66, eth.ETH67, eth.ETH68}, } // protocolLengths is a mapping of each supported devp2p protocol to its message version length diff --git a/blockchain/eth/test/message.go b/blockchain/eth/test/message.go index e113bd4..6fcd5f3 100644 --- a/blockchain/eth/test/message.go +++ b/blockchain/eth/test/message.go @@ -59,6 +59,22 @@ func (t *MsgReadWriter) QueueIncomingMessage(code uint64, payload interface{}) { t.ReadMessages <- msg } +// QueueIncomingMessageWithDelay simulates the peer sending a message to be read with delay +func (t *MsgReadWriter) QueueIncomingMessageWithDelay(code uint64, payload any, delay time.Duration) { + time.Sleep(delay) + size, reader, err := rlp.EncodeToReader(payload) + if err != nil { + panic(err) + } + msg := p2p.Msg{ + Code: code, + Size: uint32(size), + Payload: reader, + ReceivedAt: time.Time{}, + } + t.ReadMessages <- msg +} + // ExpectWrite waits for up to the provided duration for writes to the channel. This method is useful if the message writing happens on a goroutine. func (t *MsgReadWriter) ExpectWrite(d time.Duration) bool { if !t.writeToChannel { diff --git a/blockchain/eth/wsprovider.go b/blockchain/eth/wsprovider.go index 071cbe6..770ab12 100644 --- a/blockchain/eth/wsprovider.go +++ b/blockchain/eth/wsprovider.go @@ -163,12 +163,12 @@ func (ws *WSProvider) FetchTransactionReceipt(payload []interface{}, options blo return ws.CallRPC("eth_getTransactionReceipt", payload, options) } -//FetchTransaction check status of a transaction via CallRPC +// FetchTransaction check status of a transaction via CallRPC func (ws *WSProvider) FetchTransaction(payload []interface{}, options blockchain.RPCOptions) (interface{}, error) { return ws.CallRPC("eth_getTransactionByHash", payload, options) } -//FetchBlock query a block given height via CallRPC +// FetchBlock query a block given height via CallRPC func (ws *WSProvider) FetchBlock(payload []interface{}, options blockchain.RPCOptions) (interface{}, error) { return ws.CallRPC("eth_getBlockByNumber", payload, options) } diff --git a/blockchain/eth/wsprovider_mock.go b/blockchain/eth/wsprovider_mock.go index e9d6dea..7f7e0bc 100644 --- a/blockchain/eth/wsprovider_mock.go +++ b/blockchain/eth/wsprovider_mock.go @@ -92,7 +92,7 @@ func (m *MockWSProvider) FetchTransaction(payload []interface{}, options blockch return nil, nil } -//FetchBlock query a block given height via CallRPC +// FetchBlock query a block given height via CallRPC func (m *MockWSProvider) FetchBlock(_ []interface{}, _ blockchain.RPCOptions) (interface{}, error) { return nil, nil } diff --git a/blockchain/network/config.go b/blockchain/network/config.go index cec1e8e..8a3cdd8 100644 --- a/blockchain/network/config.go +++ b/blockchain/network/config.go @@ -4,6 +4,8 @@ import ( "crypto/ecdsa" "errors" "fmt" + "github.com/multiformats/go-multiaddr" + "github.com/urfave/cli/v2" "math/big" "net" "os" @@ -16,9 +18,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" libp2pPeer "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" - ecdsaprysm "github.com/prysmaticlabs/prysm/v3/crypto/ecdsa" - "github.com/urfave/cli/v2" + ecdsaprysm "github.com/prysmaticlabs/prysm/v4/crypto/ecdsa" ) // PeerInfo contains the enode and websockets endpoint for an Ethereum peer diff --git a/blockchain/network/preset.go b/blockchain/network/preset.go index 512efbd..68d345f 100644 --- a/blockchain/network/preset.go +++ b/blockchain/network/preset.go @@ -21,10 +21,10 @@ const ZhejiangChainID = 1337803 // GoerliChainID ethereum Goerli chain ID const GoerliChainID = 5 -//BSCMainnetChainID BSC mainnet chain ID +// BSCMainnetChainID BSC mainnet chain ID const BSCMainnetChainID = 56 -//PolygonMainnetChainID Polygon mainnet chain ID +// PolygonMainnetChainID Polygon mainnet chain ID const PolygonMainnetChainID = 137 var networkMapping = map[string]EthConfig{ @@ -71,7 +71,7 @@ func newGoerliConfig() EthConfig { } func newZhejiangEthereumConfig() EthConfig { - td, ok := new(big.Int).SetString("0400000000", 16) + td, ok := new(big.Int).SetString("0400000000", 16) // todo: ? if !ok { panic("could not load Ethereum Mainnet configuration") } @@ -79,26 +79,22 @@ func newZhejiangEthereumConfig() EthConfig { var err error var bootNodes []*enode.Node - bootNodes, err = bootstrapNodes(enode.ValidSchemes, []string{ - "enode://691c66d0ce351633b2ef8b4e4ef7db9966915ca0937415bd2b408df22923f274873b4d4438929e029a13a680140223dcf701cabe22df7d8870044321022dfefa@64.225.78.1:30303", - "enode://89347b9461727ee1849256d78e84d5c86cc3b4c6c5347650093982b726d71f3d08027e280b399b7b6604ceeda863283dcfe1a01e93728b4883114e9f8c7cc8ef@146.190.238.212:30303", - "enode://c2892072efe247f21ed7ebea6637ade38512a0ae7c5cffa1bf0786d5e3be1e7f40ff71252a21b36aa9de54e49edbcfc6962a98032adadfa29c8524262e484ad3@165.232.84.160:30303", - "enode://71e862580d3177a99e9837bd9e9c13c83bde63d3dba1d5cea18e89eb2a17786bbd47a8e7ae690e4d29763b55c205af13965efcaf6105d58e118a5a8ed2b0f6d0@68.183.13.170:30303", - "enode://2f6cf7f774e4507e7c1b70815f9c0ccd6515ee1170c991ce3137002c6ba9c671af38920f5b8ab8a215b62b3b50388030548f1d826cb6c2b30c0f59472804a045@161.35.147.98:30303", + bootNodes, err = bootstrapNodes(enode.ValidSchemes, []string{ // todo: ? + "enode://f20741af2743d83e03d30c49801b19b0103db81667cd8482a428cb1807a790f78a6ed2045ac45694e84803c94b6e8e63a4fc151f7c676cace3d38fcd94e52c57@167.99.209.68:30303", }) if err != nil { panic("could not set Ethereum Mainnet bootstrapNodes") } - ttd, _ := big.NewInt(0).SetString("0", 0) + ttd, _ := big.NewInt(0).SetString("0", 0) // not changed return EthConfig{ Network: ZhejiangChainID, TotalDifficulty: td, TerminalTotalDifficulty: ttd, - GenesisTime: 1675263600, - Head: common.HexToHash("0x40891180a021e1f86644f42721a9badb5bc8fc04036ca847c0d6fe1c440d4261"), - Genesis: common.HexToHash("0x40891180a021e1f86644f42721a9badb5bc8fc04036ca847c0d6fe1c440d4261"), + GenesisTime: 1680523260, // cfg.MinGenesisTime = 1680523200 + cfg.GenesisDelay = 60 + Head: common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"), + Genesis: common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"), IgnoreBlockTimeout: 150 * time.Second, IgnoreSlotCount: 10, BootstrapNodes: bootNodes, @@ -159,7 +155,7 @@ func newBSCMainnetConfig() EthConfig { "enode://28b1d16562dac280dacaaf45d54516b85bc6c994252a9825c5cc4e080d3e53446d05f63ba495ea7d44d6c316b54cd92b245c5c328c37da24605c4a93a0d099c4@34.246.65.14:30311", "enode://5a7b996048d1b0a07683a949662c87c09b55247ce774aeee10bb886892e586e3c604564393292e38ef43c023ee9981e1f8b335766ec4f0f256e57f8640b079d5@35.73.137.11:30311", }) - //bootNodes, err = bootstrapNodes(enode.ValidSchemes, []string{ + // bootNodes, err = bootstrapNodes(enode.ValidSchemes, []string{ // "enode://45146a7cb02cd127d21cf3c37f533623c5caf4dea31aecc619d5e47ccc38f8377f08be8ce60f9c3429efaa50825c435498880a46b4634ccdc3ed8eb72ba054ae@3.209.235.131:30311", // "enode://eaa9a4d781fc7f1666bb15b8cb5fd6128ce9b92ab3824294e1fde8074a646d1d94e9f8558f6111a714ff28aed0f06dfb3958212b21ac943904747719c758b4d1@100.25.250.190:30311", // "enode://5a7b996048d1b0a07683a949662c87c09b55247ce774aeee10bb886892e586e3c604564393292e38ef43c023ee9981e1f8b335766ec4f0f256e57f8640b079d5@13.114.81.214:30311", @@ -174,7 +170,7 @@ func newBSCMainnetConfig() EthConfig { // "enode://3a1efd25c06f925e05eed5418534e033ee285abbdc898e64c6747e327abbf9369db846ad0532ce94149e9e3d35c1f9a6d700e99c9cfbc02de099988a4ab1049e@3.215.208.84:30311", // "enode://2149f76dabe7c1711e3180b0b8358c55ec3b37b0b0e3e00b4dc1fe994c74d4ee7012ffc9eec768f601e0809e99b8d1650dbf0d91ffb62f6b2e809fbd3411ae2f@46.137.9.186:30311", // "enode://1841077024720c251f58e6eeb10c2a3846db3610b2f4e8210e7035d0623f4ab6caef94c3bf215cb548e7c7e41d2755da33b63685de425e07aeb5cef017ea8cb5@52.51.36.24:30311", - //}) + // }) if err != nil { panic("could not set Ethereum Mainnet bootstrapNodes") } diff --git a/blockchain/noopbridge.go b/blockchain/noopbridge.go index b92a078..4008193 100644 --- a/blockchain/noopbridge.go +++ b/blockchain/noopbridge.go @@ -126,6 +126,22 @@ func (n NoOpBxBridge) ReceiveBlockchainStatusResponse() <-chan []*types.NodeEndp return make(chan []*types.NodeEndpoint) } +// SendNodeConnectionCheckRequest is a no-op +func (n NoOpBxBridge) SendNodeConnectionCheckRequest() error { return nil } + +// ReceiveNodeConnectionCheckRequest is a no-op +func (n NoOpBxBridge) ReceiveNodeConnectionCheckRequest() <-chan struct{} { + return make(chan struct{}) +} + +// SendNodeConnectionCheckResponse is a no-op +func (n NoOpBxBridge) SendNodeConnectionCheckResponse(types.NodeEndpoint) error { return nil } + +// ReceiveNodeConnectionCheckResponse is a no-op +func (n NoOpBxBridge) ReceiveNodeConnectionCheckResponse() <-chan types.NodeEndpoint { + return make(chan types.NodeEndpoint) +} + // SendValidatorListInfo is a no-op func (n *NoOpBxBridge) SendValidatorListInfo(info *ValidatorListInfo) error { return nil diff --git a/blockchain/polygon/bor/bor.go b/blockchain/polygon/bor/bor.go index ebf6630..29018ee 100644 --- a/blockchain/polygon/bor/bor.go +++ b/blockchain/polygon/bor/bor.go @@ -18,6 +18,14 @@ import ( const extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal +type runState uint8 + +const ( + stateIdle = runState(iota) + stateBooting + stateRunning +) + // errMissingSignature is returned if a block's extra-data section doesn't seem // to contain a 65 byte secp256k1 signature. var errMissingSignature = errors.New("extra-data 65 byte signature suffix missing") diff --git a/blockchain/polygon/bor/span.go b/blockchain/polygon/bor/span.go index 98c5135..f2d0153 100644 --- a/blockchain/polygon/bor/span.go +++ b/blockchain/polygon/bor/span.go @@ -19,6 +19,7 @@ import ( log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/bloXroute-Labs/gateway/v2/blockchain/polygon/bor/valset" + "github.com/bloXroute-Labs/gateway/v2/utils/ptr" ) const ( @@ -75,8 +76,7 @@ type HeimdallSpanner struct { mx *sync.RWMutex - booting *atomic.Bool - started *atomic.Bool + state *atomic.Pointer[runState] httpClient *http.Client @@ -95,8 +95,7 @@ func NewHeimdallSpanner(ctx context.Context, endpoint string) *HeimdallSpanner { mx: new(sync.RWMutex), - booting: new(atomic.Bool), - started: new(atomic.Bool), + state: atomic.NewPointer(ptr.New(stateIdle)), httpClient: new(http.Client), @@ -136,18 +135,19 @@ func (h *HeimdallSpanner) bootstrap() error { // Run bootstrap initial state and start goroutine for processing of changes. func (h *HeimdallSpanner) Run() error { - if h.started.Load() || h.booting.Load() { + if *h.state.Load() != stateIdle { return nil } - h.booting.Store(true) - defer h.booting.Store(false) + h.state.Store(ptr.New(stateBooting)) if err := h.bootstrap(); err != nil { + h.state.Store(ptr.New(stateIdle)) + return errors.WithMessage(err, "failed to bootstrap spanner") } - h.started.Store(true) + h.state.Store(ptr.New(stateRunning)) go func() { backOff := backoff.WithContext(Retry(), h.ctx) @@ -155,7 +155,7 @@ func (h *HeimdallSpanner) Run() error { for { select { case <-h.ctx.Done(): - h.started.Store(false) + h.state.Store(ptr.New(stateIdle)) return case spanInfo := <-h.spanUpdateCh: diff --git a/blockchain/polygon/bor/sprint.go b/blockchain/polygon/bor/sprint.go index 2179d84..bd49e0e 100644 --- a/blockchain/polygon/bor/sprint.go +++ b/blockchain/polygon/bor/sprint.go @@ -15,6 +15,7 @@ import ( "github.com/bloXroute-Labs/gateway/v2/blockchain" "github.com/bloXroute-Labs/gateway/v2/types" + "github.com/bloXroute-Labs/gateway/v2/utils/ptr" ) // SprintSize represent size of sprint for polygon bor module @@ -31,8 +32,7 @@ type SprintManager struct { mx *sync.RWMutex - booting *atomic.Bool - started *atomic.Bool + state *atomic.Pointer[runState] // pointer to the interface to make it managed externally wsManager *blockchain.WSManager @@ -53,8 +53,7 @@ func NewSprintManager(ctx context.Context, wsManager *blockchain.WSManager, span mx: new(sync.RWMutex), - booting: new(atomic.Bool), - started: new(atomic.Bool), + state: atomic.NewPointer(ptr.New(stateIdle)), sprintMap: make(map[uint64]string), @@ -154,14 +153,15 @@ func (m *SprintManager) getLatestSnapshot() (*Snapshot, error) { // Run bootstrap initial state and start goroutine for processing of changes. func (m *SprintManager) Run() error { - if m.started.Load() || m.booting.Load() { + if *m.state.Load() != stateIdle { return nil } - m.booting.Store(true) - defer m.booting.Store(false) + m.state.Store(ptr.New(stateBooting)) if err := m.bootstrap(); err != nil { + m.state.Store(ptr.New(stateIdle)) + return errors.WithMessage(err, "failed to bootstrap sprint manager") } @@ -171,13 +171,13 @@ func (m *SprintManager) Run() error { case <-m.spanner.GetSpanNotificationCh(): } - m.started.Store(true) + m.state.Store(ptr.New(stateRunning)) go func() { for { select { case <-m.ctx.Done(): - m.started.Store(false) + m.state.Store(ptr.New(stateIdle)) return case <-m.spanner.GetSpanNotificationCh(): @@ -196,7 +196,7 @@ func (m *SprintManager) Run() error { } // IsRunning returns current state of SprintManager. -func (m *SprintManager) IsRunning() bool { return m.started.Load() } +func (m *SprintManager) IsRunning() bool { return *m.state.Load() == stateRunning } // FutureValidators returns next n+2 producers of block. func (m *SprintManager) FutureValidators(header *ethtypes.Header) [2]*types.FutureValidatorInfo { diff --git a/blockchain/polygon/bor/valset/validator_set.go b/blockchain/polygon/bor/valset/validator_set.go index f62048d..60a8e07 100644 --- a/blockchain/polygon/bor/valset/validator_set.go +++ b/blockchain/polygon/bor/valset/validator_set.go @@ -1,4 +1,5 @@ // Package valset Tendermint leader selection algorithm +// //nolint:gofmt package valset @@ -622,14 +623,15 @@ func (vals *ValidatorSet) UpdateValidatorMap() { // UpdateWithChangeSet attempts to update the validator set with 'changes'. // It performs the following steps: -// - validates the changes making sure there are no duplicates and splits them in updates and deletes -// - verifies that applying the changes will not result in errors -// - computes the total voting power BEFORE removals to ensure that in the next steps the priorities -// across old and newly added validators are fair -// - computes the priorities of new validators against the final set -// - applies the updates against the validator set -// - applies the removals against the validator set -// - performs scaling and centering of priority values +// - validates the changes making sure there are no duplicates and splits them in updates and deletes +// - verifies that applying the changes will not result in errors +// - computes the total voting power BEFORE removals to ensure that in the next steps the priorities +// across old and newly added validators are fair +// - computes the priorities of new validators against the final set +// - applies the updates against the validator set +// - applies the removals against the validator set +// - performs scaling and centering of priority values +// // If an error is detected during verification steps, it is returned and the validator set // is not changed. func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { diff --git a/blockchain/polygon/test/prediction_test.go b/blockchain/polygon/test/prediction_test.go index 6bb6240..0215431 100644 --- a/blockchain/polygon/test/prediction_test.go +++ b/blockchain/polygon/test/prediction_test.go @@ -135,7 +135,7 @@ func TestFutureValidatorPredictionLive(t *testing.T) { blocksCh := make(chan uint64, blocksChBufferSize) - blockMap := syncmap.New[uint64, *types.EthBlockNotification]() + blockMap := syncmap.NewIntegerMapOf[uint64, *types.EthBlockNotification]() wg.Add(1) go func() { @@ -177,7 +177,7 @@ func TestFutureValidatorPredictionLive(t *testing.T) { } }() - headersMap := syncmap.New[uint64, *ethtypes.Header]() + headersMap := syncmap.NewIntegerMapOf[uint64, *ethtypes.Header]() wg.Add(1) go func() { @@ -210,7 +210,7 @@ func TestFutureValidatorPredictionLive(t *testing.T) { testCtx, testCancel := context.WithCancel(ctx) defer testCancel() - for blockMap.Len() <= int(bor.SprintSize) { + for blockMap.Size() <= int(bor.SprintSize) { time.Sleep(retryDuration) } @@ -233,7 +233,7 @@ func TestFutureValidatorPredictionLive(t *testing.T) { if func() bool { args := testNotificationArgs{} - if args.notification, exist = blockMap.Get(blockNum); !exist { + if args.notification, exist = blockMap.Load(blockNum); !exist { if _, opened = <-blocksCh; !opened { testCancel() } @@ -241,15 +241,15 @@ func TestFutureValidatorPredictionLive(t *testing.T) { return true } - if args.header, exist = headersMap.Get(blockNum); !exist { + if args.header, exist = headersMap.Load(blockNum); !exist { return true } - if args.validatorInfo[0], exist = headersMap.Get(args.notification.ValidatorInfo[0].BlockHeight); !exist { + if args.validatorInfo[0], exist = headersMap.Load(args.notification.ValidatorInfo[0].BlockHeight); !exist { return true } - if args.validatorInfo[1], exist = headersMap.Get(args.notification.ValidatorInfo[1].BlockHeight); !exist { + if args.validatorInfo[1], exist = headersMap.Load(args.notification.ValidatorInfo[1].BlockHeight); !exist { return true } @@ -329,7 +329,7 @@ func processNotification(t *testing.T, wg *sync.WaitGroup, data []byte, blocksCh blockNumber := blockNumberBigInt.Uint64() - blockMap.Set(blockNumber, notification.Params.Result) + blockMap.Store(blockNumber, notification.Params.Result) blocksCh <- blockNumber blocksCh <- notification.Params.Result.ValidatorInfo[0].BlockHeight @@ -339,7 +339,7 @@ func processNotification(t *testing.T, wg *sync.WaitGroup, data []byte, blocksCh } func processBlock(t *testing.T, blockNumber uint64, headersMap *syncmap.SyncMap[uint64, *ethtypes.Header], conn *websocket.Conn) bool { - if _, exists := headersMap.Get(blockNumber); exists { + if _, exists := headersMap.Load(blockNumber); exists { return true } @@ -371,7 +371,7 @@ func processBlock(t *testing.T, blockNumber uint64, headersMap *syncmap.SyncMap[ counter++ } - headersMap.Set(blockNumber, resp.Result) + headersMap.Store(blockNumber, resp.Result) t.Logf("block (%d) fetched", blockNumber) diff --git a/build.sh b/build.sh index 52724f3..787d00f 100755 --- a/build.sh +++ b/build.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash IMAGE=bloxroute/gateway:${1:-latest} # extract tag and keep in file (can't be done in docker). Used by Makefile -tag=`git describe --tags --always --dirty --match=v2* 2> /dev/null` +tag=`git describe --tags --always --dirty --match='v2*' 2> /dev/null` echo $tag > .gittag echo "Building container... $IMAGE" docker build --no-cache . -f Dockerfile --rm=true --platform linux/x86_64 -t $IMAGE -rm .gittag \ No newline at end of file +rm .gittag diff --git a/cmd/bxcli/main.go b/cmd/bxcli/main.go index 8cbb8d6..768c7bf 100644 --- a/cmd/bxcli/main.go +++ b/cmd/bxcli/main.go @@ -7,10 +7,14 @@ import ( log "github.com/bloXroute-Labs/gateway/v2/logger" pb "github.com/bloXroute-Labs/gateway/v2/protobuf" "github.com/bloXroute-Labs/gateway/v2/rpc" + "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" "github.com/urfave/cli/v2" "io" "os" + "time" ) func main() { @@ -236,16 +240,42 @@ func cmdDisconnectInboundPeer(ctx *cli.Context) error { } func cmdBlxrBatchTX(ctx *cli.Context) error { + transactions := ctx.StringSlice("transactions") + var txsAndSenders []*pb.TxAndSender + for _, transaction := range transactions { + var ethTx ethtypes.Transaction + txBytes, err := types.DecodeHex(transaction) + if err != nil { + fmt.Printf("Error - failed to decode transaction %v: %v. continue..", transaction, err) + continue + } + err = ethTx.UnmarshalBinary(txBytes) + if err != nil { + e := rlp.DecodeBytes(txBytes, ðTx) + if e != nil { + fmt.Printf("Error - failed to decode transaction bytes %v: %v. continue..", transaction, err) + continue + } + } + + ethSender, err := ethtypes.Sender(ethtypes.NewLondonSigner(ethTx.ChainId()), ðTx) + if err != nil { + fmt.Printf("Error - failed to get sender from the transaction %v: %v. continue..", transaction, err) + } + txsAndSenders = append(txsAndSenders, &pb.TxAndSender{Transaction: transaction, Sender: ethSender.Bytes()}) + + } err := rpc.GatewayConsoleCall( config.NewGRPCFromCLI(ctx), func(callCtx context.Context, client pb.GatewayClient) (interface{}, error) { return client.BlxrBatchTX(callCtx, &pb.BlxrBatchTXRequest{ - Transactions: ctx.StringSlice("transactions"), - NonceMonitoring: ctx.Bool("nonce-monitoring"), - NextValidator: ctx.Bool("next-validator"), - ValidatorsOnly: ctx.Bool("validators-only"), - Fallback: int32(ctx.Int("fallback")), - NodeValidation: ctx.Bool("node-validation"), + TransactionsAndSenders: txsAndSenders, + NonceMonitoring: ctx.Bool("nonce-monitoring"), + NextValidator: ctx.Bool("next-validator"), + ValidatorsOnly: ctx.Bool("validators-only"), + Fallback: int32(ctx.Int("fallback")), + NodeValidation: ctx.Bool("node-validation"), + SendingTime: time.Now().UnixNano(), }) }, ) diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go index addb62f..bd38866 100644 --- a/cmd/gateway/main.go +++ b/cmd/gateway/main.go @@ -88,6 +88,8 @@ func main() { utils.ForwardTransactionEndpoint, utils.ForwardTransactionMethod, utils.PolygonMainnetHeimdallEndpoint, + utils.BSCTransactionHoldDuration, + utils.BSCTransactionPassedDueDuration, }, Action: runGateway, } @@ -176,7 +178,7 @@ func runGateway(c *cli.Context) error { return fmt.Errorf("if websocket server management is enabled, a valid websocket address must be provided") } - gateway, err := nodes.NewGateway(ctx, bxConfig, bridge, wsManager, blockchainPeers, ethConfig.StaticPeers, gatewayPublicKey, sdn, sslCerts, len(ethConfig.StaticEnodes()), c.String(utils.PolygonMainnetHeimdallEndpoint.Name)) + gateway, err := nodes.NewGateway(ctx, bxConfig, bridge, wsManager, blockchainPeers, ethConfig.StaticPeers, gatewayPublicKey, sdn, sslCerts, len(ethConfig.StaticEnodes()), c.String(utils.PolygonMainnetHeimdallEndpoint.Name), c.Int(utils.BSCTransactionHoldDuration.Name), c.Int(utils.BSCTransactionPassedDueDuration.Name)) if err != nil { return err } @@ -224,11 +226,6 @@ func runGateway(c *cli.Context) error { log.Infof("skipping starting blockchain client as no enodes have been provided") } - var beaconChain *beacon.Chain - if startupBeaconNode || startupPrysmClient { - beaconChain = beacon.NewChain(ctx, ethConfig.GenesisTime, ethConfig.IgnoreSlotCount) - } - var beaconNode *beacon.Node if startupBeaconNode { var genesisPath string @@ -244,7 +241,7 @@ func runGateway(c *cli.Context) error { } log.Info("connecting to beacon node using ", genesisPath) - beaconNode, err := beacon.NewNode(ctx, c.String(utils.BlockchainNetworkFlag.Name), ethConfig, beaconChain, localGenesisFile, bridge) + beaconNode, err := beacon.NewNode(ctx, c.String(utils.BlockchainNetworkFlag.Name), ethConfig, localGenesisFile, bridge) if err != nil { return err } @@ -256,7 +253,7 @@ func runGateway(c *cli.Context) error { var prysmClient *beacon.PrysmClient if startupPrysmClient { - prysmClient = beacon.NewPrysmClient(ctx, ethConfig, beaconChain, prysmAddr, bridge, prysmEndpoint) + prysmClient = beacon.NewPrysmClient(ctx, ethConfig, prysmAddr, bridge, prysmEndpoint) prysmClient.Start() } @@ -281,7 +278,7 @@ func downloadGenesisFile(network, genesisFilePath string) (string, error) { case bxgateway.Ropsten: genesisFileURL = "https://github.com/eth-clients/merge-testnets/raw/main/ropsten-beacon-chain/genesis.ssz" case bxgateway.Zhejiang: - genesisFileURL = "https://github.com/ethpandaops/withdrawals-testnet/raw/master/zhejiang-testnet/custom_config_data/genesis.ssz" + genesisFileURL = "https://github.com/ethpandaops/withdrawals-testnet/raw/master/withdrawal-mainnet-shadowfork-3/custom_config_data/genesis.ssz" case bxgateway.Goerli: genesisFileURL = "https://github.com/eth-clients/goerli/raw/main/prater/genesis.ssz" diff --git a/connections/blockchain.go b/connections/blockchain.go index 17857b4..49340ee 100644 --- a/connections/blockchain.go +++ b/connections/blockchain.go @@ -11,6 +11,8 @@ import ( // Blockchain is a placeholder struct to represent a connection for blockchain nodes type Blockchain struct { + ConnDetails + endpoint types.NodeEndpoint log *log.Entry } @@ -33,16 +35,14 @@ func (b Blockchain) NodeEndpoint() types.NodeEndpoint { return b.endpoint } -// Info returns connection metadata -func (b Blockchain) Info() Info { - return Info{ - ConnectionType: utils.Blockchain, - NetworkNum: types.AllNetworkNum, - PeerIP: b.endpoint.IP, - PeerPort: int64(b.endpoint.Port), - PeerEnode: b.endpoint.PublicKey, - } -} +// GetPeerIP return peer IP +func (b Blockchain) GetPeerIP() string { return b.endpoint.IP } + +// GetPeerPort return peer port +func (b Blockchain) GetPeerPort() int64 { return int64(b.endpoint.Port) } + +// GetPeerEnode return peer enode +func (b Blockchain) GetPeerEnode() string { return b.endpoint.PublicKey } // ID returns placeholder func (b Blockchain) ID() Socket { diff --git a/connections/conn.go b/connections/conn.go index 07a8a8a..5504c88 100644 --- a/connections/conn.go +++ b/connections/conn.go @@ -19,8 +19,9 @@ type EndpointConn interface { // Conn defines a network interface that sends and receives messages type Conn interface { + ConnectionDetails + ID() Socket - Info() Info IsOpen() bool IsDisabled() bool diff --git a/connections/connection.go b/connections/connection.go new file mode 100644 index 0000000..e17c085 --- /dev/null +++ b/connections/connection.go @@ -0,0 +1,140 @@ +package connections + +import ( + "time" + + "github.com/bloXroute-Labs/gateway/v2/types" + "github.com/bloXroute-Labs/gateway/v2/utils" +) + +// ConnectionDetails interface of base details for all connections +type ConnectionDetails interface { + GetNodeID() types.NodeID + GetPeerIP() string + GetVersion() string + GetPeerPort() int64 + GetPeerEnode() string + GetLocalPort() int64 + GetAccountID() types.AccountID + GetNetworkNum() types.NetworkNum + GetConnectedAt() time.Time + GetCapabilities() types.CapabilityFlags + GetConnectionType() utils.NodeType + GetConnectionState() string + + IsLocalGEO() bool + IsInitiator() bool + IsSameRegion() bool + IsPrivateNetwork() bool +} + +// ConnDetails is base details for all connections +type ConnDetails struct{} + +// GetNodeID return node ID +func (b ConnDetails) GetNodeID() types.NodeID { return "" } + +// GetPeerIP return peer IP +func (b ConnDetails) GetPeerIP() string { return "" } + +// GetVersion return version +func (b ConnDetails) GetVersion() string { return "" } + +// GetPeerPort return peer port +func (b ConnDetails) GetPeerPort() int64 { return 0 } + +// GetPeerEnode return peer enode +func (b ConnDetails) GetPeerEnode() string { return "" } + +// GetLocalPort return local port +func (b ConnDetails) GetLocalPort() int64 { return 0 } + +// GetAccountID return account ID (default empty) +func (b ConnDetails) GetAccountID() types.AccountID { return "" } + +// GetNetworkNum gets the message network number +func (b ConnDetails) GetNetworkNum() types.NetworkNum { return types.AllNetworkNum } + +// GetConnectedAt gets ttime of connection +func (b ConnDetails) GetConnectedAt() time.Time { return time.Time{} } + +// GetCapabilities return capabilities +func (b ConnDetails) GetCapabilities() types.CapabilityFlags { return 0 } + +// GetConnectionType returns type of the connection +func (b ConnDetails) GetConnectionType() utils.NodeType { return utils.Blockchain } + +// GetConnectionState returns state of the connection +func (b ConnDetails) GetConnectionState() string { return "" } + +// IsLocalGEO indicates if the peer is form the same GEO as we (China vs non-China) +func (b ConnDetails) IsLocalGEO() bool { return false } + +// IsInitiator returns whether this node initiated the connection +func (b ConnDetails) IsInitiator() bool { return false } + +// IsSameRegion indicates if the peer is from the same region as we (us-east1, eu-west1, ...) +func (b ConnDetails) IsSameRegion() bool { return false } + +// IsPrivateNetwork indicates of the peer connection is over a private network (CEN) +func (b ConnDetails) IsPrivateNetwork() bool { return false } + +// IsCustomerGateway indicates whether the connected gateway belongs to a customer +func IsCustomerGateway(connectionType utils.NodeType, accountID types.AccountID) bool { + return connectionType&(utils.ExternalGateway|utils.GatewayGo) != 0 && accountID != types.BloxrouteAccountID +} + +// IsBloxrouteGateway indicates if the connected gateway belongs to bloxroute +func IsBloxrouteGateway(connectionType utils.NodeType, accountID types.AccountID) bool { + return connectionType&utils.Gateway != 0 && accountID == types.BloxrouteAccountID +} + +// IsGateway indicates if the connection is a gateway +func IsGateway(connectionType utils.NodeType) bool { + return connectionType&utils.Gateway != 0 +} + +// IsMevMinerGateway indicates if the connection is a mev-miner gateway +func IsMevMinerGateway(capabilities types.CapabilityFlags) bool { + return capabilities&types.CapabilityMEVMiner != 0 +} + +// IsMevBuilderGateway indicates if the connection is a mev-builder gateway +func IsMevBuilderGateway(capabilities types.CapabilityFlags) bool { + return capabilities&types.CapabilityMEVBuilder != 0 +} + +// IsBDN indicates if the connection is a BDN gateway +func IsBDN(capabilities types.CapabilityFlags) bool { + return capabilities&types.CapabilityBDN != 0 +} + +// IsCloudAPI indicates if the connection is a cloud-api +func IsCloudAPI(connectionType utils.NodeType) bool { + return connectionType&utils.CloudAPI != 0 +} + +// IsLocalRegion indicates if the connection is a GW or a cloud-api +func IsLocalRegion(connectionType utils.NodeType) bool { + return IsCloudAPI(connectionType) || IsGateway(connectionType) +} + +// IsRelayTransaction indicates if the connection is a transaction relay +func IsRelayTransaction(connectionType utils.NodeType) bool { + return connectionType&utils.RelayTransaction != 0 +} + +// IsRelayProxy indicates if the connection is a relay proxy +func IsRelayProxy(connectionType utils.NodeType) bool { + return connectionType&utils.RelayProxy != 0 +} + +// IsRelay indicates if the connection is a relay type +func IsRelay(connectionType utils.NodeType) bool { + return connectionType&utils.RelayProxy != 0 || connectionType&utils.RelayTransaction != 0 || connectionType&utils.RelayBlock != 0 +} + +// IsGrpc indicates if the connection is a gRPC type +func IsGrpc(connectionType utils.NodeType) bool { + return connectionType&utils.GRPC != 0 +} diff --git a/connections/handler/bxconn.go b/connections/handler/bxconn.go index d4a4e14..626c7e9 100644 --- a/connections/handler/bxconn.go +++ b/connections/handler/bxconn.go @@ -105,27 +105,41 @@ func (b *BxConn) Send(msg bxmessage.Message) error { return nil } -// Info returns connection metadata -func (b *BxConn) Info() connections.Info { - meta := b.Conn.Info() - return connections.Info{ - NodeID: b.peerID, - AccountID: b.accountID, - PeerIP: meta.PeerIP, - PeerPort: meta.PeerPort, - LocalPort: b.localPort, - ConnectionState: "todo", - ConnectionType: b.connectionType, - FromMe: meta.FromMe, - NetworkNum: b.networkNum, - LocalGEO: b.localGEO, - PrivateNetwork: b.privateNetwork, - Capabilities: b.capabilities, - Version: b.clientVersion, - SameRegion: b.sameRegion, - ConnectedAt: b.connectedAt, - } -} +// GetNodeID return node ID +func (b *BxConn) GetNodeID() types.NodeID { return b.peerID } + +// GetLocalPort return local port +func (b *BxConn) GetLocalPort() int64 { return b.localPort } + +// GetVersion return version +func (b *BxConn) GetVersion() string { return b.clientVersion } + +// GetCapabilities return capabilities +func (b *BxConn) GetCapabilities() types.CapabilityFlags { return b.capabilities } + +// GetConnectionType returns type of the connection +func (b *BxConn) GetConnectionType() utils.NodeType { return b.connectionType } + +// GetConnectionState returns state of the connection +func (b *BxConn) GetConnectionState() string { return "todo" } + +// GetConnectedAt gets ttime of connection +func (b *BxConn) GetConnectedAt() time.Time { return b.connectedAt } + +// GetNetworkNum returns network number +func (b *BxConn) GetNetworkNum() types.NetworkNum { return b.networkNum } + +// IsLocalGEO indicates if the peer is form the same GEO as we (China vs non-China) +func (b *BxConn) IsLocalGEO() bool { return b.localGEO } + +// IsSameRegion indicates if the peer is from the same region as we (us-east1, eu-west1, ...) +func (b *BxConn) IsSameRegion() bool { return b.sameRegion } + +// IsPrivateNetwork indicates of the peer connection is over a private network (CEN) +func (b *BxConn) IsPrivateNetwork() bool { return b.privateNetwork } + +// GetAccountID return account ID +func (b *BxConn) GetAccountID() types.AccountID { return b.accountID } // IsOpen returns when the connection is ready for broadcasting func (b *BxConn) IsOpen() bool { @@ -145,10 +159,8 @@ func (b *BxConn) Connect() error { return err } - connInfo := b.Conn.Info() - - b.peerID = connInfo.NodeID - b.accountID = connInfo.AccountID + b.peerID = b.Conn.GetNodeID() + b.accountID = b.Conn.GetAccountID() b.connectedAt = b.clock.Now() b.stringRepresentation = fmt.Sprintf("%v/%v@%v{%v}", b.connectionType, b.Conn, b.accountID, b.peerID) @@ -207,7 +219,7 @@ func (b *BxConn) ProcessMessage(msg bxmessage.MessageBytes) { ack := bxmessage.Ack{} _ = b.Send(&ack) - if !b.Info().FromMe { + if !b.IsInitiator() { hello := bxmessage.Hello{NodeID: b.nodeID, Protocol: b.Protocol()} hello.SetNetworkNum(b.networkNum) _ = b.Send(&hello) @@ -217,7 +229,7 @@ func (b *BxConn) ProcessMessage(msg bxmessage.MessageBytes) { case bxmessage.AckType: b.lock.Lock() // avoid racing with Close - if !b.Info().FromMe { + if !b.IsInitiator() { b.setConnectionEstablished() } b.lock.Unlock() @@ -359,7 +371,7 @@ func (b *BxConn) handleNonces(nodeNonce, peerNonce uint64) { // readLoop connects and reads messages from the socket. // If we are the initiator of the connection we auto-recover on disconnect. func (b *BxConn) readLoop() { - isInitiator := b.Info().FromMe + isInitiator := b.IsInitiator() for { err := b.Connect() if err != nil { diff --git a/connections/handler/relay.go b/connections/handler/relay.go index be18b50..f9a5fa6 100644 --- a/connections/handler/relay.go +++ b/connections/handler/relay.go @@ -81,7 +81,7 @@ func (r *Relay) NodeEndpoint() types.NodeEndpoint { func (r *Relay) addBdnToUpscale() { const NOTINUSE = 0 - ipPort := net.JoinHostPort(r.Info().PeerIP, strconv.FormatInt(r.Info().PeerPort, 10)) + ipPort := net.JoinHostPort(r.GetPeerIP(), strconv.FormatInt(r.GetPeerPort(), 10)) addr, err := net.ResolveTCPAddr("tcp", ipPort) if err != nil { panic("relay ip port is invalid") @@ -115,7 +115,7 @@ func (r *Relay) ProcessMessage(msg bxmessage.MessageBytes) { case bxmessage.HelloType: // if the running program is gw and the connected component is relay - we want to add the relay as an active peer to upscale if r.Node.NodeStatus().Capabilities&types.CapabilityBDN != 0 && - r.Info().IsRelay() { + connections.IsRelay(r.GetConnectionType()) { r.addBdnToUpscale() } r.BxConn.ProcessMessage(msg) diff --git a/connections/info.go b/connections/info.go deleted file mode 100644 index 6021b6c..0000000 --- a/connections/info.go +++ /dev/null @@ -1,97 +0,0 @@ -package connections - -import ( - "github.com/bloXroute-Labs/gateway/v2/types" - "github.com/bloXroute-Labs/gateway/v2/utils" - "time" -) - -// Info represents various information fields about the connection. -type Info struct { - NodeID types.NodeID - AccountID types.AccountID - PeerIP string - PeerPort int64 - PeerEnode string - LocalPort int64 // either the local listening server port, or 0 for outbound connections - ConnectionType utils.NodeType - ConnectionState string // TODO: flag? - NetworkNum types.NetworkNum - FromMe bool - LocalGEO bool - PrivateNetwork bool - Capabilities types.CapabilityFlags - Version string - SameRegion bool - ConnectedAt time.Time -} - -// IsCustomerGateway indicates whether the connected gateway belongs to a customer -func (ci Info) IsCustomerGateway() bool { - return ci.ConnectionType&(utils.ExternalGateway|utils.GatewayGo) != 0 && ci.AccountID != types.BloxrouteAccountID -} - -// IsBloxrouteGateway indicates if the connected gateway belongs to bloxroute -func (ci Info) IsBloxrouteGateway() bool { - return ci.ConnectionType&utils.Gateway != 0 && ci.AccountID == types.BloxrouteAccountID -} - -// IsGateway indicates if the connection is a gateway -func (ci Info) IsGateway() bool { - return ci.ConnectionType&utils.Gateway != 0 -} - -// IsMevMinerGateway indicates if the connection is a mev-miner gateway -func (ci Info) IsMevMinerGateway() bool { - return ci.Capabilities&types.CapabilityMEVMiner != 0 -} - -// IsMevBuilderGateway indicates if the connection is a mev-builder gateway -func (ci Info) IsMevBuilderGateway() bool { - return ci.Capabilities&types.CapabilityMEVBuilder != 0 -} - -// IsBDN indicates if the connection is a BDN gateway -func (ci Info) IsBDN() bool { - return ci.Capabilities&types.CapabilityBDN != 0 -} - -// IsCloudAPI indicates if the connection is a cloud-api -func (ci Info) IsCloudAPI() bool { - return ci.ConnectionType&utils.CloudAPI != 0 -} - -// IsLocalRegion indicates if the connection is a GW or a cloud-api -func (ci Info) IsLocalRegion() bool { - return ci.IsCloudAPI() || ci.IsGateway() -} - -// IsRelayTransaction indicates if the connection is a transaction relay -func (ci Info) IsRelayTransaction() bool { - return ci.ConnectionType&utils.RelayTransaction != 0 -} - -// IsRelayProxy indicates if the connection is a relay proxy -func (ci Info) IsRelayProxy() bool { - return ci.ConnectionType&utils.RelayProxy != 0 -} - -// IsRelay indicates if the connection is a relay type -func (ci Info) IsRelay() bool { - return ci.ConnectionType&utils.RelayProxy != 0 || ci.ConnectionType&utils.RelayTransaction != 0 || ci.ConnectionType&utils.RelayBlock != 0 -} - -// IsPrivateNetwork indicates of the peer connection is over a private network (CEN) -func (ci Info) IsPrivateNetwork() bool { - return ci.PrivateNetwork -} - -// IsLocalGEO indicates if the peer is form the same GEO as we (China vs non-China) -func (ci Info) IsLocalGEO() bool { - return ci.LocalGEO -} - -// IsSameRegion indicates if the peer is from the same region as we (us-east1, eu-west1, ...) -func (ci Info) IsSameRegion() bool { - return ci.SameRegion -} diff --git a/connections/rpc.go b/connections/rpc.go index 001ecd4..d10ea12 100644 --- a/connections/rpc.go +++ b/connections/rpc.go @@ -13,6 +13,8 @@ var rpcTLSConn = TLS{} // RPCConn is a placeholder struct to represent connection requests from RPC transaction requests type RPCConn struct { + ConnDetails + AccountID types.AccountID RemoteAddress string networkNum types.NetworkNum @@ -40,13 +42,19 @@ func (r RPCConn) ID() Socket { return rpcTLSConn } -// Info returns connection metadata -func (r RPCConn) Info() Info { - return Info{ - AccountID: r.AccountID, - ConnectionType: r.connectionType, - NetworkNum: r.networkNum, - } +// GetConnectionType returns type of the connection +func (r RPCConn) GetConnectionType() utils.NodeType { + return r.connectionType +} + +// GetNetworkNum gets the message network number +func (r RPCConn) GetNetworkNum() types.NetworkNum { + return r.networkNum +} + +// GetAccountID return account ID +func (r RPCConn) GetAccountID() types.AccountID { + return r.AccountID } // IsOpen is never true, since the RPCConn is not writable diff --git a/connections/sdnhttp.go b/connections/sdnhttp.go index 49ed2f3..37562ff 100644 --- a/connections/sdnhttp.go +++ b/connections/sdnhttp.go @@ -66,6 +66,7 @@ type SDNHTTP interface { MinTxAge() time.Duration SendNodeEvent(event sdnmessage.NodeEvent, id types.NodeID) Get(endpoint string, requestBody []byte) ([]byte, error) + GetQuotaUsage(accountID string) (*QuotaResponseBody, error) } // realSDNHTTP is a connection to the bloxroute API @@ -102,6 +103,17 @@ type RelayInstruction struct { // ConnInstructionType specifies connection or disconnection type ConnInstructionType int +type quotaRequestBody struct { + AccountID string `json:"account_id"` +} + +// QuotaResponseBody quota usage response body +type QuotaResponseBody struct { + AccountID string `json:"account_id"` + QuotaFilled int `json:"quota_filled"` + QuotaLimit int `json:"quota_limit"` +} + const ( // Connect is the instruction to connect to a relay Connect ConnInstructionType = iota @@ -429,6 +441,29 @@ func (s *realSDNHTTP) close(resp *http.Response) { } } +func (s *realSDNHTTP) GetQuotaUsage(accountID string) (*QuotaResponseBody, error) { + reqBody := quotaRequestBody{ + AccountID: accountID, + } + body, err := json.Marshal(reqBody) + if err != nil { + log.Errorf("unable to marshal SDN request: %v", err) + return nil, err + } + + resp, err := s.Get("/accounts/quota-status", body) + if err != nil { + return nil, err + } + + quotaResp := QuotaResponseBody{} + if err = json.Unmarshal(resp, "aResp); err != nil { + return nil, err + } + + return "aResp, nil +} + func (s *realSDNHTTP) getAccountModelWithEndpoint(accountID types.AccountID, endpoint string) (sdnmessage.Account, error) { url := fmt.Sprintf("%v/%v/%v", s.sdnURL, endpoint, accountID) accountModel := sdnmessage.Account{} diff --git a/connections/sslconn.go b/connections/sslconn.go index cd604d2..3c4ef64 100644 --- a/connections/sslconn.go +++ b/connections/sslconn.go @@ -9,6 +9,7 @@ import ( "github.com/bloXroute-Labs/gateway/v2/bxmessage" log "github.com/bloXroute-Labs/gateway/v2/logger" + "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" ) @@ -27,6 +28,7 @@ const ( // SSLConn represents the basic connection properties for connections opened between bloxroute nodes. SSLConn does not define any message handlers, and only implements the Conn interface. type SSLConn struct { + ConnDetails Socket writer *bufio.Writer @@ -129,21 +131,26 @@ func (s *SSLConn) ID() Socket { return s.Socket } -// Info returns connection details, include details parsed from certificates -func (s *SSLConn) Info() Info { - return Info{ - NodeID: s.extensions.NodeID, - AccountID: s.extensions.AccountID, - PeerIP: s.ip, - PeerPort: s.port, - LocalPort: LocalInitiatedPort, - ConnectionType: s.extensions.NodeType, - ConnectionState: "", - NetworkNum: 0, - FromMe: s.isInitiator(), - ConnectedAt: s.connectedAt, - } -} +// GetConnectionType returns type of the connection +func (s SSLConn) GetConnectionType() utils.NodeType { return s.extensions.NodeType } + +// GetNodeID return node ID +func (s SSLConn) GetNodeID() types.NodeID { return s.extensions.NodeID } + +// GetPeerIP return peer IP +func (s SSLConn) GetPeerIP() string { return s.ip } + +// GetPeerPort return peer port +func (s SSLConn) GetPeerPort() int64 { return s.port } + +// GetLocalPort return local port +func (s SSLConn) GetLocalPort() int64 { return LocalInitiatedPort } + +// GetConnectedAt gets ttime of connection +func (s SSLConn) GetConnectedAt() time.Time { return s.connectedAt } + +// GetAccountID return account ID +func (s SSLConn) GetAccountID() types.AccountID { return s.extensions.AccountID } // IsOpen indicates whether the socket connection is open func (s SSLConn) IsOpen() bool { @@ -317,8 +324,8 @@ func (s SSLConn) String() string { return s.Socket.RemoteAddr().String() } -// isInitiator returns whether this node initiated the connection -func (s *SSLConn) isInitiator() bool { +// IsInitiator returns whether this node initiated the connection +func (s *SSLConn) IsInitiator() bool { return s.port != RemoteInitiatedPort } diff --git a/constants.go b/constants.go index 1dba0f1..e894894 100644 --- a/constants.go +++ b/constants.go @@ -16,6 +16,9 @@ const AllInterfaces = "0.0.0.0" // MicroSecTimeFormat - use for representing tx "time" in feed const MicroSecTimeFormat = "2006-01-02 15:04:05.000000" +// MillisecondsToNanosecondsMultiplier used to convert milliseconds to nanoseconds +const MillisecondsToNanosecondsMultiplier = 1000000 + // SlowPingPong - ping/pong delay above it is considered a problem const SlowPingPong = int64(100000) // 100 ms @@ -113,7 +116,6 @@ const ( GatewayTimeout = 504 ) -// const ( // BloxrouteBuilderName - set bloxroute mev builder name BloxrouteBuilderName = "bloxroute" @@ -158,11 +160,14 @@ const BSCMainnetNum types.NetworkNum = 10 // BSCChainID - BSC chain ID const BSCChainID = 56 +// PolygonMainnetNum - for Polygon main net blockchain network number +const PolygonMainnetNum types.NetworkNum = 36 + // RopstenNum - for Ropsten blockchain network number const RopstenNum types.NetworkNum = 32 -// PolygonMainnetNum - for Polygon main net blockchain network number -const PolygonMainnetNum types.NetworkNum = 36 +// GoerliNum - for Goerli blockchain network number +const GoerliNum types.NetworkNum = 45 // BlockchainNetworkToNetworkNum converts blockchain network to number var BlockchainNetworkToNetworkNum = map[string]types.NetworkNum{ @@ -170,4 +175,12 @@ var BlockchainNetworkToNetworkNum = map[string]types.NetworkNum{ BSCMainnet: BSCMainnetNum, PolygonMainnet: PolygonMainnetNum, Ropsten: RopstenNum, + Goerli: GoerliNum, +} + +// NetworkToBlockDuration defines block interval for each network +var NetworkToBlockDuration = map[string]time.Duration{ + Mainnet: 12 * time.Second, + BSCMainnet: 3 * time.Second, + PolygonMainnet: 2 * time.Second, } diff --git a/go.mod b/go.mod index f219748..f538297 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/bloXroute-Labs/gateway/v2 -go 1.18 +go 1.19 require ( github.com/bits-and-blooms/bloom/v3 v3.3.1 @@ -14,21 +14,21 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/jinzhu/copier v0.3.5 - github.com/libp2p/go-libp2p v0.24.0 - github.com/libp2p/go-libp2p-pubsub v0.8.0 + github.com/libp2p/go-libp2p v0.26.2 + github.com/libp2p/go-libp2p-pubsub v0.9.3 github.com/multiformats/go-multiaddr v0.8.0 github.com/orandin/lumberjackrus v1.0.1 - github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 github.com/pkg/errors v0.9.1 github.com/prysmaticlabs/fastssz v0.0.0-20220628121656-93dfe28febab github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 - github.com/prysmaticlabs/prysm/v3 v3.2.2-rc.2 - github.com/satori/go.uuid v1.2.0 + github.com/prysmaticlabs/prysm/v4 v4.0.1 + github.com/puzpuzpuz/xsync/v2 v2.4.0 + github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102 github.com/sirupsen/logrus v1.9.0 github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 github.com/stretchr/testify v1.8.1 github.com/struCoder/pidusage v0.1.3 - github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa + github.com/urfave/cli/v2 v2.23.7 github.com/wk8/go-ordered-map v1.0.0 github.com/wk8/go-ordered-map/v2 v2.1.6 github.com/zhouzhuojie/conditions v0.2.3 @@ -44,7 +44,7 @@ require ( contrib.go.opencensus.io/exporter/jaeger v0.2.1 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/DataDog/zstd v1.5.2 // indirect - github.com/VictoriaMetrics/fastcache v1.10.0 // indirect + github.com/VictoriaMetrics/fastcache v1.12.0 // indirect github.com/allegro/bigcache v1.2.1 // indirect github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect @@ -79,6 +79,7 @@ require ( github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.3.0 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect @@ -91,13 +92,12 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect + github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e // indirect - github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.0 // indirect + github.com/holiman/uint256 v1.2.1 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/ipfs/go-cid v0.3.2 // indirect - github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect @@ -113,21 +113,16 @@ require ( github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect github.com/libp2p/go-mplex v0.7.0 // indirect - github.com/libp2p/go-msgio v0.2.0 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.1.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect - github.com/libp2p/go-openssl v0.1.0 // indirect github.com/libp2p/go-reuseport v0.2.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect - github.com/lucas-clemente/quic-go v0.31.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-isatty v0.0.16 // indirect - github.com/mattn/go-pointer v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect @@ -147,12 +142,11 @@ require ( github.com/multiformats/go-multibase v0.1.1 // indirect github.com/multiformats/go-multicodec v0.7.0 // indirect github.com/multiformats/go-multihash v0.2.1 // indirect - github.com/multiformats/go-multistream v0.3.3 // indirect + github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo/v2 v2.5.1 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/philhofer/fwd v1.1.1 // indirect @@ -161,25 +155,27 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/prometheus/tsdb v0.10.0 // indirect github.com/prysmaticlabs/gohashtree v0.0.2-alpha // indirect github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-19 v0.2.1 // indirect + github.com/quic-go/qtls-go1-20 v0.1.1 // indirect + github.com/quic-go/quic-go v0.33.0 // indirect + github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rivo/uniseg v0.3.4 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/schollz/progressbar/v3 v3.3.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect github.com/tinylib/msgp v1.1.5 // indirect - github.com/tklauser/go-sysconf v0.3.10 // indirect - github.com/tklauser/numcpus v0.5.0 // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect - github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.etcd.io/bbolt v1.3.5 // indirect @@ -211,6 +207,7 @@ require ( k8s.io/klog/v2 v2.80.0 // indirect k8s.io/utils v0.0.0-20200520001619-278ece378a50 // indirect lukechampine.com/blake3 v1.1.7 // indirect + nhooyr.io/websocket v1.8.7 // indirect sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) @@ -218,5 +215,5 @@ require ( replace ( // Used in prysm which also has replace for this but for some reason go list which is used in some IDEs does not recognize semver before replace github.com/MariusVanDerWijden/tx-fuzz => github.com/marcopolo/tx-fuzz v0.0.0-20220927011827-b5c461bc7cae - github.com/ethereum/go-ethereum => github.com/bloXroute-Labs/go-ethereum v1.11.1-upscale + github.com/ethereum/go-ethereum => github.com/bloXroute-Labs/go-ethereum v1.11.5-upscale.1 ) diff --git a/go.sum b/go.sum index 8cca6e7..d52df95 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqR github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= -github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= +github.com/VictoriaMetrics/fastcache v1.12.0 h1:vnVi/y9yKDcD9akmc4NqAoqgQhJrOwUF+j9LTgn4QDE= +github.com/VictoriaMetrics/fastcache v1.12.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= @@ -114,8 +114,8 @@ github.com/bits-and-blooms/bitset v1.3.3 h1:R1XWiopGiXf66xygsiLpzLo67xEYvMkHw3w+ github.com/bits-and-blooms/bitset v1.3.3/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ= github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90= -github.com/bloXroute-Labs/go-ethereum v1.11.1-upscale h1:JcqLOlBFZC/nYf7S2TmaCTJA4rymYeSbJ2fWJ2/2Qbk= -github.com/bloXroute-Labs/go-ethereum v1.11.1-upscale/go.mod h1:ZkxcBvuVfQ3KhXzwszS3ZQvFW80J+jgBLVyZS5ph9bg= +github.com/bloXroute-Labs/go-ethereum v1.11.5-upscale.1 h1:UtcrLzNFAEj+HjIccWgziavxIZPPWmwBRLmRKO+kiMo= +github.com/bloXroute-Labs/go-ethereum v1.11.5-upscale.1/go.mod h1:ctmCBnsAHv+OBLxNg1hOjY66JKgrv+jIJvLaBLwRRd8= github.com/bloXroute-Labs/upscale-client v0.0.0-20230305080936-5cffcb5b470c h1:5SRpZKPvzPS4bYYprS59CyL68V4SOtn6jjYk9aubmjk= github.com/bloXroute-Labs/upscale-client v0.0.0-20230305080936-5cffcb5b470c/go.mod h1:4d1zYTKDopVpvQkG6HTVJ6BCAGv8aeoTF79QkcLxe/k= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= @@ -197,7 +197,6 @@ github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018/go.mod h1:MI github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -255,7 +254,11 @@ github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnR github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -265,12 +268,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= @@ -282,19 +283,32 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= @@ -432,6 +446,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= +github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -439,12 +455,10 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e h1:wCMygKUQhmcQAjlk2Gquzq6dLmyMv2kF+llRspoRgrk= github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/holiman/uint256 v1.2.1 h1:XRtyuda/zw2l+Bq/38n5XUoEF72aSOu/77Thd9pPp2o= +github.com/holiman/uint256 v1.2.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= @@ -462,9 +476,6 @@ github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= -github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= @@ -512,6 +523,7 @@ github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -538,33 +550,34 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.24.0 h1:DQk/5bBon+yUVIGTeRVBmOYpZzoBHx/VTC0xoLgJGG4= -github.com/libp2p/go-libp2p v0.24.0/go.mod h1:28t24CYDlnBs23rIs1OclU89YbhgibrBq2LFbMe+cFw= +github.com/libp2p/go-libp2p v0.26.2 h1:eHEoW/696FP7/6DxOvcrKfTD6Bi0DExxiMSZUJxswA0= +github.com/libp2p/go-libp2p v0.26.2/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-pubsub v0.8.0 h1:KygfDpaa9AeUPGCVcpVenpXNFauDn+5kBYu3EjcL3Tg= -github.com/libp2p/go-libp2p-pubsub v0.8.0/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= +github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= -github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= @@ -574,8 +587,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lucas-clemente/quic-go v0.31.0 h1:MfNp3fk0wjWRajw6quMFA3ap1AVtlU+2mtwmbVogB2M= -github.com/lucas-clemente/quic-go v0.31.0/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -583,14 +594,8 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= -github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= -github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= -github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/marten-seemann/webtransport-go v0.4.1 h1:8Ir7OoAvtp79yxQpa3foTKIPuoH+0eKpisHObJyu9Sk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -604,12 +609,10 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -677,8 +680,8 @@ github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6y github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= +github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= +github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= @@ -700,7 +703,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -734,8 +736,6 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -743,8 +743,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/orandin/lumberjackrus v1.0.1 h1:7ysDQ0MHD79zIFN9/EiDHjUcgopNi5ehtxFDy8rUkWo= github.com/orandin/lumberjackrus v1.0.1/go.mod h1:xYLt6H8W93pKnQgUQaxsApS0Eb4BwHLOkxk5DVzf5H0= -github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU= -github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -752,7 +750,9 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= @@ -808,8 +808,6 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= -github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/prysmaticlabs/fastssz v0.0.0-20220628121656-93dfe28febab h1:Y3PcvUrnneMWLuypZpwPz8P70/DQsz6KgV9JveKpyZs= github.com/prysmaticlabs/fastssz v0.0.0-20220628121656-93dfe28febab/go.mod h1:MA5zShstUwCQaE9faGHgCGvEWUbG87p4SAXINhmCkvg= github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= @@ -819,15 +817,27 @@ github.com/prysmaticlabs/gohashtree v0.0.2-alpha/go.mod h1:4pWaT30XoEx1j8KNJf3TV github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c h1:9PHRCuO/VN0s9k+RmLykho7AjDxblNYI5bYKed16NPU= github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c/go.mod h1:ZRws458tYHS/Zs936OQ6oCrL+Ict5O4Xpwve1UQ6C9M= github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h1:q9wE0ZZRdTUAAeyFP/w0SwBEnCqlVy2+on6X2/e+eAU= -github.com/prysmaticlabs/prysm/v3 v3.2.2-rc.2 h1:aafUJ+1KczpNPeXH9LC+sXWFN/gI3w5/qbTYnzix4cg= -github.com/prysmaticlabs/prysm/v3 v3.2.2-rc.2/go.mod h1:tk/LvCJpiq/EpMPkSJwE0OYBLnXOiMktEN+Xt1IIKkE= +github.com/prysmaticlabs/prysm/v4 v4.0.1 h1:5A3YJb+FdTFT9qn+P0ZkxLAK0+yVgctJLmkq5R6um6A= +github.com/prysmaticlabs/prysm/v4 v4.0.1/go.mod h1:EQ8hYKry3NYHGirqLiQh8gXJ/O12dWZXpKC67CO2GQU= +github.com/puzpuzpuz/xsync/v2 v2.4.0 h1:5sXAMHrtx1bg9nbRZTOn8T4MkWe5V+o8yKRH02Eznag= +github.com/puzpuzpuz/xsync/v2 v2.4.0/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= +github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= +github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= +github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= +github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= +github.com/quic-go/webtransport-go v0.5.2/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= -github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -842,8 +852,9 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102 h1:WAQaHPfnpevd8SKXCcy5nk3JzEv2h5Q0kSwvoMqXiZs= +github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/schollz/progressbar/v3 v3.3.4 h1:nMinx+JaEm/zJz4cEyClQeAw5rsYSB5th3xv+5lV6Vg= github.com/schollz/progressbar/v3 v3.3.4/go.mod h1:Rp5lZwpgtYmlvmGo1FyDwXMqagyRBQYSDwzlP9QDu84= @@ -888,8 +899,6 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 h1:marA1XQDC7N870zmSFIoHZpIUduK80USeY0Rkuflgp4= github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -938,11 +947,10 @@ github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1: github.com/tinylib/msgp v1.1.5 h1:2gXmtWueD2HefZHQe1QOy9HVzmFrLOVvsXwXBQ0ayy0= github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= -github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= -github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= -github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= -github.com/tklauser/numcpus v0.5.0 h1:ooe7gN0fg6myJ0EKoTAf5hebTZrH52px3New/D9iJ+A= -github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= @@ -951,14 +959,16 @@ github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2n github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= +github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= @@ -967,8 +977,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= -github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8= github.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk= github.com/wk8/go-ordered-map/v2 v2.1.6 h1:vOC/zsyAuGiLrAatj6b+yJuJzeRKQG0FLQQ4JFtMwhc= @@ -1013,7 +1021,6 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -1025,14 +1032,12 @@ go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpK go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= @@ -1251,16 +1256,15 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1544,6 +1548,8 @@ k8s.io/utils v0.0.0-20200520001619-278ece378a50 h1:ZtTUW5+ZWaoqjR3zOpRa7oFJ5d4aA k8s.io/utils v0.0.0-20200520001619-278ece378a50/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/jsonrpc/request.go b/jsonrpc/request.go index 88f9cf7..fbf5605 100644 --- a/jsonrpc/request.go +++ b/jsonrpc/request.go @@ -45,6 +45,7 @@ type RPCTxPayload struct { OriginalRPCMethod RPCRequestType `json:"original_rpc_method"` NodeValidation bool `json:"node_validation"` NoBackrun bool `json:"no_backrun"` + FrontRunningProtection bool `json:"front_running_protection"` } // RPCBatchTxPayload is the payload of blxr_batch_tx request @@ -65,6 +66,7 @@ type rpcTxJSON struct { OriginalRPCMethod RPCRequestType `json:"original_rpc_method"` NodeValidation bool `json:"node_validation"` NoBackrun bool `json:"no_backrun"` + FrontRunningProtection bool `json:"front_running_protection"` } // UnmarshalJSON provides a compatibility layer for go-ethereum style RPC calls, which are [object], instead of just object. @@ -95,5 +97,6 @@ func (p *RPCTxPayload) UnmarshalJSON(b []byte) error { p.OriginalRPCMethod = payload.OriginalRPCMethod p.NodeValidation = payload.NodeValidation p.NoBackrun = payload.NoBackrun + p.FrontRunningProtection = payload.FrontRunningProtection return nil } diff --git a/nodes/bx.go b/nodes/bx.go index 17d992f..e863380 100644 --- a/nodes/bx.go +++ b/nodes/bx.go @@ -57,12 +57,12 @@ func NewBx(bxConfig *config.Bx, dataDir string, accountsFetcher AccountsFetcher) // OnConnEstablished - a callback function. Called when new connection is established func (bn *Bx) OnConnEstablished(conn connections.Conn) error { - connInfo := conn.Info() conn.Log().Infof("connection established, gateway: %v, bdn: %v protocol version %v, network %v, on local port %v", - connInfo.IsGateway(), connInfo.IsBDN(), conn.Protocol(), connInfo.NetworkNum, conn.Info().LocalPort) + connections.IsGateway(conn.GetConnectionType()), connections.IsBDN(conn.GetCapabilities()), conn.Protocol(), + conn.GetNetworkNum(), conn.GetLocalPort()) bn.ConnectionsLock.Lock() - defer bn.ConnectionsLock.Unlock() bn.Connections = append(bn.Connections, conn) + bn.ConnectionsLock.Unlock() return nil } @@ -78,7 +78,7 @@ func (bn *Bx) OnConnClosed(conn connections.Conn) error { defer bn.ConnectionsLock.Unlock() for idx, connection := range bn.Connections { if connection.ID() == conn.ID() { - if conn.Info().ConnectionType&utils.RelayTransaction != 0 { + if conn.GetConnectionType()&utils.RelayTransaction != 0 { bn.TxStore.Clear() } bn.Connections = append(bn.Connections[:idx], bn.Connections[idx+1:]...) @@ -129,7 +129,7 @@ func (bn *Bx) HandleMsg(msg bxmessage.Message, source connections.Conn) error { syncTxs := &bxmessage.SyncTxsMessage{} syncTxs.SetNetworkNum(syncReq.GetNetworkNum()) priority := bxmessage.OnPongPriority - if source.Info().ConnectionType&utils.RelayProxy != 0 || source.Protocol() >= bxmessage.MinFastSyncProtocol { + if source.GetConnectionType()&utils.RelayProxy != 0 || source.Protocol() >= bxmessage.MinFastSyncProtocol { priority = bxmessage.NormalPriority } @@ -191,13 +191,13 @@ func (bn *Bx) HandleMsg(msg bxmessage.Message, source connections.Conn) error { // DisconnectConn - disconnect a specific connection func (bn *Bx) DisconnectConn(id types.NodeID) { bn.ConnectionsLock.Lock() - defer bn.ConnectionsLock.Unlock() for _, conn := range bn.Connections { - if id == conn.Info().NodeID { + if id == conn.GetNodeID() { // closing in a new go routine in order to avoid deadlock while Close method acquiring ConnectionsLock go conn.Close("disconnect requested by bxapi") } } + bn.ConnectionsLock.Unlock() } // Peers provides a list of current peers for the requested type @@ -214,31 +214,32 @@ func (bn *Bx) Peers(_ context.Context, req *pbbase.PeersRequest) (*pbbase.PeersR defer bn.ConnectionsLock.RUnlock() for _, conn := range bn.Connections { - connInfo := conn.Info() - if connInfo.ConnectionType&nodeType == 0 { + connectionType := conn.GetConnectionType() + if connectionType&nodeType == 0 { continue } - connType := connInfo.ConnectionType.String() + connType := connectionType.String() var trusted string + accountID := conn.GetAccountID() if bn.AccountsFetcher != nil { - if accountModel := bn.AccountsFetcher.GetAccount(connInfo.AccountID); accountModel != nil { + if accountModel := bn.AccountsFetcher.GetAccount(accountID); accountModel != nil { trusted = strconv.FormatBool(accountModel.IsTrusted()) } } peer := &pbbase.Peer{ - Ip: connInfo.PeerIP, - NodeId: string(connInfo.NodeID), + Ip: conn.GetPeerIP(), + NodeId: string(conn.GetNodeID()), Type: connType, - State: connInfo.ConnectionState, - Network: uint32(connInfo.NetworkNum), - Initiator: connInfo.FromMe, - AccountId: string(connInfo.AccountID), - Port: conn.Info().LocalPort, + State: conn.GetConnectionState(), + Network: uint32(conn.GetNetworkNum()), + Initiator: conn.IsInitiator(), + AccountId: string(accountID), + Port: conn.GetLocalPort(), Disabled: conn.IsDisabled(), - Capability: uint32(conn.Info().Capabilities), + Capability: uint32(conn.GetCapabilities()), Trusted: trusted, } if bxConn, ok := conn.(*handler.BxConn); ok { @@ -258,10 +259,10 @@ func (bn *Bx) PingLoop() { for { select { case <-pingTicker.Alert(): - bn.ConnectionsLock.RLock() count := 0 + bn.ConnectionsLock.RLock() for _, conn := range bn.Connections { - if conn.Info().ConnectionType&to != 0 { + if conn.GetConnectionType()&to != 0 { err := conn.Send(ping) if err != nil { conn.Log().Errorf("error sending ping message: %v", err) diff --git a/nodes/gateway.go b/nodes/gateway.go index cd79818..4cf4cb5 100644 --- a/nodes/gateway.go +++ b/nodes/gateway.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" "github.com/sirupsen/logrus" "github.com/sourcegraph/jsonrpc2" "github.com/zhouzhuojie/conditions" @@ -57,6 +57,7 @@ import ( "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" "github.com/bloXroute-Labs/gateway/v2/utils/orderedmap" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" "github.com/bloXroute-Labs/gateway/v2/version" ) @@ -105,12 +106,18 @@ type gateway struct { gatewayPeers string gatewayPublicKey string - staticEnodesCount int - startupArgs string - validatorStatusMap *sync.Map // validator addr -> online/offline - validatorListMap *sync.Map // block height -> list of validators - nextValidatorMap *orderedmap.OrderedMap // next accessible validator - validatorListReady bool + staticEnodesCount int + startupArgs string + validatorStatusMap *syncmap.SyncMap[string, bool] // validator addr -> online/offline + validatorListMap *syncmap.SyncMap[uint64, []string] // block height -> list of validators + nextValidatorMap *orderedmap.OrderedMap // next accessible validator + validatorListReady bool + validatorInfoUpdateLock sync.Mutex + latestValidatorInfo []*types.FutureValidatorInfo + latestValidatorInfoHeight int64 + transactionSlotStartDuration int + transactionSlotEndDuration int + nextBlockTime time.Time polygonValidatorInfoManager polygon.ValidatorInfoManager } @@ -136,43 +143,46 @@ func GeneratePeers(peersInfo []network.PeerInfo) string { // NewGateway returns a new gateway node to send messages from a blockchain node to the relay network func NewGateway(parent context.Context, bxConfig *config.Bx, bridge blockchain.Bridge, wsManager blockchain.WSManager, blockchainPeers []types.NodeEndpoint, peersInfo []network.PeerInfo, gatewayPublicKeyStr string, sdn connections.SDNHTTP, - sslCerts *utils.SSLCerts, staticEnodesCount int, polygonHeimdallEndpoint string) (Node, error) { + sslCerts *utils.SSLCerts, staticEnodesCount int, polygonHeimdallEndpoint string, transactionSlotStartDuration int, transactionSlotEndDuration int) (Node, error) { ctx, cancel := context.WithCancel(parent) clock := utils.RealClock{} g := &gateway{ - Bx: NewBx(bxConfig, "datadir", nil), - bridge: bridge, - isBDN: bxConfig.GatewayMode.IsBDN(), - wsManager: wsManager, - context: ctx, - cancel: cancel, - blockchainPeers: blockchainPeers, - pendingTxs: services.NewHashHistory("pendingTxs", 15*time.Minute), - possiblePendingTxs: services.NewHashHistory("possiblePendingTxs", 15*time.Minute), - bdnBlocks: services.NewHashHistory("bdnBlocks", 15*time.Minute), - newBlocks: services.NewHashHistory("newBlocks", 15*time.Minute), - seenMEVMinerBundles: services.NewHashHistory("mevMinerBundle", 30*time.Minute), - seenMEVSearchers: services.NewHashHistory("mevSearcher", 30*time.Minute), - seenBlockConfirmation: services.NewHashHistory("blockConfirmation", 30*time.Minute), - clock: clock, - timeStarted: clock.Now(), - gatewayPeers: GeneratePeers(peersInfo), - gatewayPublicKey: gatewayPublicKeyStr, - staticEnodesCount: staticEnodesCount, - sdn: sdn, - sslCerts: sslCerts, + Bx: NewBx(bxConfig, "datadir", nil), + bridge: bridge, + isBDN: bxConfig.GatewayMode.IsBDN(), + wsManager: wsManager, + context: ctx, + cancel: cancel, + blockchainPeers: blockchainPeers, + pendingTxs: services.NewHashHistory("pendingTxs", 15*time.Minute), + possiblePendingTxs: services.NewHashHistory("possiblePendingTxs", 15*time.Minute), + bdnBlocks: services.NewHashHistory("bdnBlocks", 15*time.Minute), + newBlocks: services.NewHashHistory("newBlocks", 15*time.Minute), + seenMEVMinerBundles: services.NewHashHistory("mevMinerBundle", 30*time.Minute), + seenMEVSearchers: services.NewHashHistory("mevSearcher", 30*time.Minute), + seenBlockConfirmation: services.NewHashHistory("blockConfirmation", 30*time.Minute), + clock: clock, + timeStarted: clock.Now(), + gatewayPeers: GeneratePeers(peersInfo), + gatewayPublicKey: gatewayPublicKeyStr, + staticEnodesCount: staticEnodesCount, + transactionSlotStartDuration: transactionSlotStartDuration, + transactionSlotEndDuration: transactionSlotEndDuration, + + sdn: sdn, + sslCerts: sslCerts, } g.polygonValidatorInfoManager = bor.NewSprintManager(parent, &g.wsManager, bor.NewHeimdallSpanner(parent, polygonHeimdallEndpoint)) if bxConfig.BlockchainNetwork == bxgateway.BSCMainnet || bxConfig.BlockchainNetwork == bxgateway.PolygonMainnet { - g.validatorStatusMap = new(sync.Map) + g.validatorStatusMap = syncmap.NewStringMapOf[bool]() g.nextValidatorMap = orderedmap.New() } if bxConfig.BlockchainNetwork == bxgateway.BSCMainnet { - g.validatorListMap = &sync.Map{} + g.validatorListMap = syncmap.NewIntegerMapOf[uint64, []string]() g.validatorListReady = false g.bscTxClient = &http.Client{ Transport: &http.Transport{ @@ -374,11 +384,12 @@ func (g *gateway) Run() error { g.feedManager = servers.NewFeedManager(g.context, g, g.feedChan, services.NewNoOpSubscriptionServices(), networkNum, blockchainNetwork.DefaultAttributes.NetworkID, g.sdn.NodeModel().NodeID, g.wsManager, accountModel, g.sdn.FetchCustomerAccountModel, - sslCert.PrivateCertFile(), sslCert.PrivateKeyFile(), *g.BxConfig, g.stats, g.nextValidatorMap) + sslCert.PrivateCertFile(), sslCert.PrivateKeyFile(), *g.BxConfig, g.stats, g.nextValidatorMap, g.validatorStatusMap, + ) if g.BxConfig.WebsocketEnabled || g.BxConfig.WebsocketTLSEnabled { clientHandler := servers.NewClientHandler(g.feedManager, nil, servers.NewHTTPServer(g.feedManager, g.BxConfig.HTTPPort), log.WithFields(log.Fields{ "component": "gatewayClientHandler", - })) + }), g.sdn.GetQuotaUsage) go clientHandler.ManageWSServer(g.BxConfig.ManageWSServer) go clientHandler.ManageHTTPServer(g.context) g.feedManager.Start() @@ -448,13 +459,14 @@ func (g *gateway) connectRelay(instruction connections.RelayInstruction, sslCert } func (g *gateway) broadcast(msg bxmessage.Message, source connections.Conn, to utils.NodeType) types.BroadcastResults { - g.ConnectionsLock.RLock() - defer g.ConnectionsLock.RUnlock() results := types.BroadcastResults{} + g.ConnectionsLock.RLock() for _, conn := range g.Connections { + connectionType := conn.GetConnectionType() + // if connection type is not in target - skip - if conn.Info().ConnectionType&to == 0 { + if connectionType&to == 0 { continue } @@ -471,12 +483,14 @@ func (g *gateway) broadcast(msg bxmessage.Message, source connections.Conn, to u continue } - if conn.Info().IsGateway() { + if connections.IsGateway(connectionType) { results.SentGatewayPeers++ } results.SentPeers++ } + g.ConnectionsLock.RUnlock() + return results } @@ -604,7 +618,7 @@ func (g *gateway) generateBSCValidator(blockHeight uint64) []*types.FutureValida return vi } } - previousEpochValidatorList := prevEpochValidatorList.([]string) + previousEpochValidatorList := prevEpochValidatorList var currentEpochValidatorList []string currEpochValidatorList, exist := g.validatorListMap.Load(currentEpochBlockHeight) @@ -616,7 +630,7 @@ func (g *gateway) generateBSCValidator(blockHeight uint64) []*types.FutureValida return vi } } - currentEpochValidatorList = currEpochValidatorList.([]string) + currentEpochValidatorList = currEpochValidatorList if !g.validatorListReady { g.validatorListReady = true log.Info("The gateway has all the information to support next_validator transactions") @@ -638,14 +652,52 @@ func (g *gateway) generateBSCValidator(blockHeight uint64) []*types.FutureValida g.nextValidatorMap.Set(targetingBlockHeight, strings.ToLower(vi[i-1].WalletID)) // nextValidatorMap is simulating a queue with height as expiration key. Regardless of the accessible status, next walletID will be appended to the queue accessible, exist := g.validatorStatusMap.Load(strings.ToLower(vi[i-1].WalletID)) if exist { - vi[i-1].Accessible = accessible.(bool) + vi[i-1].Accessible = accessible } } g.cleanUpNextValidatorMap(blockHeight) + go g.reevaluatePendingBSCNextValidatorTx() return vi } +func (g *gateway) reevaluatePendingBSCNextValidatorTx() { + now := time.Now() + g.feedManager.LockPendingNextValidatorTxs() + defer g.feedManager.UnlockPendingNextValidatorTxs() + + pendingNextValidatorTxsMap := g.feedManager.GetPendingNextValidatorTxs() + for txHash, txInfo := range pendingNextValidatorTxsMap { + fallback := time.Duration(uint64(txInfo.Fallback) * bxgateway.MillisecondsToNanosecondsMultiplier) + // if fallback time is not reached or if fallback is 0 meaning we don't have fallback deadline + if fallbackTimeNotReached := now.Before(txInfo.TimeOfRequest.Add(fallback)); fallbackTimeNotReached || txInfo.Fallback == 0 { + timeSinceRequest := now.Sub(txInfo.TimeOfRequest) + adjustedFallback := fallback - timeSinceRequest + + firstValidatorInaccessible, err := servers.ProcessNextValidatorTx(txInfo.Tx, uint16(adjustedFallback.Milliseconds()), g.nextValidatorMap, g.validatorStatusMap, txInfo.Tx.GetNetworkNum(), txInfo.Source, pendingNextValidatorTxsMap) + delete(pendingNextValidatorTxsMap, txHash) + + if err != nil { + log.Errorf("failed to reevaluate next validator tx %v: %v", txHash, err) + } + + // send with adjusted fallback regardless of if validator is accessible + if firstValidatorInaccessible { + txInfo.Tx.RemoveFlags(types.TFNextValidator) + txInfo.Tx.SetFallback(0) + } + err = g.HandleMsg(txInfo.Tx, txInfo.Source, connections.RunForeground) + if err != nil { + log.Errorf("failed to process reevaluated next validator tx %v: %v", txHash, err) + } + continue + } else { + // should have already been sent by gateway + delete(pendingNextValidatorTxsMap, txHash) + } + } +} + func (g *gateway) generatePolygonValidator(bxBlock *types.BxBlock) []*types.FutureValidatorInfo { blockHeight := bxBlock.Number.Uint64() @@ -680,7 +732,7 @@ func (g *gateway) generatePolygonValidator(bxBlock *types.BxBlock) []*types.Futu accessible, exist := g.validatorStatusMap.Load(info.WalletID) if exist { - info.Accessible = accessible.(bool) + info.Accessible = accessible } } @@ -690,11 +742,21 @@ func (g *gateway) generatePolygonValidator(bxBlock *types.BxBlock) []*types.Futu } func (g *gateway) generateFutureValidatorInfo(block *types.BxBlock) []*types.FutureValidatorInfo { + g.validatorInfoUpdateLock.Lock() + defer g.validatorInfoUpdateLock.Unlock() + + if block.Number.Int64() <= g.latestValidatorInfoHeight { + return g.latestValidatorInfo + } + g.latestValidatorInfoHeight = block.Number.Int64() + switch g.sdn.NetworkNum() { case bxgateway.PolygonMainnetNum: - return g.generatePolygonValidator(block) + g.latestValidatorInfo = g.generatePolygonValidator(block) + return g.latestValidatorInfo case bxgateway.BSCMainnetNum: - return g.generateBSCValidator(block.Number.Uint64()) + g.latestValidatorInfo = g.generateBSCValidator(block.Number.Uint64()) + return g.latestValidatorInfo default: return nil } @@ -1031,12 +1093,39 @@ func (g *gateway) HandleMsg(msg bxmessage.Message, source connections.Conn, back log.Exit(0) case *bxmessage.Hello: source.Log().Tracef("received hello msg") + if connections.IsRelay(source.GetConnectionType()) && g.sdn.AccountModel().Miner { + // check if it has blockchain connection + go func() { + if g.BxConfig.ForwardTransactionEndpoint != "" { + g.sdn.SendNodeEvent( + sdnmessage.NewAddAccessibleGatewayEvent(g.sdn.NodeID(), fmt.Sprintf( + "gateway %v (%v) established connection with relay %v", + g.sdn.NodeModel().ExternalIP, g.accountID, source.GetPeerIP()), g.clock.Now().String()), + g.sdn.NodeID()) + } else if g.gatewayHasBlockchainConnection() { + g.sdn.SendNodeEvent( + sdnmessage.NewAddAccessibleGatewayEvent(g.sdn.NodeID(), fmt.Sprintf( + "gateway %v (%v) established connection with relay %v:%v and connected to at least one node", + g.sdn.NodeModel().ExternalIP, g.accountID, source.GetPeerIP(), source.GetPeerPort(), + ), g.clock.Now().String()), + g.sdn.NodeID()) + } else { + g.sdn.SendNodeEvent( + sdnmessage.NewRemoveAccessibleGatewayEvent(g.sdn.NodeID(), fmt.Sprintf( + "gateway %v (%v) established connection with relay %v:%v but not connected to any node", + g.sdn.NodeModel().ExternalIP, g.accountID, source.GetPeerIP(), source.GetPeerPort(), + ), g.clock.Now().String()), + g.sdn.NodeID()) + } + }() + } + case *bxmessage.BlockConfirmation: hashString := typedMsg.Hash().String() if g.seenBlockConfirmation.SetIfAbsent(hashString, 30*time.Minute) { if !g.BxConfig.SendConfirmation { log.Debug("gateway is not sending block confirm message to relay") - } else if source.Info().ConnectionType == utils.Blockchain { + } else if source.GetConnectionType() == utils.Blockchain { log.Tracef("gateway broadcasting block confirmation of block %v to relays", hashString) g.broadcast(typedMsg, source, utils.Relay) } @@ -1049,8 +1138,29 @@ func (g *gateway) HandleMsg(msg bxmessage.Message, source connections.Conn, back return err } +func (g *gateway) gatewayHasBlockchainConnection() bool { + err := g.bridge.SendNodeConnectionCheckRequest() + if err == nil { + select { + case status := <-g.bridge.ReceiveNodeConnectionCheckResponse(): + log.Tracef("received status from %v:%v", status.IP, status.Port) + return true + case <-time.After(time.Second): + return false + } + + } else { + log.Errorf("failed to send blockchain status request when received hello msg from relay: %v", err) + } + + return false +} + func (g *gateway) processBroadcast(broadcastMsg *bxmessage.Broadcast, source connections.Conn) { startTime := time.Now() + // update the next block time + blockTime, _ := bxgateway.NetworkToBlockDuration[g.BxConfig.BlockchainNetwork] + g.nextBlockTime = startTime.Add(blockTime).Round(time.Second) bxBlock, missingShortIDs, err := g.blockProcessor.BxBlockFromBroadcast(broadcastMsg) if err != nil { switch err { @@ -1124,9 +1234,16 @@ func (g *gateway) processValidatorUpdate(msg *bxmessage.ValidatorUpdates, source source.Log().Debugf("ignore validator update message for %v network number from relay, gateway is %v", msg.GetNetworkNum(), g.sdn.NetworkNum()) return } - - g.validatorStatusMap = new(sync.Map) onlineList := msg.GetOnlineList() + + removeValidator := func(key string, value bool) bool { + if !utils.Exists(key, onlineList) { + g.validatorStatusMap.Delete(key) + } + return true + } + g.validatorStatusMap.Range(removeValidator) + for _, addr := range onlineList { g.validatorStatusMap.Store(strings.ToLower(addr), true) } @@ -1137,17 +1254,24 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) networkDuration := startTime.Sub(tx.Timestamp()).Microseconds() sentToBlockchainNode := false sentToBDN := false - sourceInfo := source.Info() - sourceEndpoint := types.NodeEndpoint{IP: sourceInfo.PeerIP, Port: int(sourceInfo.PeerPort), PublicKey: sourceInfo.PeerEnode} + connectionType := source.GetConnectionType() + nodeID := source.GetNodeID() + peerIP := source.GetPeerIP() + peerPort := source.GetPeerPort() + sourceEndpoint := types.NodeEndpoint{IP: peerIP, Port: int(peerPort), PublicKey: source.GetPeerEnode()} connEndpoint, ok := source.(connections.EndpointConn) + frontRunProtectionDelay := time.Duration(0) if ok { sourceEndpoint = connEndpoint.NodeEndpoint() } var broadcastRes types.BroadcastResults upscale_client.AddTransactionEvents(sourceEndpoint.ID, upscale_client.TX, 1) - // we add the transaction to TxStore with current time so we can measure time difference to node announcement/confirmation - txResult := g.TxStore.Add(tx.Hash(), tx.Content(), tx.ShortID(), tx.GetNetworkNum(), !sourceInfo.IsRelay(), tx.Flags(), g.clock.Now(), 0, tx.Sender()) + isRelay := connections.IsRelay(connectionType) + isGrpc := connections.IsGrpc(connectionType) + sender := tx.Sender() + // we add the transaction to TxStore with current time, so we can measure time difference to node announcement/confirmation + txResult := g.TxStore.Add(tx.Hash(), tx.Content(), tx.ShortID(), tx.GetNetworkNum(), !(isRelay || (isGrpc && sender != types.EmptySender)), tx.Flags(), g.clock.Now(), 0, sender) eventName := "TxProcessedByGatewayFromPeerIgnoreSeen" switch { @@ -1157,25 +1281,25 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) eventName = "TxReuseSenderNonce" log.Trace(txResult.DebugData) case txResult.AlreadySeen: - log.Tracef("received already Seen transaction %v from %v:%v (%v) and account id %v", tx.Hash(), sourceInfo.PeerIP, sourceInfo.PeerPort, sourceInfo.NodeID, sourceInfo.AccountID) + log.Tracef("received already Seen transaction %v from %v:%v (%v) and account id %v", tx.Hash(), peerIP, peerPort, nodeID, source.GetAccountID()) case txResult.NewContent || txResult.NewSID || txResult.Reprocess: eventName = "TxProcessedByGatewayFromPeer" if txResult.NewContent || txResult.Reprocess { if txResult.NewContent { upscale_client.TransactionAdded(tx.Hash().String(), sourceEndpoint.ID) } - validatorsOnlyTxFromCloudAPI := sourceInfo.ConnectionType == utils.CloudAPI && tx.Flags().IsValidatorsOnly() - nextValidatorTxFromCloudAPI := sourceInfo.ConnectionType == utils.CloudAPI && tx.Flags().IsNextValidator() + validatorsOnlyTxFromCloudAPI := connectionType == utils.CloudAPI && tx.Flags().IsValidatorsOnly() + nextValidatorTxFromCloudAPI := connectionType == utils.CloudAPI && tx.Flags().IsNextValidator() if txResult.NewContent && !tx.Flags().IsValidatorsOnly() && !tx.Flags().IsNextValidator() { newTxsNotification := types.CreateNewTransactionNotification(txResult.Transaction) g.notify(newTxsNotification) if !sourceEndpoint.IsDynamic() { - g.publishPendingTx(txResult.Transaction.Hash(), txResult.Transaction, sourceInfo.ConnectionType == utils.Blockchain) + g.publishPendingTx(txResult.Transaction.Hash(), txResult.Transaction, connectionType == utils.Blockchain) } } - if !sourceInfo.IsRelay() { - if sourceInfo.ConnectionType == utils.Blockchain { + if !isRelay { + if connectionType == utils.Blockchain { g.bdnStats.LogNewTxFromNode(sourceEndpoint) } @@ -1184,8 +1308,8 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) allowed bool behavior sdnmessage.BDNServiceBehaviorType ) - if sourceInfo.ConnectionType == utils.CloudAPI { - allowed, behavior = g.burstLimiter.AllowTransaction(sourceInfo.AccountID, paidTx) + if connectionType == utils.CloudAPI { + allowed, behavior = g.burstLimiter.AllowTransaction(source.GetAccountID(), paidTx) } else { allowed, behavior = g.burstLimiter.AllowTransaction(g.accountID, paidTx) } @@ -1231,10 +1355,10 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) } } - shouldSendTxFromNodeToOtherNodes := sourceInfo.ConnectionType == utils.Blockchain && len(g.blockchainPeers) > 1 + shouldSendTxFromNodeToOtherNodes := connectionType == utils.Blockchain && len(g.blockchainPeers) > 1 // send to node if all are true shouldSendTxFromBDNToNodes := len(g.blockchainPeers) > 0 && // Gateway is connected to nodes - sourceInfo.ConnectionType != utils.Blockchain && // Transaction is not from blockchain node (transaction is from Relay or RPC) + connectionType != utils.Blockchain && // Transaction is not from blockchain node (transaction is from Relay or RPC) ((!g.BxConfig.BlocksOnly && tx.Flags().ShouldDeliverToNode()) || // (Gateway didn't start with blocks only mode and DeliverToNode flag is on) OR (g.BxConfig.AllTransactions && !validatorsOnlyTxFromCloudAPI && !nextValidatorTxFromCloudAPI)) // OR (Gateway started with a flag to send all transactions and the tx is not sent from Cloud API for validators only) @@ -1242,21 +1366,52 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) txsToDeliverToNodes := blockchain.Transactions{ Transactions: []*types.BxTransaction{txResult.Transaction}, PeerEndpoint: sourceEndpoint, - ConnectionType: sourceInfo.ConnectionType, + ConnectionType: connectionType, } - err := g.bridge.SendTransactionsFromBDN(txsToDeliverToNodes) - if err == nil { - sentToBlockchainNode = true + + // Check for front run protection, if tx arrive too earlier, we send it with delay, if arrive too late, do not send to node + if tx.Flags().IsFrontRunningProtection() { + now := time.Now() + deadline := g.nextBlockTime.Add(-time.Millisecond * time.Duration(g.transactionSlotEndDuration)) + slotBegin := g.nextBlockTime.Add(-time.Millisecond * time.Duration(g.transactionSlotStartDuration)) + if now.Before(slotBegin) { + frontRunProtectionDelay = slotBegin.Sub(now) + sentToBlockchainNode = true + log.Debugf("received next validator tx %v before slot start %v, wait for %v to prevent front running", tx.String(), slotBegin.String(), frontRunProtectionDelay.String()) + } else if now.After(deadline) { + sentToBlockchainNode = false + log.Errorf("received next validator tx %v after slot deadline %v, will not send the tx", tx.String(), deadline.String()) + break + } + + time.AfterFunc(frontRunProtectionDelay, func() { + err := g.bridge.SendTransactionsFromBDN(txsToDeliverToNodes) + if err != nil { + log.Errorf("failed to send transaction %v from BDN to bridge - %v", txResult.Transaction.Hash(), err) + } + + if shouldSendTxFromNodeToOtherNodes { + g.bdnStats.LogTxSentToAllNodesExceptSourceNode(sourceEndpoint) + } + + log.Debugf("tx %v sent to blockchain, flag %v, with front run protection delay %v", tx.Hash().String(), tx.Flags(), frontRunProtectionDelay.String()) + }) } else { - log.Errorf("failed to send transaction %v from BDN to bridge - %v", txResult.Transaction.Hash(), err) - } + err := g.bridge.SendTransactionsFromBDN(txsToDeliverToNodes) + if err != nil { + log.Errorf("failed to send transaction %v from BDN to bridge - %v", txResult.Transaction.Hash(), err) + } - if shouldSendTxFromNodeToOtherNodes { - g.bdnStats.LogTxSentToAllNodesExceptSourceNode(sourceEndpoint) + sentToBlockchainNode = true + if shouldSendTxFromNodeToOtherNodes { + g.bdnStats.LogTxSentToAllNodesExceptSourceNode(sourceEndpoint) + } + + log.Debugf("tx %v sent to blockchain, flag %v", tx.Hash().String(), tx.Flags()) } } - if sourceInfo.NetworkNum == bxgateway.BSCMainnetNum && (tx.Flags().IsValidatorsOnly() || tx.Flags().IsNextValidator()) { + if source.GetNetworkNum() == bxgateway.BSCMainnetNum && (tx.Flags().IsValidatorsOnly() || tx.Flags().IsNextValidator()) { rawBytesString, err := getRawBytesStringFromTXMsg(tx) if err != nil { log.Errorf("failed to forward transaction %v to %v due to rpl decoding error %v", txResult.Transaction.Hash(), g.BxConfig.ForwardTransactionEndpoint, err) @@ -1265,7 +1420,7 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) } } - if sourceInfo.IsRelay() && !txResult.Reprocess { + if isRelay && !txResult.Reprocess { g.bdnStats.LogNewTxFromBDN() } @@ -1277,13 +1432,13 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) eventName = "TxReuseSenderNonceIgnoreSeen" log.Trace(txResult.DebugData) } - if sourceInfo.ConnectionType == utils.Blockchain { + if connectionType == utils.Blockchain { g.bdnStats.LogDuplicateTxFromNode(sourceEndpoint) } } statsStart := time.Now() - g.stats.AddTxsByShortIDsEvent(eventName, source, txResult.Transaction, tx.ShortID(), sourceInfo.NodeID, broadcastRes.RelevantPeers, broadcastRes.SentGatewayPeers, startTime, tx.GetPriority(), txResult.DebugData) + g.stats.AddTxsByShortIDsEvent(eventName, source, txResult.Transaction, tx.ShortID(), nodeID, broadcastRes.RelevantPeers, broadcastRes.SentGatewayPeers, startTime, tx.GetPriority(), txResult.DebugData) statsDuration := time.Since(statsStart) // usage of log.WithFields 7 times slower than usage of direct log.Tracef @@ -1291,11 +1446,11 @@ func (g *gateway) processTransaction(tx *bxmessage.Tx, source connections.Conn) "msgTx: from %v, hash %v, nonce %v, flags %v, new Tx %v, new content %v, new shortid %v, event %v,"+ " sentToBDN: %v, sentPeersNum %v, sentToBlockchainNode: %v, handling duration %v, sender %v,"+ " networkDuration %v statsDuration %v, nextValidator enabled %v,fallback duration %v,"+ - " next validator fallback %v", + " next validator fallback %v, front run protection delay %v", source, tx.Hash(), txResult.Nonce, tx.Flags(), txResult.NewTx, txResult.NewContent, txResult.NewSID, eventName, sentToBDN, broadcastRes.SentPeers, sentToBlockchainNode, time.Since(startTime), txResult.Transaction.Sender(), networkDuration, statsDuration, tx.Flags().IsNextValidator(), tx.Fallback(), - tx.Flags().IsNextValidatorRebroadcast(), + tx.Flags().IsNextValidatorRebroadcast(), frontRunProtectionDelay.String(), ) } @@ -1402,7 +1557,7 @@ func (g gateway) handleMEVBundleMessage(mevBundle bxmessage.MEVBundle, source co if g.seenMEVMinerBundles.SetIfAbsent(mevBundle.Hash().String(), time.Minute*30) { event = "broadcast" - if source.Info().IsRelay() { + if connections.IsRelay(source.GetConnectionType()) { if g.BxConfig.MEVMinerURI == "" { log.Warnf("received mevBundle message, but mev miner uri is empty. Message %v from %v in network %v", mevBundle.Hash(), mevBundle.SourceID(), mevBundle.GetNetworkNum()) return @@ -1462,7 +1617,7 @@ func (g gateway) handleMEVSearcherMessage(mevSearcher bxmessage.MEVSearcher, sou if g.seenMEVSearchers.SetIfAbsent(mevSearcher.Hash().String(), time.Minute*30) { event = "broadcast" - if source.Info().IsRelay() { + if connections.IsRelay(source.GetConnectionType()) { if g.BxConfig.MEVBuilderURI == "" { log.Warnf("received mevSearcher message, but mev-builder-uri is empty. Message %v from %v in network %v", mevSearcher.Hash(), mevSearcher.SourceID(), mevSearcher.GetNetworkNum()) return @@ -1553,14 +1708,13 @@ const bdn = "BDN" func (g *gateway) Status(context.Context, *pb.StatusRequest) (*pb.StatusResponse, error) { var bdnConn = func() map[string]*pb.BDNConnStatus { - g.ConnectionsLock.RLock() - defer g.ConnectionsLock.RUnlock() - var mp = make(map[string]*pb.BDNConnStatus) + + g.ConnectionsLock.RLock() for _, conn := range g.Connections { - connInfo := conn.Info() + connectionType := conn.GetConnectionType() - if connInfo.ConnectionType&utils.Relay == 0 { + if connectionType&utils.Relay == 0 { continue } @@ -1575,20 +1729,23 @@ func (g *gateway) Status(context.Context, *pb.StatusRequest) (*pb.StatusResponse } } + peerIP := conn.GetPeerIP() + if !conn.IsOpen() { - mp[connInfo.PeerIP] = &pb.BDNConnStatus{ + mp[peerIP] = &pb.BDNConnStatus{ Status: connectionStatusNotConnected, } continue } - mp[connInfo.PeerIP] = &pb.BDNConnStatus{ + mp[peerIP] = &pb.BDNConnStatus{ Status: connectionStatusConnected, - ConnectedAt: connInfo.ConnectedAt.Format(time.RFC3339), + ConnectedAt: conn.GetConnectedAt().Format(time.RFC3339), Latency: connectionLatency, } } + g.ConnectionsLock.RUnlock() if len(mp) == 0 { // set "BDN: NOT_CONNECTED" in case of missing connections to any relay @@ -1748,9 +1905,10 @@ func (g *gateway) BlxrTx(_ context.Context, req *pb.BlxrTxRequest) (*pb.BlxrTxRe } func (g *gateway) BlxrBatchTX(_ context.Context, req *pb.BlxrBatchTXRequest) (*pb.BlxrBatchTXReply, error) { + startTime := time.Now() var txHashes []*pb.TxIndex var txErrors []*pb.ErrorIndex - transactions := req.GetTransactions() + transactions := req.GetTransactionsAndSenders() if len(transactions) > 2 { txError := "blxr-batch-tx currently supports a maximum of two transactions" txErrors = append(txErrors, &pb.ErrorIndex{Idx: 0, Error: txError}) @@ -1758,30 +1916,41 @@ func (g *gateway) BlxrBatchTX(_ context.Context, req *pb.BlxrBatchTXRequest) (*p } grpc := connections.NewRPCConn(g.accountID, "", g.sdn.NetworkNum(), utils.GRPC) - for idx, transaction := range transactions { - blockchainNetwork, err := g.sdn.Networks().FindNetwork(g.sdn.NetworkNum()) - if err != nil { - txErrors = append(txErrors, &pb.ErrorIndex{Idx: int32(idx), Error: err.Error()}) - continue - } - validTx, err := servers.ValidateTxFromExternalSource(transaction, req.ValidatorsOnly, blockchainNetwork.DefaultAttributes.NetworkID, req.NextValidator, uint16(req.Fallback), g.nextValidatorMap, g.sdn.NetworkNum(), g.sdn.AccountModel().AccountID, req.NodeValidation, g.wsManager) - if err != nil { - txErrors = append(txErrors, &pb.ErrorIndex{Idx: int32(idx), Error: err.Error()}) - continue - } - g.HandleMsg(validTx, grpc, connections.RunForeground) - txContent, err := types.DecodeHex(transaction) - if err != nil { - txErrors = append(txErrors, &pb.ErrorIndex{Idx: int32(idx), Error: fmt.Sprintf("failed to decode transaction %v sent via GRPC blxrtx: %v", transaction, err)}) - continue + blockchainNetwork, err := g.sdn.Networks().FindNetwork(g.sdn.NetworkNum()) + if err != nil { + txErrors = append(txErrors, &pb.ErrorIndex{Idx: 0, Error: err.Error()}) + } else { + networkNum := g.sdn.NetworkNum() + accountID := g.sdn.AccountModel().AccountID + for idx, txAndSender := range transactions { + tx := txAndSender.GetTransaction() + txContent, err := types.DecodeHex(tx) + if err != nil { + txErrors = append(txErrors, &pb.ErrorIndex{Idx: int32(idx), Error: fmt.Sprintf("failed to decode transaction %v sent via GRPC blxrtx: %v", tx, err)}) + continue + } + g.feedManager.LockPendingNextValidatorTxs() + validTx, pendingReevaluation, err := servers.ValidateTxFromExternalSource( + tx, txContent, req.ValidatorsOnly, blockchainNetwork.DefaultAttributes.NetworkID, + req.NextValidator, uint16(req.Fallback), g.nextValidatorMap, g.validatorStatusMap, networkNum, + accountID, req.NodeValidation, g.wsManager, grpc, g.feedManager.GetPendingNextValidatorTxs(), false) + g.feedManager.UnlockPendingNextValidatorTxs() + if err != nil { + txErrors = append(txErrors, &pb.ErrorIndex{Idx: int32(idx), Error: err.Error()}) + continue + } + if pendingReevaluation { + continue + } + var sender types.Sender + copy(sender[:], txAndSender.GetSender()[:]) + validTx.SetSender(sender) + g.HandleMsg(validTx, grpc, connections.RunForeground) + txHashes = append(txHashes, &pb.TxIndex{Idx: int32(idx), TxHash: validTx.HashString(true)}) } - hashAsByteArr := crypto.Keccak256(txContent) - var txHash types.SHA256Hash - copy(txHash[:], hashAsByteArr) - - txHashes = append(txHashes, &pb.TxIndex{Idx: int32(idx), TxHash: txHash.String()}) } + log.Debugf("blxr-batch-tx network time %v, handle time: %v, txs success: %v, txs error: %v, validatorsOnly: %v, nextValidator %v fallback %v ms, nodeValidation %v", startTime.Sub(time.Unix(0, req.GetSendingTime())), time.Now().Sub(startTime), len(txHashes), len(txErrors), req.ValidatorsOnly, req.NextValidator, req.Fallback, req.NodeValidation) return &pb.BlxrBatchTXReply{TxHashes: txHashes, TxErrors: txErrors}, nil } @@ -1811,27 +1980,64 @@ func (g *gateway) handleBlockchainConnectionStatusUpdate() { if _, ok := g.bdnStats.NodeStats()[blockchainConnectionStatus.PeerEndpoint.IPPort()]; ok { g.bdnStats.NodeStats()[blockchainConnectionStatus.PeerEndpoint.IPPort()].IsConnected = blockchainConnectionStatus.IsConnected } + blockchainIP := blockchainConnectionStatus.PeerEndpoint.IP + blockchainPort := blockchainConnectionStatus.PeerEndpoint.Port if !blockchainConnectionStatus.IsDynamic { + // check if gateway is connected to a relay if blockchainConnectionStatus.IsConnected { g.sdn.SendNodeEvent( sdnmessage.NewBlockchainNodeConnEstablishedEvent( g.sdn.NodeID(), - blockchainConnectionStatus.PeerEndpoint.IP, - blockchainConnectionStatus.PeerEndpoint.Port, + blockchainIP, + blockchainPort, g.clock.Now().String(), blockchainConnectionStatus.PeerEndpoint.Version, blockchainConnectionStatus.PeerEndpoint.Name, ), - g.sdn.NodeID()) + g.sdn.NodeID(), + ) + // check if gateway has relay connection + if !g.sdn.AccountModel().Miner { + return + } + var isConnectedRelay bool + for _, conn := range g.Connections { + if connections.IsRelay(conn.GetConnectionType()) { + isConnectedRelay = true + } + } + if isConnectedRelay { + g.sdn.SendNodeEvent( + sdnmessage.NewAddAccessibleGatewayEvent(g.sdn.NodeID(), fmt.Sprintf( + "gateway %v (%v) from account %v established connection with node %v:%v", + g.sdn.NodeModel().ExternalIP, g.sdn.NodeID(), g.accountID, blockchainIP, int64(blockchainPort), + ), g.clock.Now().String()), + g.sdn.NodeID()) + } else { + g.sdn.SendNodeEvent( + sdnmessage.NewRemoveAccessibleGatewayEvent(g.sdn.NodeID(), fmt.Sprintf( + "gateway %v (%v) from account %v established connection with node %v:%v but not connected to any relay", + g.sdn.NodeModel().ExternalIP, g.sdn.NodeID(), g.accountID, blockchainIP, int64(blockchainPort), + ), g.clock.Now().String()), + g.sdn.NodeID()) + } } else { g.sdn.SendNodeEvent( - sdnmessage.NewBlockchainNodeConnError( - g.sdn.NodeID(), - blockchainConnectionStatus.PeerEndpoint.IP, - blockchainConnectionStatus.PeerEndpoint.Port, - g.clock.Now().String(), - ), - g.sdn.NodeID()) + sdnmessage.NewBlockchainNodeConnError(g.sdn.NodeID(), blockchainIP, blockchainPort, g.clock.Now().String()), + g.sdn.NodeID(), + ) + if g.sdn.AccountModel().Miner && !g.gatewayHasBlockchainConnection() { + go func() { + // check if gateway doesn't have any other node connections + g.sdn.SendNodeEvent( + sdnmessage.NewRemoveAccessibleGatewayEvent(g.sdn.NodeID(), fmt.Sprintf( + "gateway %v (%v) from account %v is not connected to any node", + g.sdn.NodeModel().ExternalIP, g.sdn.NodeID(), g.accountID, + ), g.clock.Now().String()), + g.sdn.NodeID()) + + }() + } } } } diff --git a/nodes/gateway_test.go b/nodes/gateway_test.go index 16a2ae5..ed8dc1a 100644 --- a/nodes/gateway_test.go +++ b/nodes/gateway_test.go @@ -88,7 +88,7 @@ func setup(t *testing.T, numPeers int) (blockchain.Bridge, *gateway) { bridge := blockchain.NewBxBridge(eth.Converter{}, true) blockchainPeers, blockchainPeersInfo := ethtest.GenerateBlockchainPeersInfo(numPeers) - node, _ := NewGateway(context.Background(), bxConfig, bridge, eth.NewEthWSManager(blockchainPeersInfo, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), blockchainPeers, blockchainPeersInfo, "", sdn, nil, 0, "") + node, _ := NewGateway(context.Background(), bxConfig, bridge, eth.NewEthWSManager(blockchainPeersInfo, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), blockchainPeers, blockchainPeersInfo, "", sdn, nil, 0, "", 0, 0) g := node.(*gateway) g.setupTxStore() @@ -97,7 +97,7 @@ func setup(t *testing.T, numPeers int) (blockchain.Bridge, *gateway) { g.feedManager = servers.NewFeedManager(g.context, g, g.feedChan, services.NewNoOpSubscriptionServices(), networkNum, types.NetworkID(chainID), g.sdn.NodeModel().NodeID, g.wsManager, g.sdn.AccountModel(), nil, - "", "", *g.BxConfig, g.stats, nil) + "", "", *g.BxConfig, g.stats, nil, nil) return bridge, g } @@ -539,7 +539,7 @@ func TestGateway_HandleTransactionFromRelay(t *testing.T) { bdnTxs := <-bridge.ReceiveBDNTransactions() assert.Equal(t, 1, len(bdnTxs.Transactions)) - assert.Equal(t, types.NodeEndpoint{IP: relayConn1.Info().PeerIP, Port: int(relayConn1.Info().PeerPort)}, bdnTxs.PeerEndpoint) + assert.Equal(t, types.NodeEndpoint{IP: relayConn1.GetPeerIP(), Port: int(relayConn1.GetPeerPort())}, bdnTxs.PeerEndpoint) bdnTx := bdnTxs.Transactions[0] assert.Equal(t, deliveredEthTx.Hash().Bytes(), bdnTx.Hash().Bytes()) @@ -579,7 +579,7 @@ func TestGateway_ReprocessTransactionFromRelay(t *testing.T) { bdnTxs := <-bridge.ReceiveBDNTransactions() assert.Equal(t, 1, len(bdnTxs.Transactions)) - assert.Equal(t, types.NodeEndpoint{IP: relayConn1.Info().PeerIP, Port: int(relayConn1.Info().PeerPort)}, bdnTxs.PeerEndpoint) + assert.Equal(t, types.NodeEndpoint{IP: relayConn1.GetPeerIP(), Port: int(relayConn1.GetPeerPort())}, bdnTxs.PeerEndpoint) bdnTx := bdnTxs.Transactions[0] assert.Equal(t, ethTx.Hash().Bytes(), bdnTx.Hash().Bytes()) diff --git a/nodes/gatewaygrpcserver_test.go b/nodes/gatewaygrpcserver_test.go index 8a6f92e..ab794c8 100644 --- a/nodes/gatewaygrpcserver_test.go +++ b/nodes/gatewaygrpcserver_test.go @@ -100,5 +100,5 @@ func TestGatewayGRPCServerPeers(t *testing.T) { assert.Equal(t, 1, len(peers.GetPeers())) peer := peers.GetPeers()[0] - assert.Equal(t, conn.Info().PeerIP, peer.Ip) + assert.Equal(t, conn.GetPeerIP(), peer.Ip) } diff --git a/protobuf/Dockerfile b/protobuf/Dockerfile index 4925318..5694e11 100644 --- a/protobuf/Dockerfile +++ b/protobuf/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18.9 +FROM golang:1.19 ENV PROTOC_VERSION=3.19.3 RUN apt-get update && apt-get install unzip diff --git a/protobuf/Makefile b/protobuf/Makefile index ef7a070..f4fee61 100644 --- a/protobuf/Makefile +++ b/protobuf/Makefile @@ -4,7 +4,7 @@ LOCAL_IMAGE_NAME=bx-proto-gen .PHONY: genproto .SILENT: genproto genproto: - docker run -v $(CURDIR):/go/protobuf $(IMAGE_NAME) \ + docker run -v $(CURDIR):/go/protobuf --platform linux/amd64 $(IMAGE_NAME) \ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative gateway.proto GENERATE_CMD=docker run -v $(CURDIR):/go/protobuf $(LOCAL_IMAGE_NAME) \ diff --git a/protobuf/gateway.pb.go b/protobuf/gateway.pb.go index 7e4cd83..8e7a188 100644 --- a/protobuf/gateway.pb.go +++ b/protobuf/gateway.pb.go @@ -1608,23 +1608,79 @@ func (x *TxStoreReply) GetNetworkData() []*TxStoreNetworkData { return nil } +type TxAndSender struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Transaction string `protobuf:"bytes,1,opt,name=transaction,proto3" json:"transaction,omitempty"` + Sender []byte `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` +} + +func (x *TxAndSender) Reset() { + *x = TxAndSender{} + if protoimpl.UnsafeEnabled { + mi := &file_gateway_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TxAndSender) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TxAndSender) ProtoMessage() {} + +func (x *TxAndSender) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TxAndSender.ProtoReflect.Descriptor instead. +func (*TxAndSender) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{25} +} + +func (x *TxAndSender) GetTransaction() string { + if x != nil { + return x.Transaction + } + return "" +} + +func (x *TxAndSender) GetSender() []byte { + if x != nil { + return x.Sender + } + return nil +} + type BlxrBatchTXRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Transactions []string `protobuf:"bytes,1,rep,name=transactions,proto3" json:"transactions,omitempty"` - NonceMonitoring bool `protobuf:"varint,2,opt,name=nonce_monitoring,json=nonceMonitoring,proto3" json:"nonce_monitoring,omitempty"` - NextValidator bool `protobuf:"varint,3,opt,name=next_validator,json=nextValidator,proto3" json:"next_validator,omitempty"` - Fallback int32 `protobuf:"varint,4,opt,name=fallback,proto3" json:"fallback,omitempty"` - ValidatorsOnly bool `protobuf:"varint,5,opt,name=validators_only,json=validatorsOnly,proto3" json:"validators_only,omitempty"` - NodeValidation bool `protobuf:"varint,6,opt,name=node_validation,json=nodeValidation,proto3" json:"node_validation,omitempty"` + TransactionsAndSenders []*TxAndSender `protobuf:"bytes,1,rep,name=transactions_and_senders,json=transactionsAndSenders,proto3" json:"transactions_and_senders,omitempty"` + NonceMonitoring bool `protobuf:"varint,2,opt,name=nonce_monitoring,json=nonceMonitoring,proto3" json:"nonce_monitoring,omitempty"` + NextValidator bool `protobuf:"varint,3,opt,name=next_validator,json=nextValidator,proto3" json:"next_validator,omitempty"` + Fallback int32 `protobuf:"varint,4,opt,name=fallback,proto3" json:"fallback,omitempty"` + ValidatorsOnly bool `protobuf:"varint,5,opt,name=validators_only,json=validatorsOnly,proto3" json:"validators_only,omitempty"` + NodeValidation bool `protobuf:"varint,6,opt,name=node_validation,json=nodeValidation,proto3" json:"node_validation,omitempty"` + SendingTime int64 `protobuf:"varint,7,opt,name=sending_time,json=sendingTime,proto3" json:"sending_time,omitempty"` } func (x *BlxrBatchTXRequest) Reset() { *x = BlxrBatchTXRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[25] + mi := &file_gateway_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1637,7 +1693,7 @@ func (x *BlxrBatchTXRequest) String() string { func (*BlxrBatchTXRequest) ProtoMessage() {} func (x *BlxrBatchTXRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[25] + mi := &file_gateway_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1650,12 +1706,12 @@ func (x *BlxrBatchTXRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BlxrBatchTXRequest.ProtoReflect.Descriptor instead. func (*BlxrBatchTXRequest) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{25} + return file_gateway_proto_rawDescGZIP(), []int{26} } -func (x *BlxrBatchTXRequest) GetTransactions() []string { +func (x *BlxrBatchTXRequest) GetTransactionsAndSenders() []*TxAndSender { if x != nil { - return x.Transactions + return x.TransactionsAndSenders } return nil } @@ -1695,6 +1751,13 @@ func (x *BlxrBatchTXRequest) GetNodeValidation() bool { return false } +func (x *BlxrBatchTXRequest) GetSendingTime() int64 { + if x != nil { + return x.SendingTime + } + return 0 +} + type BlxrTxRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1708,7 +1771,7 @@ type BlxrTxRequest struct { func (x *BlxrTxRequest) Reset() { *x = BlxrTxRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[26] + mi := &file_gateway_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1721,7 +1784,7 @@ func (x *BlxrTxRequest) String() string { func (*BlxrTxRequest) ProtoMessage() {} func (x *BlxrTxRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[26] + mi := &file_gateway_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1734,7 +1797,7 @@ func (x *BlxrTxRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BlxrTxRequest.ProtoReflect.Descriptor instead. func (*BlxrTxRequest) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{26} + return file_gateway_proto_rawDescGZIP(), []int{27} } func (x *BlxrTxRequest) GetTransaction() string { @@ -1769,7 +1832,7 @@ type BlxrTxReply struct { func (x *BlxrTxReply) Reset() { *x = BlxrTxReply{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[27] + mi := &file_gateway_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1782,7 +1845,7 @@ func (x *BlxrTxReply) String() string { func (*BlxrTxReply) ProtoMessage() {} func (x *BlxrTxReply) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[27] + mi := &file_gateway_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1795,7 +1858,7 @@ func (x *BlxrTxReply) ProtoReflect() protoreflect.Message { // Deprecated: Use BlxrTxReply.ProtoReflect.Descriptor instead. func (*BlxrTxReply) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{27} + return file_gateway_proto_rawDescGZIP(), []int{28} } func (x *BlxrTxReply) GetTxHash() string { @@ -1817,7 +1880,7 @@ type TxIndex struct { func (x *TxIndex) Reset() { *x = TxIndex{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[28] + mi := &file_gateway_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1830,7 +1893,7 @@ func (x *TxIndex) String() string { func (*TxIndex) ProtoMessage() {} func (x *TxIndex) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[28] + mi := &file_gateway_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1843,7 +1906,7 @@ func (x *TxIndex) ProtoReflect() protoreflect.Message { // Deprecated: Use TxIndex.ProtoReflect.Descriptor instead. func (*TxIndex) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{28} + return file_gateway_proto_rawDescGZIP(), []int{29} } func (x *TxIndex) GetIdx() int32 { @@ -1872,7 +1935,7 @@ type ErrorIndex struct { func (x *ErrorIndex) Reset() { *x = ErrorIndex{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[29] + mi := &file_gateway_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1885,7 +1948,7 @@ func (x *ErrorIndex) String() string { func (*ErrorIndex) ProtoMessage() {} func (x *ErrorIndex) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[29] + mi := &file_gateway_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1898,7 +1961,7 @@ func (x *ErrorIndex) ProtoReflect() protoreflect.Message { // Deprecated: Use ErrorIndex.ProtoReflect.Descriptor instead. func (*ErrorIndex) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{29} + return file_gateway_proto_rawDescGZIP(), []int{30} } func (x *ErrorIndex) GetIdx() int32 { @@ -1927,7 +1990,7 @@ type BlxrBatchTXReply struct { func (x *BlxrBatchTXReply) Reset() { *x = BlxrBatchTXReply{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[30] + mi := &file_gateway_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1940,7 +2003,7 @@ func (x *BlxrBatchTXReply) String() string { func (*BlxrBatchTXReply) ProtoMessage() {} func (x *BlxrBatchTXReply) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[30] + mi := &file_gateway_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1953,7 +2016,7 @@ func (x *BlxrBatchTXReply) ProtoReflect() protoreflect.Message { // Deprecated: Use BlxrBatchTXReply.ProtoReflect.Descriptor instead. func (*BlxrBatchTXReply) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{30} + return file_gateway_proto_rawDescGZIP(), []int{31} } func (x *BlxrBatchTXReply) GetTxHashes() []*TxIndex { @@ -1979,7 +2042,7 @@ type StatusRequest struct { func (x *StatusRequest) Reset() { *x = StatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[31] + mi := &file_gateway_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1992,7 +2055,7 @@ func (x *StatusRequest) String() string { func (*StatusRequest) ProtoMessage() {} func (x *StatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[31] + mi := &file_gateway_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2005,7 +2068,7 @@ func (x *StatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. func (*StatusRequest) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{31} + return file_gateway_proto_rawDescGZIP(), []int{32} } type AccountInfo struct { @@ -2020,7 +2083,7 @@ type AccountInfo struct { func (x *AccountInfo) Reset() { *x = AccountInfo{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[32] + mi := &file_gateway_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2033,7 +2096,7 @@ func (x *AccountInfo) String() string { func (*AccountInfo) ProtoMessage() {} func (x *AccountInfo) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[32] + mi := &file_gateway_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2046,7 +2109,7 @@ func (x *AccountInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AccountInfo.ProtoReflect.Descriptor instead. func (*AccountInfo) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{32} + return file_gateway_proto_rawDescGZIP(), []int{33} } func (x *AccountInfo) GetAccountId() string { @@ -2083,7 +2146,7 @@ type NodePerformance struct { func (x *NodePerformance) Reset() { *x = NodePerformance{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[33] + mi := &file_gateway_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2096,7 +2159,7 @@ func (x *NodePerformance) String() string { func (*NodePerformance) ProtoMessage() {} func (x *NodePerformance) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[33] + mi := &file_gateway_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2109,7 +2172,7 @@ func (x *NodePerformance) ProtoReflect() protoreflect.Message { // Deprecated: Use NodePerformance.ProtoReflect.Descriptor instead. func (*NodePerformance) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{33} + return file_gateway_proto_rawDescGZIP(), []int{34} } func (x *NodePerformance) GetSince() string { @@ -2195,7 +2258,7 @@ type WsConnStatus struct { func (x *WsConnStatus) Reset() { *x = WsConnStatus{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[34] + mi := &file_gateway_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2208,7 +2271,7 @@ func (x *WsConnStatus) String() string { func (*WsConnStatus) ProtoMessage() {} func (x *WsConnStatus) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[34] + mi := &file_gateway_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2221,7 +2284,7 @@ func (x *WsConnStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use WsConnStatus.ProtoReflect.Descriptor instead. func (*WsConnStatus) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{34} + return file_gateway_proto_rawDescGZIP(), []int{35} } func (x *WsConnStatus) GetAddr() string { @@ -2265,7 +2328,7 @@ type NodeConnStatus struct { func (x *NodeConnStatus) Reset() { *x = NodeConnStatus{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[35] + mi := &file_gateway_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2278,7 +2341,7 @@ func (x *NodeConnStatus) String() string { func (*NodeConnStatus) ProtoMessage() {} func (x *NodeConnStatus) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[35] + mi := &file_gateway_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2291,7 +2354,7 @@ func (x *NodeConnStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeConnStatus.ProtoReflect.Descriptor instead. func (*NodeConnStatus) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{35} + return file_gateway_proto_rawDescGZIP(), []int{36} } func (x *NodeConnStatus) GetConnStatus() string { @@ -2370,7 +2433,7 @@ type BDNConnStatus struct { func (x *BDNConnStatus) Reset() { *x = BDNConnStatus{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[36] + mi := &file_gateway_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2383,7 +2446,7 @@ func (x *BDNConnStatus) String() string { func (*BDNConnStatus) ProtoMessage() {} func (x *BDNConnStatus) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[36] + mi := &file_gateway_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2396,7 +2459,7 @@ func (x *BDNConnStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use BDNConnStatus.ProtoReflect.Descriptor instead. func (*BDNConnStatus) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{36} + return file_gateway_proto_rawDescGZIP(), []int{37} } func (x *BDNConnStatus) GetStatus() string { @@ -2434,7 +2497,7 @@ type ConnectionLatency struct { func (x *ConnectionLatency) Reset() { *x = ConnectionLatency{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[37] + mi := &file_gateway_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2447,7 +2510,7 @@ func (x *ConnectionLatency) String() string { func (*ConnectionLatency) ProtoMessage() {} func (x *ConnectionLatency) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[37] + mi := &file_gateway_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2460,7 +2523,7 @@ func (x *ConnectionLatency) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectionLatency.ProtoReflect.Descriptor instead. func (*ConnectionLatency) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{37} + return file_gateway_proto_rawDescGZIP(), []int{38} } func (x *ConnectionLatency) GetMinMsFromPeer() int64 { @@ -2510,7 +2573,7 @@ type GatewayInfo struct { func (x *GatewayInfo) Reset() { *x = GatewayInfo{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[38] + mi := &file_gateway_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2523,7 +2586,7 @@ func (x *GatewayInfo) String() string { func (*GatewayInfo) ProtoMessage() {} func (x *GatewayInfo) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[38] + mi := &file_gateway_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2536,7 +2599,7 @@ func (x *GatewayInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use GatewayInfo.ProtoReflect.Descriptor instead. func (*GatewayInfo) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{38} + return file_gateway_proto_rawDescGZIP(), []int{39} } func (x *GatewayInfo) GetVersion() string { @@ -2616,7 +2679,7 @@ type StatusResponse struct { func (x *StatusResponse) Reset() { *x = StatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[39] + mi := &file_gateway_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2629,7 +2692,7 @@ func (x *StatusResponse) String() string { func (*StatusResponse) ProtoMessage() {} func (x *StatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[39] + mi := &file_gateway_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2642,7 +2705,7 @@ func (x *StatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead. func (*StatusResponse) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{39} + return file_gateway_proto_rawDescGZIP(), []int{40} } func (x *StatusResponse) GetGatewayInfo() *GatewayInfo { @@ -2688,7 +2751,7 @@ type TxResult struct { func (x *TxResult) Reset() { *x = TxResult{} if protoimpl.UnsafeEnabled { - mi := &file_gateway_proto_msgTypes[40] + mi := &file_gateway_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2701,7 +2764,7 @@ func (x *TxResult) String() string { func (*TxResult) ProtoMessage() {} func (x *TxResult) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[40] + mi := &file_gateway_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2714,7 +2777,7 @@ func (x *TxResult) ProtoReflect() protoreflect.Message { // Deprecated: Use TxResult.ProtoReflect.Descriptor instead. func (*TxResult) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{40} + return file_gateway_proto_rawDescGZIP(), []int{41} } func (x *TxResult) GetTxHash() string { @@ -2942,248 +3005,257 @@ var file_gateway_proto_rawDesc = []byte{ 0x6f, 0x72, 0x6b, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x61, 0x74, 0x61, 0x22, 0xf8, 0x01, 0x0a, 0x12, 0x42, 0x6c, 0x78, - 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x58, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x22, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x6e, - 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, - 0x0a, 0x0e, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x5f, - 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x6e, 0x63, 0x65, - 0x5f, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, - 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x0b, 0x42, 0x6c, 0x78, - 0x72, 0x54, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, - 0x68, 0x22, 0x34, 0x0a, 0x07, 0x54, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x10, 0x0a, 0x03, - 0x69, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12, 0x17, - 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x34, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x73, 0x0a, - 0x10, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x58, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, - 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x08, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x12, 0x30, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x08, 0x74, 0x78, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x44, 0x61, - 0x74, 0x65, 0x22, 0xfd, 0x04, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x65, 0x72, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x28, - 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x23, - 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4e, - 0x6f, 0x64, 0x65, 0x12, 0x3e, 0x0a, 0x1c, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, - 0x62, 0x64, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18, 0x6e, 0x65, 0x77, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, - 0x42, 0x64, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6e, 0x65, - 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x53, 0x0a, 0x27, 0x6e, - 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x22, 0x6e, 0x65, - 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x46, 0x72, - 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, - 0x12, 0x5d, 0x0a, 0x2c, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x6e, - 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, + 0x77, 0x6f, 0x72, 0x6b, 0x44, 0x61, 0x74, 0x61, 0x22, 0x47, 0x0a, 0x0b, 0x54, 0x78, 0x41, 0x6e, + 0x64, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x22, 0xc7, 0x02, 0x0a, 0x12, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, + 0x58, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4e, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, 0x41, 0x6e, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x52, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x6e, + 0x64, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x5f, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, + 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x78, + 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x66, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, + 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, + 0x73, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x83, 0x01, 0x0a, 0x0d, + 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, + 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, + 0x78, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x22, 0x26, 0x0a, 0x0b, 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, 0x34, 0x0a, 0x07, 0x54, 0x78, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x22, + 0x34, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x10, 0x0a, + 0x03, 0x69, 0x64, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x69, 0x64, 0x78, 0x12, + 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x73, 0x0a, 0x10, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x54, 0x58, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x74, 0x78, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x08, + 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x74, 0x78, 0x5f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x52, 0x08, 0x74, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x0b, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x44, 0x61, 0x74, 0x65, 0x22, 0xfd, 0x04, 0x0a, 0x0f, 0x4e, + 0x6f, 0x64, 0x65, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x28, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x27, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x72, 0x6f, - 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x4d, 0x0a, 0x24, 0x6e, 0x65, 0x77, 0x5f, 0x74, 0x78, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1f, 0x6e, - 0x65, 0x77, 0x54, 0x78, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x36, - 0x0a, 0x18, 0x6e, 0x65, 0x77, 0x5f, 0x74, 0x78, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x64, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x14, 0x6e, 0x65, 0x77, 0x54, 0x78, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, - 0x72, 0x6f, 0x6d, 0x42, 0x64, 0x6e, 0x12, 0x25, 0x0a, 0x0f, 0x74, 0x78, 0x5f, 0x73, 0x65, 0x6e, - 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0c, 0x74, 0x78, 0x53, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x33, 0x0a, - 0x16, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x78, 0x5f, 0x66, 0x72, - 0x6f, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x64, - 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x78, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x6f, - 0x64, 0x65, 0x22, 0x64, 0x0a, 0x0c, 0x57, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x79, - 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xd8, 0x02, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, - 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, - 0x6f, 0x6e, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3a, 0x0a, 0x0d, - 0x77, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x57, 0x73, - 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x77, 0x73, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x6e, 0x6f, - 0x64, 0x65, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x73, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x73, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x44, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x22, 0x80, 0x01, 0x0a, 0x0d, 0x42, 0x44, 0x4e, 0x43, 0x6f, 0x6e, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, - 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x34, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x07, 0x6c, - 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0xba, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x27, 0x0a, 0x10, - 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x65, 0x65, 0x72, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x4d, 0x73, 0x46, 0x72, 0x6f, - 0x6d, 0x50, 0x65, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x5f, - 0x74, 0x6f, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, - 0x69, 0x6e, 0x4d, 0x73, 0x54, 0x6f, 0x50, 0x65, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6c, - 0x6f, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x73, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, - 0x6d, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x72, 0x69, 0x70, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x4d, 0x73, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, - 0x72, 0x69, 0x70, 0x22, 0xa9, 0x02, 0x0a, 0x0b, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, - 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x69, 0x6d, - 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, - 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, - 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, - 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, - 0x9f, 0x03, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0c, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x05, 0x6e, - 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, - 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, - 0x79, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x51, 0x0a, 0x0a, 0x4e, - 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x51, - 0x0a, 0x0b, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x42, 0x44, 0x4e, 0x43, 0x6f, 0x6e, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x8e, 0x01, 0x0a, 0x08, 0x54, 0x78, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x78, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x78, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, - 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x72, 0x61, 0x77, 0x54, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x77, - 0x54, 0x78, 0x32, 0xf7, 0x05, 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x38, - 0x0a, 0x06, 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, 0x12, 0x16, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x54, - 0x78, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, 0x42, 0x6c, 0x78, 0x72, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x58, 0x12, 0x1b, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x58, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x42, - 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x58, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x12, 0x35, 0x0a, 0x05, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x15, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x50, 0x65, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x78, 0x53, 0x74, - 0x6f, 0x72, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x17, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, - 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x05, - 0x47, 0x65, 0x74, 0x54, 0x78, 0x12, 0x20, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, - 0x47, 0x65, 0x74, 0x42, 0x78, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x78, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x32, 0x0a, 0x04, - 0x53, 0x74, 0x6f, 0x70, 0x12, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, - 0x12, 0x3b, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, - 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x15, 0x44, 0x69, 0x73, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x65, - 0x65, 0x72, 0x12, 0x25, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x44, 0x69, 0x73, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x65, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, - 0x12, 0x3a, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x54, 0x78, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x65, 0x77, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x65, 0x77, - 0x54, 0x78, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42, 0x42, 0x5a, 0x40, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x58, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x62, 0x78, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x2d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2d, 0x67, 0x6f, 0x2f, 0x62, - 0x78, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x23, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x3e, 0x0a, 0x1c, 0x6e, + 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x64, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x18, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x64, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x6e, + 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x53, + 0x65, 0x65, 0x6e, 0x12, 0x53, 0x0a, 0x27, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x22, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x5d, 0x0a, 0x2c, 0x6e, 0x65, 0x77, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x27, + 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x6e, 0x65, 0x77, 0x5f, 0x74, + 0x78, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1f, 0x6e, 0x65, 0x77, 0x54, 0x78, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x18, 0x6e, 0x65, 0x77, 0x5f, 0x74, 0x78, + 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, + 0x64, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6e, 0x65, 0x77, 0x54, 0x78, 0x52, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x64, 0x6e, 0x12, 0x25, + 0x0a, 0x0f, 0x74, 0x78, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x64, + 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x74, 0x78, 0x53, 0x65, 0x6e, 0x74, 0x54, + 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x5f, 0x74, 0x78, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x54, 0x78, 0x46, 0x72, 0x6f, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0x64, 0x0a, 0x0c, 0x57, 0x73, + 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1f, + 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x22, 0xd8, 0x02, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x77, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x57, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x0c, 0x77, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x43, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x20, 0x0a, 0x0b, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x80, 0x01, 0x0a, 0x0d, + 0x42, 0x44, 0x4e, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x34, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, + 0x74, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0xba, + 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, + 0x65, 0x6e, 0x63, 0x79, 0x12, 0x27, 0x0a, 0x10, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x5f, 0x66, + 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, + 0x6d, 0x69, 0x6e, 0x4d, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x65, 0x72, 0x12, 0x23, 0x0a, + 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x4d, 0x73, 0x54, 0x6f, 0x50, 0x65, + 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, + 0x73, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x5f, 0x74, 0x72, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x69, 0x6e, + 0x4d, 0x73, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x69, 0x70, 0x22, 0xa9, 0x02, 0x0a, 0x0b, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x75, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x9f, 0x03, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0c, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x3b, 0x0a, + 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, + 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x1a, 0x51, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x51, 0x0a, 0x0b, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x2e, 0x42, 0x44, 0x4e, 0x43, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8e, 0x01, 0x0a, 0x08, 0x54, 0x78, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, + 0x0a, 0x0a, 0x74, 0x78, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x74, 0x78, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x20, + 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x77, 0x54, 0x78, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x77, 0x54, 0x78, 0x32, 0xf7, 0x05, 0x0a, 0x07, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x38, 0x0a, 0x06, 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, + 0x12, 0x16, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x54, + 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x54, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x47, 0x0a, 0x0b, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x58, 0x12, + 0x1b, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x54, 0x58, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x42, 0x6c, 0x78, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x54, 0x58, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x05, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x12, 0x15, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x78, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x17, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x54, 0x78, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x05, 0x47, 0x65, 0x74, 0x54, 0x78, 0x12, 0x20, 0x2e, + 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x78, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x78, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x32, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x14, 0x2e, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x16, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x65, 0x0a, 0x15, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12, 0x25, 0x2e, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x44, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x65, 0x65, + 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x54, + 0x78, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x65, 0x77, + 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4e, 0x65, 0x77, 0x54, 0x78, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x30, 0x01, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x58, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2d, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x62, 0x78, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2d, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x2d, 0x67, 0x6f, 0x2f, 0x62, 0x78, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3198,7 +3270,7 @@ func file_gateway_proto_rawDescGZIP() []byte { return file_gateway_proto_rawDescData } -var file_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 43) +var file_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 44) var file_gateway_proto_goTypes = []interface{}{ (*NewTxsRequest)(nil), // 0: gateway.NewTxsRequest (*Tx)(nil), // 1: gateway.Tx @@ -3225,25 +3297,26 @@ var file_gateway_proto_goTypes = []interface{}{ (*TxStoreRequest)(nil), // 22: gateway.TxStoreRequest (*TxStoreNetworkData)(nil), // 23: gateway.TxStoreNetworkData (*TxStoreReply)(nil), // 24: gateway.TxStoreReply - (*BlxrBatchTXRequest)(nil), // 25: gateway.BlxrBatchTXRequest - (*BlxrTxRequest)(nil), // 26: gateway.BlxrTxRequest - (*BlxrTxReply)(nil), // 27: gateway.BlxrTxReply - (*TxIndex)(nil), // 28: gateway.TxIndex - (*ErrorIndex)(nil), // 29: gateway.ErrorIndex - (*BlxrBatchTXReply)(nil), // 30: gateway.BlxrBatchTXReply - (*StatusRequest)(nil), // 31: gateway.StatusRequest - (*AccountInfo)(nil), // 32: gateway.AccountInfo - (*NodePerformance)(nil), // 33: gateway.NodePerformance - (*WsConnStatus)(nil), // 34: gateway.WsConnStatus - (*NodeConnStatus)(nil), // 35: gateway.NodeConnStatus - (*BDNConnStatus)(nil), // 36: gateway.BDNConnStatus - (*ConnectionLatency)(nil), // 37: gateway.ConnectionLatency - (*GatewayInfo)(nil), // 38: gateway.GatewayInfo - (*StatusResponse)(nil), // 39: gateway.StatusResponse - (*TxResult)(nil), // 40: gateway.TxResult - nil, // 41: gateway.StatusResponse.NodesEntry - nil, // 42: gateway.StatusResponse.RelaysEntry - (*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp + (*TxAndSender)(nil), // 25: gateway.TxAndSender + (*BlxrBatchTXRequest)(nil), // 26: gateway.BlxrBatchTXRequest + (*BlxrTxRequest)(nil), // 27: gateway.BlxrTxRequest + (*BlxrTxReply)(nil), // 28: gateway.BlxrTxReply + (*TxIndex)(nil), // 29: gateway.TxIndex + (*ErrorIndex)(nil), // 30: gateway.ErrorIndex + (*BlxrBatchTXReply)(nil), // 31: gateway.BlxrBatchTXReply + (*StatusRequest)(nil), // 32: gateway.StatusRequest + (*AccountInfo)(nil), // 33: gateway.AccountInfo + (*NodePerformance)(nil), // 34: gateway.NodePerformance + (*WsConnStatus)(nil), // 35: gateway.WsConnStatus + (*NodeConnStatus)(nil), // 36: gateway.NodeConnStatus + (*BDNConnStatus)(nil), // 37: gateway.BDNConnStatus + (*ConnectionLatency)(nil), // 38: gateway.ConnectionLatency + (*GatewayInfo)(nil), // 39: gateway.GatewayInfo + (*StatusResponse)(nil), // 40: gateway.StatusResponse + (*TxResult)(nil), // 41: gateway.TxResult + nil, // 42: gateway.StatusResponse.NodesEntry + nil, // 43: gateway.StatusResponse.RelaysEntry + (*timestamppb.Timestamp)(nil), // 44: google.protobuf.Timestamp } var file_gateway_proto_depIdxs = []int32{ 1, // 0: gateway.NewTxsReply.tx:type_name -> gateway.Tx @@ -3254,48 +3327,49 @@ var file_gateway_proto_depIdxs = []int32{ 13, // 5: gateway.Peer.unpaid_tx_throughput:type_name -> gateway.RateSnapshot 14, // 6: gateway.PeersReply.peers:type_name -> gateway.Peer 17, // 7: gateway.Transactions.transactions:type_name -> gateway.Transaction - 43, // 8: gateway.BxTransaction.add_time:type_name -> google.protobuf.Timestamp + 44, // 8: gateway.BxTransaction.add_time:type_name -> google.protobuf.Timestamp 19, // 9: gateway.GetBxTransactionResponse.tx:type_name -> gateway.BxTransaction 19, // 10: gateway.TxStoreNetworkData.oldest_tx:type_name -> gateway.BxTransaction 23, // 11: gateway.TxStoreReply.network_data:type_name -> gateway.TxStoreNetworkData - 28, // 12: gateway.BlxrBatchTXReply.tx_hashes:type_name -> gateway.TxIndex - 29, // 13: gateway.BlxrBatchTXReply.tx_errors:type_name -> gateway.ErrorIndex - 34, // 14: gateway.NodeConnStatus.ws_connection:type_name -> gateway.WsConnStatus - 33, // 15: gateway.NodeConnStatus.node_performance:type_name -> gateway.NodePerformance - 37, // 16: gateway.BDNConnStatus.latency:type_name -> gateway.ConnectionLatency - 38, // 17: gateway.StatusResponse.gateway_info:type_name -> gateway.GatewayInfo - 41, // 18: gateway.StatusResponse.nodes:type_name -> gateway.StatusResponse.NodesEntry - 42, // 19: gateway.StatusResponse.relays:type_name -> gateway.StatusResponse.RelaysEntry - 32, // 20: gateway.StatusResponse.account_info:type_name -> gateway.AccountInfo - 35, // 21: gateway.StatusResponse.NodesEntry.value:type_name -> gateway.NodeConnStatus - 36, // 22: gateway.StatusResponse.RelaysEntry.value:type_name -> gateway.BDNConnStatus - 26, // 23: gateway.Gateway.BlxrTx:input_type -> gateway.BlxrTxRequest - 25, // 24: gateway.Gateway.BlxrBatchTX:input_type -> gateway.BlxrBatchTXRequest - 12, // 25: gateway.Gateway.Peers:input_type -> gateway.PeersRequest - 22, // 26: gateway.Gateway.TxStoreSummary:input_type -> gateway.TxStoreRequest - 20, // 27: gateway.Gateway.GetTx:input_type -> gateway.GetBxTransactionRequest - 10, // 28: gateway.Gateway.Stop:input_type -> gateway.StopRequest - 8, // 29: gateway.Gateway.Version:input_type -> gateway.VersionRequest - 31, // 30: gateway.Gateway.Status:input_type -> gateway.StatusRequest - 5, // 31: gateway.Gateway.Subscriptions:input_type -> gateway.SubscriptionsRequest - 3, // 32: gateway.Gateway.DisconnectInboundPeer:input_type -> gateway.DisconnectInboundPeerRequest - 0, // 33: gateway.Gateway.NewTxs:input_type -> gateway.NewTxsRequest - 27, // 34: gateway.Gateway.BlxrTx:output_type -> gateway.BlxrTxReply - 30, // 35: gateway.Gateway.BlxrBatchTX:output_type -> gateway.BlxrBatchTXReply - 15, // 36: gateway.Gateway.Peers:output_type -> gateway.PeersReply - 24, // 37: gateway.Gateway.TxStoreSummary:output_type -> gateway.TxStoreReply - 21, // 38: gateway.Gateway.GetTx:output_type -> gateway.GetBxTransactionResponse - 11, // 39: gateway.Gateway.Stop:output_type -> gateway.StopReply - 9, // 40: gateway.Gateway.Version:output_type -> gateway.VersionReply - 39, // 41: gateway.Gateway.Status:output_type -> gateway.StatusResponse - 7, // 42: gateway.Gateway.Subscriptions:output_type -> gateway.SubscriptionsReply - 4, // 43: gateway.Gateway.DisconnectInboundPeer:output_type -> gateway.DisconnectInboundPeerReply - 2, // 44: gateway.Gateway.NewTxs:output_type -> gateway.NewTxsReply - 34, // [34:45] is the sub-list for method output_type - 23, // [23:34] is the sub-list for method input_type - 23, // [23:23] is the sub-list for extension type_name - 23, // [23:23] is the sub-list for extension extendee - 0, // [0:23] is the sub-list for field type_name + 25, // 12: gateway.BlxrBatchTXRequest.transactions_and_senders:type_name -> gateway.TxAndSender + 29, // 13: gateway.BlxrBatchTXReply.tx_hashes:type_name -> gateway.TxIndex + 30, // 14: gateway.BlxrBatchTXReply.tx_errors:type_name -> gateway.ErrorIndex + 35, // 15: gateway.NodeConnStatus.ws_connection:type_name -> gateway.WsConnStatus + 34, // 16: gateway.NodeConnStatus.node_performance:type_name -> gateway.NodePerformance + 38, // 17: gateway.BDNConnStatus.latency:type_name -> gateway.ConnectionLatency + 39, // 18: gateway.StatusResponse.gateway_info:type_name -> gateway.GatewayInfo + 42, // 19: gateway.StatusResponse.nodes:type_name -> gateway.StatusResponse.NodesEntry + 43, // 20: gateway.StatusResponse.relays:type_name -> gateway.StatusResponse.RelaysEntry + 33, // 21: gateway.StatusResponse.account_info:type_name -> gateway.AccountInfo + 36, // 22: gateway.StatusResponse.NodesEntry.value:type_name -> gateway.NodeConnStatus + 37, // 23: gateway.StatusResponse.RelaysEntry.value:type_name -> gateway.BDNConnStatus + 27, // 24: gateway.Gateway.BlxrTx:input_type -> gateway.BlxrTxRequest + 26, // 25: gateway.Gateway.BlxrBatchTX:input_type -> gateway.BlxrBatchTXRequest + 12, // 26: gateway.Gateway.Peers:input_type -> gateway.PeersRequest + 22, // 27: gateway.Gateway.TxStoreSummary:input_type -> gateway.TxStoreRequest + 20, // 28: gateway.Gateway.GetTx:input_type -> gateway.GetBxTransactionRequest + 10, // 29: gateway.Gateway.Stop:input_type -> gateway.StopRequest + 8, // 30: gateway.Gateway.Version:input_type -> gateway.VersionRequest + 32, // 31: gateway.Gateway.Status:input_type -> gateway.StatusRequest + 5, // 32: gateway.Gateway.Subscriptions:input_type -> gateway.SubscriptionsRequest + 3, // 33: gateway.Gateway.DisconnectInboundPeer:input_type -> gateway.DisconnectInboundPeerRequest + 0, // 34: gateway.Gateway.NewTxs:input_type -> gateway.NewTxsRequest + 28, // 35: gateway.Gateway.BlxrTx:output_type -> gateway.BlxrTxReply + 31, // 36: gateway.Gateway.BlxrBatchTX:output_type -> gateway.BlxrBatchTXReply + 15, // 37: gateway.Gateway.Peers:output_type -> gateway.PeersReply + 24, // 38: gateway.Gateway.TxStoreSummary:output_type -> gateway.TxStoreReply + 21, // 39: gateway.Gateway.GetTx:output_type -> gateway.GetBxTransactionResponse + 11, // 40: gateway.Gateway.Stop:output_type -> gateway.StopReply + 9, // 41: gateway.Gateway.Version:output_type -> gateway.VersionReply + 40, // 42: gateway.Gateway.Status:output_type -> gateway.StatusResponse + 7, // 43: gateway.Gateway.Subscriptions:output_type -> gateway.SubscriptionsReply + 4, // 44: gateway.Gateway.DisconnectInboundPeer:output_type -> gateway.DisconnectInboundPeerReply + 2, // 45: gateway.Gateway.NewTxs:output_type -> gateway.NewTxsReply + 35, // [35:46] is the sub-list for method output_type + 24, // [24:35] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_gateway_proto_init() } @@ -3605,7 +3679,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlxrBatchTXRequest); i { + switch v := v.(*TxAndSender); i { case 0: return &v.state case 1: @@ -3617,7 +3691,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlxrTxRequest); i { + switch v := v.(*BlxrBatchTXRequest); i { case 0: return &v.state case 1: @@ -3629,7 +3703,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlxrTxReply); i { + switch v := v.(*BlxrTxRequest); i { case 0: return &v.state case 1: @@ -3641,7 +3715,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TxIndex); i { + switch v := v.(*BlxrTxReply); i { case 0: return &v.state case 1: @@ -3653,7 +3727,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ErrorIndex); i { + switch v := v.(*TxIndex); i { case 0: return &v.state case 1: @@ -3665,7 +3739,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlxrBatchTXReply); i { + switch v := v.(*ErrorIndex); i { case 0: return &v.state case 1: @@ -3677,7 +3751,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusRequest); i { + switch v := v.(*BlxrBatchTXReply); i { case 0: return &v.state case 1: @@ -3689,7 +3763,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountInfo); i { + switch v := v.(*StatusRequest); i { case 0: return &v.state case 1: @@ -3701,7 +3775,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodePerformance); i { + switch v := v.(*AccountInfo); i { case 0: return &v.state case 1: @@ -3713,7 +3787,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WsConnStatus); i { + switch v := v.(*NodePerformance); i { case 0: return &v.state case 1: @@ -3725,7 +3799,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeConnStatus); i { + switch v := v.(*WsConnStatus); i { case 0: return &v.state case 1: @@ -3737,7 +3811,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BDNConnStatus); i { + switch v := v.(*NodeConnStatus); i { case 0: return &v.state case 1: @@ -3749,7 +3823,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConnectionLatency); i { + switch v := v.(*BDNConnStatus); i { case 0: return &v.state case 1: @@ -3761,7 +3835,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GatewayInfo); i { + switch v := v.(*ConnectionLatency); i { case 0: return &v.state case 1: @@ -3773,7 +3847,7 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatusResponse); i { + switch v := v.(*GatewayInfo); i { case 0: return &v.state case 1: @@ -3785,6 +3859,18 @@ func file_gateway_proto_init() { } } file_gateway_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gateway_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TxResult); i { case 0: return &v.state @@ -3803,7 +3889,7 @@ func file_gateway_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gateway_proto_rawDesc, NumEnums: 0, - NumMessages: 43, + NumMessages: 44, NumExtensions: 0, NumServices: 1, }, diff --git a/protobuf/gateway.proto b/protobuf/gateway.proto index ed7a02e..3443a73 100644 --- a/protobuf/gateway.proto +++ b/protobuf/gateway.proto @@ -173,13 +173,19 @@ message TxStoreReply { } +message TxAndSender { + string transaction=1; + bytes sender=2; +} + message BlxrBatchTXRequest { - repeated string transactions = 1; + repeated TxAndSender transactions_and_senders = 1; bool nonce_monitoring = 2; bool next_validator = 3; int32 fallback = 4; bool validators_only = 5; bool node_validation = 6; + int64 sending_time=7; } message BlxrTxRequest { diff --git a/sdnmessage/accounts.go b/sdnmessage/accounts.go index 0fa3791..3ad57a6 100644 --- a/sdnmessage/accounts.go +++ b/sdnmessage/accounts.go @@ -250,6 +250,8 @@ type Account struct { SolanaDexAPIRateLimit BDNQuotaService `json:"solana_dex_api_rate_limit"` SolanaDexAPIStreamLimit BDNQuotaService `json:"solana_dex_api_stream_limit"` + + TwammStreaming BDNFeedService `json:"twamm_streaming"` } // Validate verifies the response that the response from bxapi is well understood @@ -451,6 +453,13 @@ func GetDefaultEliteAccount(now time.Time) Account { }, ExpireDateTime: now.Add(time.Hour), }, + TwammStreaming: BDNFeedService{ + ExpireDate: now.AddDate(0, 0, 1).Format("2006-01-02"), + Feed: FeedProperties{ + AllowFiltering: true, + AvailableFields: []string{"all"}, + }, + }, SecretHash: "", } } diff --git a/sdnmessage/blockchain_network.go b/sdnmessage/blockchain_network.go index fe0cb5e..1a8df82 100644 --- a/sdnmessage/blockchain_network.go +++ b/sdnmessage/blockchain_network.go @@ -20,7 +20,7 @@ type BlockchainAttributes struct { BootstrapNodes []string `json:"bootstrap_nodes"` ExecutionLayerForks []string `json:"execution_layer_forks,omitempty"` - // ETHShanghaiMergeTimeRFC3339 specifies Ethereum Shanghai merge time, after this time + // ETHShanghaiMergeTimeUnix specifies Ethereum Shanghai merge time, after this time // relay will ignore beacon blocks from gateways if their protocol version is low // and won't propagate beacon blocks to such gateways but will still propagate transactions ETHShanghaiMergeTimeUnix int64 `json:"eth_shanghai_merge_time_unix"` diff --git a/sdnmessage/node_event.go b/sdnmessage/node_event.go index dfd8e67..ab17aa3 100644 --- a/sdnmessage/node_event.go +++ b/sdnmessage/node_event.go @@ -17,6 +17,8 @@ const ( NePeerConnDisabled NodeEventType = "PEER_CONN_DISABLED" NeBlockchainNodeConnEstablished NodeEventType = "BLOCKCHAIN_NODE_CONN_ESTABLISHED" NeBlockchainNodeConnError NodeEventType = "BLOCKCHAIN_NODE_CONN_ERR" + NeAddAccessibleGateway NodeEventType = "ADD_ACCESSIBLE_GATEWAY" + NeRemoveAccessibleGateway NodeEventType = "REMOVE_ACCESSIBLE_GATEWAY" ) // NodeEvent represents a node event and its context being reported to the SDN @@ -82,3 +84,23 @@ func NewBlockchainNodeConnError(NodeID types.NodeID, peerIP string, peerPort int PeerPort: peerPort, } } + +// NewAddAccessibleGatewayEvent returns add accessible gateway event +func NewAddAccessibleGatewayEvent(nodeID types.NodeID, reason string, timestamp string) NodeEvent { + return NodeEvent{ + Timestamp: timestamp, + NodeID: nodeID, + EventType: NeAddAccessibleGateway, + Payload: reason, + } +} + +// NewRemoveAccessibleGatewayEvent returns remove accessible gateway event +func NewRemoveAccessibleGatewayEvent(nodeID types.NodeID, reason string, timestamp string) NodeEvent { + return NodeEvent{ + Timestamp: timestamp, + NodeID: nodeID, + EventType: NeRemoveAccessibleGateway, + Payload: reason, + } +} diff --git a/servers/clienthandler.go b/servers/clienthandler.go index 7a9a4db..295c8bd 100644 --- a/servers/clienthandler.go +++ b/servers/clienthandler.go @@ -25,6 +25,7 @@ import ( "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" "github.com/bloXroute-Labs/gateway/v2/utils/orderedmap" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -43,6 +44,7 @@ type ClientHandler struct { websocketServer *http.Server httpServer *HTTPServer log *log.Entry + getQuotaUsage func(accountID string) (*connections.QuotaResponseBody, error) } // MultiTransactions - response for MultiTransactions subscription @@ -87,6 +89,7 @@ type handlerObj struct { remoteAddress string connectionAccount sdnmessage.Account log *log.Entry + getQuotaUsage func(accountID string) (*connections.QuotaResponseBody, error) } type clientReq struct { @@ -133,12 +136,13 @@ func newCall(name string) *RPCCall { } // NewClientHandler is a constructor for ClientHandler -func NewClientHandler(feedManager *FeedManager, websocketServer *http.Server, httpServer *HTTPServer, log *log.Entry) ClientHandler { +func NewClientHandler(feedManager *FeedManager, websocketServer *http.Server, httpServer *HTTPServer, log *log.Entry, getQuotaUsage func(accountID string) (*connections.QuotaResponseBody, error)) ClientHandler { return ClientHandler{ feedManager: feedManager, websocketServer: websocketServer, httpServer: httpServer, log: log, + getQuotaUsage: getQuotaUsage, } } @@ -228,7 +232,7 @@ var operands = []string{"and", "or"} var availableFeeds = []types.FeedType{types.NewTxsFeed, types.NewBlocksFeed, types.BDNBlocksFeed, types.PendingTxsFeed, types.OnBlockFeed, types.TxReceiptsFeed, types.NewBeaconBlocksFeed, types.BDNBeaconBlocksFeed} // NewWSServer creates and returns a new websocket server managed by FeedManager -func NewWSServer(feedManager *FeedManager) *http.Server { +func NewWSServer(feedManager *FeedManager, getQuotaUsage func(accountID string) (*connections.QuotaResponseBody, error)) *http.Server { handler := http.NewServeMux() wsHandler := func(responseWriter http.ResponseWriter, request *http.Request) { connectionAccountID, connectionSecretHash, err := getAccountIDSecretHashFromReq(request, feedManager.cfg.WebsocketTLSEnabled) @@ -271,7 +275,7 @@ func NewWSServer(feedManager *FeedManager) *http.Server { errorWithDelay(responseWriter, request, "wrong value in the authorization header") return } - handleWSClientConnection(feedManager, responseWriter, request, connectionAccountModel) + handleWSClientConnection(feedManager, responseWriter, request, connectionAccountModel, getQuotaUsage) } handler.HandleFunc("/ws", wsHandler) @@ -298,7 +302,7 @@ func errorWithDelay(w http.ResponseWriter, r *http.Request, msg string) { } // handleWsClientConnection - when new http connection is made we get here upgrade to ws, and start handling -func handleWSClientConnection(feedManager *FeedManager, w http.ResponseWriter, r *http.Request, accountModel sdnmessage.Account) { +func handleWSClientConnection(feedManager *FeedManager, w http.ResponseWriter, r *http.Request, accountModel sdnmessage.Account, getQuotaUsage func(accountID string) (*connections.QuotaResponseBody, error)) { log.Debugf("New web-socket connection from %v", r.RemoteAddr) connection, err := upgrader.Upgrade(w, r, nil) if err != nil { @@ -318,6 +322,7 @@ func handleWSClientConnection(feedManager *FeedManager, w http.ResponseWriter, r remoteAddress: r.RemoteAddr, connectionAccount: accountModel, log: logger, + getQuotaUsage: getQuotaUsage, } asynHhandler := jsonrpc2.AsyncHandler(handler) @@ -357,7 +362,7 @@ func getAccountIDSecretHashFromReq(request *http.Request, websocketTLSEnabled bo } func (ch *ClientHandler) runWSServer() { - ch.websocketServer = NewWSServer(ch.feedManager) + ch.websocketServer = NewWSServer(ch.feedManager, ch.getQuotaUsage) ch.log.Infof("starting websockets RPC server at: %v", ch.websocketServer.Addr) var err error if ch.feedManager.cfg.WebsocketTLSEnabled { @@ -588,10 +593,24 @@ func (h *handlerObj) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonr return } feedName := request.feed - if len(h.FeedManager.nodeWSManager.Providers()) == 0 && (feedName != types.NewTxsFeed && feedName != types.BDNBlocksFeed && feedName != types.NewBeaconBlocksFeed && feedName != types.BDNBeaconBlocksFeed) { - errMsg := fmt.Sprintf("%v feed requires a websockets endpoint to be specifed via either --eth-ws-uri or --multi-node startup parameter", feedName) - SendErrorMsg(ctx, jsonrpc.InvalidParams, errMsg, conn, req) - return + if len(h.FeedManager.nodeWSManager.Providers()) == 0 { + switch request.feed { + case types.NewTxsFeed: + case types.BDNBlocksFeed: + case types.NewBeaconBlocksFeed: + case types.BDNBeaconBlocksFeed: + case types.NewBlocksFeed: + // Blocks in consensus come not from websocket + if h.FeedManager.networkNum == bxgateway.RopstenNum || h.FeedManager.networkNum == bxgateway.GoerliNum || h.FeedManager.networkNum == bxgateway.MainnetNum { + break + } + + fallthrough + default: + errMsg := fmt.Sprintf("%v feed requires a websockets endpoint to be specifed via either --eth-ws-uri or --multi-node startup parameter", feedName) + SendErrorMsg(ctx, jsonrpc.InvalidParams, errMsg, conn, req) + return + } } var filters string if request.expr != nil { @@ -800,7 +819,7 @@ func (h *handlerObj) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonr ws = connections.NewRPCConn(h.connectionAccount.AccountID, h.remoteAddress, h.FeedManager.networkNum, utils.Websocket) } - txHash, ok := h.handleSingleTransaction(ctx, conn, req, params.Transaction, ws, params.ValidatorsOnly, true, params.NextValidator, params.Fallback, h.FeedManager.nextValidatorMap, params.NodeValidation) + txHash, ok := h.handleSingleTransaction(ctx, conn, req, params.Transaction, ws, params.ValidatorsOnly, true, params.NextValidator, params.Fallback, h.FeedManager.nextValidatorMap, h.FeedManager.validatorStatusMap, params.NodeValidation, params.FrontRunningProtection) if !ok { return } @@ -838,7 +857,7 @@ func (h *handlerObj) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonr } for _, transaction := range params.Transactions { - txHash, ok := h.handleSingleTransaction(ctx, conn, req, transaction, ws, params.ValidatorsOnly, false, false, 0, nil, false) + txHash, ok := h.handleSingleTransaction(ctx, conn, req, transaction, ws, params.ValidatorsOnly, false, false, 0, nil, nil, false, false) if !ok { continue } @@ -952,6 +971,16 @@ func (h *handlerObj) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonr if err := reply(ctx, conn, req.ID, map[string]string{"status": "ok"}); err != nil { h.log.Errorf("%v mev searcher error: %v", jsonrpc.RPCMEVSearcher, err) } + case jsonrpc.RPCQuotaUsage: + accountID := fmt.Sprint(h.connectionAccount.AccountID) + quotaRes, err := h.getQuotaUsage(accountID) + if err != nil { + sendErr := fmt.Errorf("failed to fetch quota usage: %v", err) + SendErrorMsg(ctx, jsonrpc.MethodNotFound, sendErr.Error(), conn, req) + } + if err = reply(ctx, conn, req.ID, quotaRes); err != nil { + h.log.Errorf("%v reply error - %v", jsonrpc.RPCQuotaUsage, err) + } default: err := fmt.Errorf("got unsupported method name: %v", req.Method) @@ -1311,39 +1340,63 @@ func EvaluateFilters(expr conditions.Expr) error { return err } -func (h *handlerObj) handleSingleTransaction(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request, transaction string, ws connections.Conn, validatorsOnly bool, sendError bool, nextValidator bool, fallback uint16, nextValidatorMap *orderedmap.OrderedMap, nodeValidationRequested bool) (string, bool) { - tx, err := ValidateTxFromExternalSource(transaction, validatorsOnly, h.FeedManager.chainID, nextValidator, fallback, nextValidatorMap, h.FeedManager.networkNum, ws.Info().AccountID, nodeValidationRequested, h.FeedManager.nodeWSManager) +func (h *handlerObj) handleSingleTransaction(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request, transaction string, ws connections.Conn, validatorsOnly bool, sendError bool, nextValidator bool, fallback uint16, nextValidatorMap *orderedmap.OrderedMap, validatorStatusMap *syncmap.SyncMap[string, bool], nodeValidationRequested bool, frontRunningProtection bool) (string, bool) { + h.FeedManager.LockPendingNextValidatorTxs() + + txContent, err := types.DecodeHex(transaction) + if err != nil { + SendErrorMsg(ctx, jsonrpc.InvalidParams, err.Error(), conn, req) + return "", false + } + tx, pendingReevaluation, err := ValidateTxFromExternalSource(transaction, txContent, validatorsOnly, h.FeedManager.chainID, nextValidator, fallback, nextValidatorMap, validatorStatusMap, h.FeedManager.networkNum, ws.GetAccountID(), nodeValidationRequested, h.FeedManager.nodeWSManager, ws, h.FeedManager.pendingBSCNextValidatorTxHashToInfo, frontRunningProtection) + h.FeedManager.UnlockPendingNextValidatorTxs() if err != nil && sendError { SendErrorMsg(ctx, jsonrpc.InvalidParams, err.Error(), conn, req) return "", false } - // call the Handler. Don't invoke in a go routine - err = h.FeedManager.node.HandleMsg(tx, ws, connections.RunForeground) - if err != nil { - log.Errorf("failed to handle single transaction: %v", err) - return "", false + if !pendingReevaluation { + // call the Handler. Don't invoke in a go routine + err = h.FeedManager.node.HandleMsg(tx, ws, connections.RunForeground) + if err != nil { + log.Errorf("failed to handle single transaction: %v", err) + return "", false + } + } else if fallback != 0 { + // BSC first validator was not accessible and fallback > BSCBlockTime + // in case fallback time is up before next validator is evaluated, send tx as normal tx at fallback time + // (tx with fallback less than BSCBlockTime are not marked as pending) + time.AfterFunc(time.Duration(uint64(fallback)*bxgateway.MillisecondsToNanosecondsMultiplier), func() { + h.FeedManager.LockPendingNextValidatorTxs() + defer h.FeedManager.UnlockPendingNextValidatorTxs() + if _, exists := h.FeedManager.pendingBSCNextValidatorTxHashToInfo[tx.Hash().String()]; exists { + delete(h.FeedManager.pendingBSCNextValidatorTxHashToInfo, tx.Hash().String()) + log.Infof("sending next validator tx %v because fallback time reached", tx.Hash().String()) + + tx.RemoveFlags(types.TFNextValidator) + tx.SetFallback(0) + err = h.FeedManager.node.HandleMsg(tx, ws, connections.RunForeground) + if err != nil { + log.Errorf("failed to send pending next validator tx %v at fallback time: %v", tx.Hash().String(), err) + } + } + }) } return tx.Hash().String(), true } -// ValidateTxFromExternalSource validate transaction from external source (ws / grpc) -func ValidateTxFromExternalSource(transaction string, validatorsOnly bool, gatewayChainID types.NetworkID, nextValidator bool, fallback uint16, nextValidatorMap *orderedmap.OrderedMap, networkNum types.NetworkNum, accountID types.AccountID, nodeValidationRequested bool, wsManager blockchain.WSManager) (*bxmessage.Tx, error) { - txBytes, err := types.DecodeHex(transaction) - if err != nil { - return nil, err - } - +// ValidateTxFromExternalSource validate transaction from external source (ws / grpc), return bool indicates if tx is pending reevaluation +func ValidateTxFromExternalSource(transaction string, txBytes []byte, validatorsOnly bool, gatewayChainID types.NetworkID, nextValidator bool, fallback uint16, nextValidatorMap *orderedmap.OrderedMap, validatorStatusMap *syncmap.SyncMap[string, bool], networkNum types.NetworkNum, accountID types.AccountID, nodeValidationRequested bool, wsManager blockchain.WSManager, source connections.Conn, pendingBSCNextValidatorTxHashToInfo map[string]PendingNextValidatorTxInfo, frontRunningProtection bool) (*bxmessage.Tx, bool, error) { // Ethereum's transactions encoding for RPC interfaces is slightly different from the RLP encoded format, so decode + re-encode the transaction for consistency. // Specifically, note `UnmarshalBinary` should be used for RPC interfaces, and rlp.DecodeBytes should be used for the wire protocol. var ethTx ethtypes.Transaction - err = ethTx.UnmarshalBinary(txBytes) + err := ethTx.UnmarshalBinary(txBytes) if err != nil { // If UnmarshalBinary failed, we will try RLP in case user made mistake e := rlp.DecodeBytes(txBytes, ðTx) if e != nil { - return nil, err + return nil, false, err } log.Warnf("Ethereum transaction was in RLP format instead of binary," + " transaction has been processed anyway, but it'd be best to use the Ethereum binary standard encoding") @@ -1351,13 +1404,13 @@ func ValidateTxFromExternalSource(transaction string, validatorsOnly bool, gatew if ethTx.ChainId().Int64() != 0 && gatewayChainID != 0 && types.NetworkID(ethTx.ChainId().Int64()) != gatewayChainID { log.Debugf("chainID mismatch for hash %v - tx chainID %v , gateway networkNum %v networkChainID %v", ethTx.Hash().String(), ethTx.ChainId().Int64(), networkNum, gatewayChainID) - return nil, fmt.Errorf("chainID mismatch for hash %v, expect %v got %v, make sure the tx is sent with the right blockchain network", ethTx.Hash().String(), gatewayChainID, ethTx.ChainId().Int64()) + return nil, false, fmt.Errorf("chainID mismatch for hash %v, expect %v got %v, make sure the tx is sent with the right blockchain network", ethTx.Hash().String(), gatewayChainID, ethTx.ChainId().Int64()) } txContent, err := rlp.EncodeToBytes(ðTx) if err != nil { - return nil, err + return nil, false, err } var txFlags = types.TFPaidTx | types.TFLocalRegion @@ -1369,15 +1422,22 @@ func ValidateTxFromExternalSource(transaction string, validatorsOnly bool, gatew txFlags |= types.TFDeliverToNode } + if frontRunningProtection { + txFlags |= types.TFFrontRunningProtection + } + var hash types.SHA256Hash copy(hash[:], ethTx.Hash().Bytes()) // should set the account of the sender, not the account of the gateway itself tx := bxmessage.NewTx(hash, txContent, networkNum, txFlags, accountID) if nextValidator { - err = processNextValidatorTx(tx, fallback, nextValidatorMap, networkNum) + txPendingReevaluation, err := ProcessNextValidatorTx(tx, fallback, nextValidatorMap, validatorStatusMap, networkNum, source, pendingBSCNextValidatorTxHashToInfo) if err != nil { - return nil, err + return nil, false, err + } + if txPendingReevaluation { + return tx, true, nil } } @@ -1394,14 +1454,14 @@ func ValidateTxFromExternalSource(transaction string, validatorsOnly bool, gatew if err != nil { if !strings.Contains(err.Error(), "already known") { // gateway propagates tx to node before doing this check errMsg := fmt.Sprintf("tx (%v) failed node validation with error: %v", tx.Hash(), err.Error()) - return nil, errors.New(errMsg) + return nil, false, errors.New(errMsg) } } } else { - return nil, fmt.Errorf("failed to validate tx (%v) via node: no synced WS provider available", tx.Hash()) + return nil, false, fmt.Errorf("failed to validate tx (%v) via node: no synced WS provider available", tx.Hash()) } } - return tx, nil + return tx, false, nil } type sendBundleArgs struct { @@ -1413,21 +1473,55 @@ type sendBundleArgs struct { RevertingTxHashes []common.Hash `json:"revertingTxHashes"` } -func processNextValidatorTx(tx *bxmessage.Tx, fallback uint16, nextValidatorMap *orderedmap.OrderedMap, networkNum types.NetworkNum) error { +// ProcessNextValidatorTx - sets next validator wallets if accessible and returns bool indicating if tx is pending reevaluation due to inaccessible first validator for BSC +func ProcessNextValidatorTx(tx *bxmessage.Tx, fallback uint16, nextValidatorMap *orderedmap.OrderedMap, validatorStatusMap *syncmap.SyncMap[string, bool], networkNum types.NetworkNum, source connections.Conn, pendingBSCNextValidatorTxHashToInfo map[string]PendingNextValidatorTxInfo) (bool, error) { if networkNum != bxgateway.BSCMainnetNum && networkNum != bxgateway.PolygonMainnetNum { - return errors.New("currently next_validator is only supported on BSC and Polygon networks, please contact bloXroute support") + return false, errors.New("currently next_validator is only supported on BSC and Polygon networks, please contact bloXroute support") } if nextValidatorMap == nil { log.Errorf("failed to process next validator tx, because next validator map is nil, tx %v", tx.Hash().String()) - return errors.New("failed to send next validator tx, please contact bloXroute support") + return false, errors.New("failed to send next validator tx, please contact bloXroute support") } tx.SetFallback(fallback) // take the latest two blocks from the ordered map for updating txMsg walletID n2Validator := nextValidatorMap.Newest() - if n2Validator != nil { + if n2Validator == nil { + return false, errors.New("can't send tx with next_validator because the gateway encountered an issue fetching the epoch block, please try again later or contact bloXroute support") + } + + if networkNum == bxgateway.BSCMainnetNum { + n1Validator := n2Validator.Prev() + n1ValidatorAccessible := false + n1Wallet := "" + if n1Validator != nil { + n1Wallet = n1Validator.Value.(string) + accessible, exist := validatorStatusMap.Load(n1Wallet) + if exist { + n1ValidatorAccessible = accessible + } + } + + if n1ValidatorAccessible { + tx.SetWalletID(0, n1Wallet) + } else { + blockIntervalBSC := bxgateway.NetworkToBlockDuration[bxgateway.BSCMainnet] + if fallback != 0 && fallback < uint16(blockIntervalBSC.Milliseconds()) { + return false, nil + } + pendingBSCNextValidatorTxHashToInfo[tx.Hash().String()] = PendingNextValidatorTxInfo{ + Tx: tx, + Fallback: fallback, + TimeOfRequest: time.Now(), + Source: source, + } + return true, nil + } + } + + if networkNum == bxgateway.PolygonMainnetNum { n1Validator := n2Validator.Prev() if n1Validator != nil { tx.SetWalletID(0, n1Validator.Value.(string)) @@ -1435,11 +1529,9 @@ func processNextValidatorTx(tx *bxmessage.Tx, fallback uint16, nextValidatorMap } else { tx.SetWalletID(0, n2Validator.Value.(string)) } - } else { - return errors.New("can't send tx with next_validator because the gateway encountered an issue fetching the epoch block, please try again later or contact bloXroute support") } - return nil + return false, nil } func (s *sendBundleArgs) validate() error { diff --git a/servers/clienthandler_test.go b/servers/clienthandler_test.go index 4fd1d8b..723e2b5 100644 --- a/servers/clienthandler_test.go +++ b/servers/clienthandler_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/bloXroute-Labs/gateway/v2/connections" "math/big" "net/http" "testing" @@ -74,6 +75,16 @@ func getMockCustomerAccountModel(accountID types.AccountID) (sdnmessage.Account, return accountIDToAccountModel[accountID], err } +func getMockQuotaUsage(accountID string) (*connections.QuotaResponseBody, error) { + res := connections.QuotaResponseBody{ + AccountID: accountID, + QuotaFilled: 1, + QuotaLimit: 2, + } + + return &res, nil +} + func reset(fm *FeedManager, wsURL string, blockchainPeers []types.NodeEndpoint) *websocket.Conn { fm.CloseAllClientConnections() markAllPeersWithSyncStatus(fm, blockchainPeers, blockchain.Synced) @@ -119,7 +130,7 @@ func TestClientHandler(t *testing.T) { cfg := config.Bx{WebsocketPort: 28332, ManageWSServer: true, WebsocketTLSEnabled: false} blockchainPeers, blockchainPeersInfo := test.GenerateBlockchainPeersInfo(3) - fm := NewFeedManager(context.Background(), g, feedChan, services.NewNoOpSubscriptionServices(), types.NetworkNum(1), 1, types.NodeID("nodeID"), eth.NewEthWSManager(blockchainPeersInfo, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), gwAccount, getMockCustomerAccountModel, "", "", cfg, stats, nil) + fm := NewFeedManager(context.Background(), g, feedChan, services.NewNoOpSubscriptionServices(), types.NetworkNum(1), 1, types.NodeID("nodeID"), eth.NewEthWSManager(blockchainPeersInfo, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), gwAccount, getMockCustomerAccountModel, "", "", cfg, stats, nil, nil) providers := fm.nodeWSManager.Providers() p1 := providers[blockchainPeers[0].IPPort()] assert.NotNil(t, p1) @@ -131,7 +142,7 @@ func TestClientHandler(t *testing.T) { var group errgroup.Group clientHandler := NewClientHandler(fm, nil, NewHTTPServer(fm, cfg.HTTPPort), log.WithFields(log.Fields{ "component": "gatewayClientHandler", - })) + }), nil) go clientHandler.ManageWSServer(cfg.ManageWSServer) go clientHandler.ManageHTTPServer(context.Background()) group.Go(fm.Start) @@ -142,12 +153,12 @@ func TestClientHandler(t *testing.T) { BscWsURLs := fmt.Sprintf("ws://%s/ws", urlBSC) blockchainPeersBSC, blockchainPeersInfoBSC := test.GenerateBlockchainPeersInfo(1) - fmBSC := NewFeedManager(context.Background(), g, feedChan, services.NewNoOpSubscriptionServices(), types.NetworkNum(1), 56, types.NodeID("nodeID"), eth.NewEthWSManager(blockchainPeersInfoBSC, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), gwAccount, getMockCustomerAccountModel, "", "", cfgBSC, stats, nil) + fmBSC := NewFeedManager(context.Background(), g, feedChan, services.NewNoOpSubscriptionServices(), types.NetworkNum(1), 56, types.NodeID("nodeID"), eth.NewEthWSManager(blockchainPeersInfoBSC, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), gwAccount, getMockCustomerAccountModel, "", "", cfgBSC, stats, nil, nil) p4 := providers[blockchainPeersBSC[0].IPPort()] assert.NotNil(t, p4) clientHandlerBSC := NewClientHandler(fmBSC, nil, NewHTTPServer(fmBSC, cfg.HTTPPort+1), log.WithFields(log.Fields{ "component": "gatewayClientHandlerBSC", - })) + }), getMockQuotaUsage) go clientHandlerBSC.ManageWSServer(false) go clientHandlerBSC.ManageHTTPServer(context.Background()) group.Go(fmBSC.Start) @@ -211,10 +222,10 @@ func TestClientHandler(t *testing.T) { testWSShutdown(t, fm, ws, blockchainPeers) }) // restart bc last test shut down ws server - fm = NewFeedManager(context.Background(), g, feedChan, services.NewNoOpSubscriptionServices(), types.NetworkNum(1), 1, types.NodeID("nodeID"), eth.NewEthWSManager(blockchainPeersInfo, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), gwAccount, getMockCustomerAccountModel, "", "", cfg, stats, nil) + fm = NewFeedManager(context.Background(), g, feedChan, services.NewNoOpSubscriptionServices(), types.NetworkNum(1), 1, types.NodeID("nodeID"), eth.NewEthWSManager(blockchainPeersInfo, eth.NewMockWSProvider, bxgateway.WSProviderTimeout), gwAccount, getMockCustomerAccountModel, "", "", cfg, stats, nil, nil) clientHandler = NewClientHandler(fm, nil, NewHTTPServer(fm, cfg.HTTPPort), log.WithFields(log.Fields{ "component": "gatewayClientHandler", - })) + }), getMockQuotaUsage) go clientHandler.ManageWSServer(cfg.ManageWSServer) go clientHandler.ManageHTTPServer(context.Background()) group.Go(fm.Start) @@ -696,6 +707,15 @@ func handlePingRequest(t *testing.T, ws *websocket.Conn) { assert.True(t, timeServerReceivesRequest.After(timeClientSendsRequest)) } +func handleQuotaUsageRequest(t *testing.T, ws *websocket.Conn) { + msg := writeMsgToWsAndReadResponse(t, ws, []byte(`{"id": "1", "method": "quota_usage"}`), nil) + clientRes := getClientResponse(t, msg) + res := parseQuotaUsage(t, clientRes.Result) + assert.Nil(t, "gw", res.AccountID) + assert.Nil(t, 1, res.QuotaFilled) + assert.Nil(t, 2, res.QuotaLimit) +} + func handleError(t *testing.T, ws *websocket.Conn, closeError *websocket.CloseError) { _ = writeMsgToWsAndReadResponse(t, ws, []byte(`{"id": "1", "method": "ping"}`), closeError) } @@ -873,6 +893,19 @@ func parsePingResult(t *testing.T, rpcResponse interface{}) (pr rpcPingResponse) return res } +func parseQuotaUsage(t *testing.T, rpcResponse interface{}) (qr connections.QuotaResponseBody) { + res := connections.QuotaResponseBody{ + AccountID: "account-id", + QuotaFilled: 1, + QuotaLimit: 2, + } + b, err := json.Marshal(rpcResponse) + assert.Nil(t, err) + err = json.Unmarshal(b, &res) + assert.Nil(t, err) + return res +} + func parseBlxrTxResult(t *testing.T, rpcResponse interface{}) (tr rpcTxResponse) { res := rpcTxResponse{} b, err := json.Marshal(rpcResponse) diff --git a/servers/feedmanager.go b/servers/feedmanager.go index 12c793f..7f04808 100644 --- a/servers/feedmanager.go +++ b/servers/feedmanager.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "github.com/bloXroute-Labs/gateway/v2/bxmessage" + "github.com/bloXroute-Labs/gateway/v2" "github.com/bloXroute-Labs/gateway/v2/blockchain" "github.com/bloXroute-Labs/gateway/v2/config" @@ -18,10 +20,15 @@ import ( "github.com/bloXroute-Labs/gateway/v2/services/statistics" "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils/orderedmap" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" uuid "github.com/satori/go.uuid" "github.com/sourcegraph/jsonrpc2" ) +const ( + pendingNextValidatorListInitCapacity = 100 +) + // ClientSubscription contains client subscription feed and websocket connection type ClientSubscription struct { feed chan *types.Notification @@ -47,24 +54,35 @@ type ClientSubscriptionHandlingInfo struct { PermissionRespChan chan *sdnmessage.SubscriptionPermissionMessage } +// PendingNextValidatorTxInfo holds info needed to reevaluate next validator tx when next block published +type PendingNextValidatorTxInfo struct { + Tx *bxmessage.Tx + Fallback uint16 + TimeOfRequest time.Time + Source connections.Conn +} + // FeedManager - feed manager fields type FeedManager struct { - feedChan chan types.Notification - idToClientSubscription map[uuid.UUID]ClientSubscription - subscriptionServices services.SubscriptionServices - lock sync.RWMutex - node connections.BxListener - networkNum types.NetworkNum - chainID types.NetworkID - nodeID types.NodeID - nodeWSManager blockchain.WSManager - accountModel sdnmessage.Account - getCustomerAccountModel func(types.AccountID) (sdnmessage.Account, error) - certFile string - keyFile string - cfg config.Bx - log *log.Entry - nextValidatorMap *orderedmap.OrderedMap + feedChan chan types.Notification + idToClientSubscription map[uuid.UUID]ClientSubscription + subscriptionServices services.SubscriptionServices + lock sync.RWMutex + node connections.BxListener + networkNum types.NetworkNum + chainID types.NetworkID + nodeID types.NodeID + nodeWSManager blockchain.WSManager + accountModel sdnmessage.Account + getCustomerAccountModel func(types.AccountID) (sdnmessage.Account, error) + certFile string + keyFile string + cfg config.Bx + log *log.Entry + nextValidatorMap *orderedmap.OrderedMap + validatorStatusMap *syncmap.SyncMap[string, bool] + pendingBSCNextValidatorTxHashToInfo map[string]PendingNextValidatorTxInfo + pendingBSCNextValidatorTxsMapLock sync.Mutex context context.Context cancel context.CancelFunc @@ -77,31 +95,34 @@ func NewFeedManager(parent context.Context, node connections.BxListener, feedCha networkNum types.NetworkNum, networkID types.NetworkID, nodeID types.NodeID, wsManager blockchain.WSManager, accountModel sdnmessage.Account, getCustomerAccountModel func(types.AccountID) (sdnmessage.Account, error), - certFile string, keyFile string, cfg config.Bx, stats statistics.Stats, nextValidatorMap *orderedmap.OrderedMap) *FeedManager { + certFile string, keyFile string, cfg config.Bx, stats statistics.Stats, + nextValidatorMap *orderedmap.OrderedMap, validatorStatusMap *syncmap.SyncMap[string, bool]) *FeedManager { ctx, cancel := context.WithCancel(parent) logger := log.WithFields(log.Fields{ "component": "feedManager", }) newServer := &FeedManager{ - feedChan: feedChan, - idToClientSubscription: make(map[uuid.UUID]ClientSubscription), - subscriptionServices: subscriptionServices, - node: node, - networkNum: networkNum, - chainID: networkID, - nodeID: nodeID, - nodeWSManager: wsManager, - accountModel: accountModel, - getCustomerAccountModel: getCustomerAccountModel, - nextValidatorMap: nextValidatorMap, - certFile: certFile, - keyFile: keyFile, - cfg: cfg, - context: ctx, - cancel: cancel, - stats: stats, - log: logger, + feedChan: feedChan, + idToClientSubscription: make(map[uuid.UUID]ClientSubscription), + subscriptionServices: subscriptionServices, + node: node, + networkNum: networkNum, + chainID: networkID, + nodeID: nodeID, + nodeWSManager: wsManager, + accountModel: accountModel, + getCustomerAccountModel: getCustomerAccountModel, + nextValidatorMap: nextValidatorMap, + validatorStatusMap: validatorStatusMap, + certFile: certFile, + keyFile: keyFile, + cfg: cfg, + context: ctx, + cancel: cancel, + stats: stats, + log: logger, + pendingBSCNextValidatorTxHashToInfo: make(map[string]PendingNextValidatorTxInfo), } return newServer } @@ -380,3 +401,18 @@ func (f *FeedManager) GetAllSubscriptions() []sdnmessage.SubscriptionModel { } return subscriptionModels } + +// GetPendingNextValidatorTxs returns map of pending next validator transactions +func (f *FeedManager) GetPendingNextValidatorTxs() map[string]PendingNextValidatorTxInfo { + return f.pendingBSCNextValidatorTxHashToInfo +} + +// LockPendingNextValidatorTxs activates mutex lock for pendingBSCNextValidatorTxHashToInfo map +func (f *FeedManager) LockPendingNextValidatorTxs() { + f.pendingBSCNextValidatorTxsMapLock.Lock() +} + +// UnlockPendingNextValidatorTxs activates mutex lock for pendingBSCNextValidatorTxHashToInfo map +func (f *FeedManager) UnlockPendingNextValidatorTxs() { + f.pendingBSCNextValidatorTxsMapLock.Unlock() +} diff --git a/services/account_burst_limiter.go b/services/account_burst_limiter.go index 2792b2d..cf246db 100644 --- a/services/account_burst_limiter.go +++ b/services/account_burst_limiter.go @@ -1,11 +1,12 @@ package services import ( + "time" + "github.com/bloXroute-Labs/gateway/v2/sdnmessage" "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" - cmap "github.com/orcaman/concurrent-map" - "time" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" ) // AccountBurstLimiter represents a service for managing burst limiters for accounts @@ -21,14 +22,14 @@ type AccountBurstLimiter interface { // NewAccountBurstLimiter returns a service for managing burst limiters for accounts with leaky bucket burst limiters func NewAccountBurstLimiter(clock utils.Clock) AccountBurstLimiter { return &leakyBucketAccountBurstLimiter{ - accountToLimiter: cmap.New(), + accountToLimiter: syncmap.NewTypedMapOf[types.AccountID, accountLimiter](syncmap.AccountIDHasher), clock: clock, totalExcess: NewRateSnapshot(clock), } } type leakyBucketAccountBurstLimiter struct { - accountToLimiter cmap.ConcurrentMap + accountToLimiter *syncmap.SyncMap[types.AccountID, accountLimiter] clock utils.Clock totalExcess RateSnapshot } @@ -76,7 +77,7 @@ func (l *leakyBucketAccountBurstLimiter) Register(account *sdnmessage.Account) { paidBurstLimit := account.PaidTransactionBurstLimit.MsgQuota.Limit paidBehavior := account.PaidTransactionBurstLimit.MsgQuota.BehaviorLimitFail - l.accountToLimiter.Set(string(account.AccountID), accountLimiter{ + l.accountToLimiter.Store(account.AccountID, accountLimiter{ unpaidBurstLimiter: utils.NewLeakyBucketRateLimiter(l.clock, uint64(unpaidBurstLimit), 5*time.Second), unpaidBehavior: unpaidBehavior, paidBurstLimiter: utils.NewLeakyBucketRateLimiter(l.clock, uint64(paidBurstLimit), 5*time.Second), @@ -137,11 +138,11 @@ func (l *leakyBucketAccountBurstLimiter) limitBehavior(id types.AccountID, paid } func (l *leakyBucketAccountBurstLimiter) accountLimiter(id types.AccountID) (accountLimiter, bool) { - rawAccountLimiter, ok := l.accountToLimiter.Get(string(id)) + rawAccountLimiter, ok := l.accountToLimiter.Load(id) if !ok { return accountLimiter{}, false } - return rawAccountLimiter.(accountLimiter), true + return rawAccountLimiter, true } func (al *accountLimiter) count(paid bool) { diff --git a/services/bxtxstore.go b/services/bxtxstore.go index 8fc9e3e..59536b6 100644 --- a/services/bxtxstore.go +++ b/services/bxtxstore.go @@ -3,23 +3,24 @@ package services import ( "encoding/hex" "fmt" + "runtime/debug" + "sort" + "sync" + "time" + "github.com/bloXroute-Labs/gateway/v2" log "github.com/bloXroute-Labs/gateway/v2/logger" pbbase "github.com/bloXroute-Labs/gateway/v2/protobuf" "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" - "github.com/orcaman/concurrent-map" - "runtime/debug" - "sort" - "sync" - "time" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" ) // BxTxStore represents the storage of transaction info for a given node type BxTxStore struct { clock utils.Clock - hashToContent cmap.ConcurrentMap - shortIDToHash cmap.ConcurrentMap + hashToContent *syncmap.SyncMap[string, *types.BxTransaction] + shortIDToHash *syncmap.SyncMap[types.ShortID, types.SHA256Hash] seenTxs HashHistory timeToAvoidReEntry time.Duration @@ -46,8 +47,8 @@ func newBxTxStore(clock utils.Clock, cleanupFreq time.Duration, maxTxAge time.Du timeToAvoidReEntry time.Duration, bloom BloomFilter) BxTxStore { return BxTxStore{ clock: clock, - hashToContent: cmap.New(), - shortIDToHash: cmap.New(), + hashToContent: syncmap.NewStringMapOf[*types.BxTransaction](), + shortIDToHash: syncmap.NewIntegerMapOf[types.ShortID, types.SHA256Hash](), seenTxs: seenTxs, timeToAvoidReEntry: timeToAvoidReEntry, cleanupFreq: cleanupFreq, @@ -81,15 +82,15 @@ func (t *BxTxStore) Clear() { // Count indicates the number of stored transaction in BxTxStore func (t *BxTxStore) Count() int { - return t.hashToContent.Count() + return t.hashToContent.Size() } // remove deletes a single transaction, including its shortIDs func (t *BxTxStore) remove(hash string, reEntryProtection ReEntryProtectionFlags, reason string) { - if tx, ok := t.hashToContent.Pop(hash); ok { - bxTransaction := tx.(*types.BxTransaction) + if tx, ok := t.hashToContent.LoadAndDelete(hash); ok { + bxTransaction := tx for _, shortID := range bxTransaction.ShortIDs() { - t.shortIDToHash.Remove(fmt.Sprint(shortID)) + t.shortIDToHash.Delete(shortID) } // if asked, add the hash to the history map so we remember this transaction for some time // and prevent if from being added back to the TxStore @@ -114,9 +115,8 @@ func (t *BxTxStore) RemoveShortIDs(shortIDs *types.ShortIDList, reEntryProtectio // note - it is OK for hashesToRemove to hold the same hash multiple times. hashesToRemove := make(types.SHA256HashList, 0) for _, shortID := range *shortIDs { - strShortID := fmt.Sprint(shortID) - if hash, ok := t.shortIDToHash.Get(strShortID); ok { - hashesToRemove = append(hashesToRemove, hash.(types.SHA256Hash)) + if hash, ok := t.shortIDToHash.Load(shortID); ok { + hashesToRemove = append(hashesToRemove, hash) } } t.RemoveHashes(&hashesToRemove, reEntryProtection, reason) @@ -124,10 +124,9 @@ func (t *BxTxStore) RemoveShortIDs(shortIDs *types.ShortIDList, reEntryProtectio // GetTxByShortID lookup a transaction by its shortID. return error if not found func (t *BxTxStore) GetTxByShortID(shortID types.ShortID) (*types.BxTransaction, error) { - if h, ok := t.shortIDToHash.Get(fmt.Sprint(shortID)); ok { - hash := h.(types.SHA256Hash) - if tx, exists := t.hashToContent.Get(string(hash[:])); exists { - return tx.(*types.BxTransaction), nil + if hash, ok := t.shortIDToHash.Load(shortID); ok { + if tx, exists := t.hashToContent.Load(string(hash[:])); exists { + return tx, nil } return nil, fmt.Errorf("transaction content for shortID %v and hash %v does not exist", shortID, hash) } @@ -145,12 +144,14 @@ func (t *BxTxStore) RemoveHashes(hashes *types.SHA256HashList, reEntryProtection func (t *BxTxStore) Iter() (iter <-chan *types.BxTransaction) { newChan := make(chan *types.BxTransaction) go func() { - for elem := range t.hashToContent.IterBuffered() { - tx := elem.Val.(*types.BxTransaction) - if t.clock.Now().Sub(tx.AddTime()) < t.maxTxAge { - newChan <- tx + + t.hashToContent.Range(func(key string, bxTransaction *types.BxTransaction) bool { + if t.clock.Now().Sub(bxTransaction.AddTime()) < t.maxTxAge { + newChan <- bxTransaction } - } + return true + }) + close(newChan) }() return newChan @@ -180,23 +181,26 @@ func (t *BxTxStore) Add(hash types.SHA256Hash, content types.TxContent, shortID t.seenTxs.Remove(hashStr) } else { result.Transaction = types.NewBxTransaction(hash, networkNum, flags, timestamp) - result.DebugData = fmt.Sprintf("Transaction already seen and deleted from store") + result.DebugData = "Transaction already seen and deleted from store" result.AlreadySeen = true return result } } bxTransaction := types.NewBxTransaction(hash, networkNum, flags, timestamp) - if result.NewTx = t.hashToContent.SetIfAbsent(hashStr, bxTransaction); !result.NewTx { - tx, exists := t.hashToContent.Get(hashStr) + + _, exists := t.hashToContent.LoadOrStore(hashStr, bxTransaction) + + if result.NewTx = !exists; !result.NewTx { + tx, exists := t.hashToContent.Load(hashStr) if !exists { log.Warnf("couldn't Get an existing transaction %v, network %v, flags %v, shortID %v, content %v", hash, networkNum, flags, shortID, hex.EncodeToString(content[:])) result.Transaction = bxTransaction - result.DebugData = fmt.Sprintf("Transaction deleted by other GO routine") + result.DebugData = "Transaction deleted by other GO routine" return result } - bxTransaction = tx.(*types.BxTransaction) + bxTransaction = tx } // make sure we are the only process that makes changes to the transaction @@ -236,7 +240,7 @@ func (t *BxTxStore) Add(hash types.SHA256Hash, content types.TxContent, shortID bxTransaction.Unlock() if result.NewSID { - t.shortIDToHash.Set(fmt.Sprint(shortID), bxTransaction.Hash()) + t.shortIDToHash.Store(shortID, bxTransaction.Hash()) } return result @@ -255,8 +259,7 @@ func (t *BxTxStore) clean() (cleaned int, cleanedShortIDs types.ShortIDsByNetwor var networks = make(map[types.NetworkNum]*networkData) cleanedShortIDs = make(types.ShortIDsByNetwork) - for item := range t.hashToContent.IterBuffered() { - bxTransaction := item.Val.(*types.BxTransaction) + t.hashToContent.Range(func(key string, bxTransaction *types.BxTransaction) bool { netData, netDataExists := networks[bxTransaction.NetworkNum()] if !netDataExists { netData = &networkData{} @@ -264,7 +267,9 @@ func (t *BxTxStore) clean() (cleaned int, cleanedShortIDs types.ShortIDsByNetwor } txAge := int(currTime.Sub(bxTransaction.AddTime()) / time.Second) networks[bxTransaction.NetworkNum()].ages = append(networks[bxTransaction.NetworkNum()].ages, txAge) - } + + return true + }) for net, netData := range networks { // if we are below the number of allowed Txs, no need to do anything @@ -283,8 +288,8 @@ func (t *BxTxStore) clean() (cleaned int, cleanedShortIDs types.ShortIDsByNetwor net, len(netData.ages), len(netData.ages)-bxgateway.TxStoreMaxSize, networks[net].maxAge) } - for item := range t.hashToContent.IterBuffered() { - bxTransaction := item.Val.(*types.BxTransaction) + t.hashToContent.Range(func(key string, bxTransaction *types.BxTransaction) bool { + networkNum := bxTransaction.NetworkNum() netData, netDataExists := networks[networkNum] removeReason := "" @@ -304,10 +309,12 @@ func (t *BxTxStore) clean() (cleaned int, cleanedShortIDs types.ShortIDsByNetwor // remove the transaction by hash from both maps // no need to add the hash to the history as it is deleted after long time // dec-5-2021: add to hash history to prevent a lot of reentry (BSC, Polygon) - t.remove(item.Key, FullReEntryProtection, removeReason) + t.remove(key, FullReEntryProtection, removeReason) cleanedShortIDs[networkNum] = append(cleanedShortIDs[networkNum], bxTransaction.ShortIDs()...) } - } + + return true + }) for net, netData := range networks { log.Debugf("TxStore network %v #txs before cleanup %v cleaned %v missing SID entries and %v aged entries", @@ -350,11 +357,11 @@ func (t *BxTxStore) Get(hash types.SHA256Hash) (*types.BxTransaction, bool) { if t.refreshSeenTx(hash) { return nil, false } - tx, ok := t.hashToContent.Get(string(hash[:])) + tx, ok := t.hashToContent.Load(string(hash[:])) if !ok { return nil, ok } - return tx.(*types.BxTransaction), ok + return tx, ok } // Known returns whether if a tx hash is in seenTx @@ -375,15 +382,12 @@ func (t *BxTxStore) HasContent(hash types.SHA256Hash) bool { func (t *BxTxStore) Summarize() *pbbase.TxStoreReply { networks := make(map[types.NetworkNum]*pbbase.TxStoreNetworkData) res := pbbase.TxStoreReply{ - TxCount: uint64(t.hashToContent.Count()), - ShortIdCount: uint64(t.shortIDToHash.Count()), + TxCount: uint64(t.hashToContent.Size()), + ShortIdCount: uint64(t.shortIDToHash.Size()), } - for item := range t.hashToContent.IterBuffered() { - bxTransaction, ok := item.Val.(*types.BxTransaction) - if !ok { - continue - } + t.hashToContent.Range(func(key string, bxTransaction *types.BxTransaction) bool { + networkData, exists := networks[bxTransaction.NetworkNum()] if !exists { networkData = &pbbase.TxStoreNetworkData{} @@ -393,7 +397,8 @@ func (t *BxTxStore) Summarize() *pbbase.TxStoreReply { networkData.ShortIdCount += uint64(len(bxTransaction.ShortIDs())) networks[bxTransaction.NetworkNum()] = networkData - continue + // continue iteration + return true } oldestTx := networkData.OldestTx oldestTxTS := oldestTx.AddTime @@ -402,7 +407,10 @@ func (t *BxTxStore) Summarize() *pbbase.TxStoreReply { } networkData.TxCount++ networkData.ShortIdCount += uint64(len(bxTransaction.ShortIDs())) - } + + return true + }) + for _, netData := range networks { res.NetworkData = append(res.NetworkData, netData) } diff --git a/services/bxtxstore_test.go b/services/bxtxstore_test.go index 056a4e6..f4d59a1 100644 --- a/services/bxtxstore_test.go +++ b/services/bxtxstore_test.go @@ -186,14 +186,14 @@ func TestBxTxStore_clean256K(t *testing.T) { clock.IncTime(time.Second) } assert.Equal(t, count+otherNetworkTxs, store.Count()) - assert.Equal(t, count+otherNetworkTxs, store.hashToContent.Count()) - assert.Equal(t, count+otherNetworkTxs, store.shortIDToHash.Count()) + assert.Equal(t, count+otherNetworkTxs, store.hashToContent.Size()) + assert.Equal(t, count+otherNetworkTxs, store.shortIDToHash.Size()) cleaned, cleanedShortIDs := store.clean() assert.Equal(t, bxgateway.TxStoreMaxSize*0.9+otherNetworkTxs, store.Count()) assert.Equal(t, bxgateway.TxStoreMaxSize*0.1+extra, len(cleanedShortIDs[testNetworkNum])) - assert.Equal(t, bxgateway.TxStoreMaxSize*0.9+otherNetworkTxs, store.hashToContent.Count()) - assert.Equal(t, bxgateway.TxStoreMaxSize*0.9+otherNetworkTxs, store.shortIDToHash.Count()) + assert.Equal(t, bxgateway.TxStoreMaxSize*0.9+otherNetworkTxs, store.hashToContent.Size()) + assert.Equal(t, bxgateway.TxStoreMaxSize*0.9+otherNetworkTxs, store.shortIDToHash.Size()) assert.Equal(t, bxgateway.TxStoreMaxSize*0.1+extra, store.seenTxs.Count()) assert.Equal(t, bxgateway.TxStoreMaxSize*0.1+extra, cleaned) @@ -329,7 +329,7 @@ func TestBxTxStore_ResetSeenTxTime(t *testing.T) { store := newBxTxStore(&clock, 30*time.Second, 30*time.Second, 10*time.Second, NewEmptyShortIDAssigner(), newHashHistory("seenTxs", &clock, 60*time.Minute), cleanedShortIDsChan, 30*time.Second, NoOpBloomFilter{}) // Case 1: - // Base case, add hash1 to TxStore and wait for TxStore to clean up, so hash1 is stored in SeenTx + // ConnDetails case, add hash1 to TxStore and wait for TxStore to clean up, so hash1 is stored in SeenTx // without calling Add() or Get() to reset the hash1 in seenTx, hash1 will expire after cleanupFreq which is 30 seconds hash1 := types.SHA256Hash{1} diff --git a/services/ethtxstore.go b/services/ethtxstore.go index 85e1842..43d907e 100644 --- a/services/ethtxstore.go +++ b/services/ethtxstore.go @@ -2,16 +2,17 @@ package services import ( "fmt" + "math/big" + "strconv" + "strings" + "time" + log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/bloXroute-Labs/gateway/v2/sdnmessage" "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" "github.com/ethereum/go-ethereum/common" - cmap "github.com/orcaman/concurrent-map" - "math/big" - "strconv" - "strings" - "time" ) // TODO : move ethtxstore and related tests outside of bxgateway package @@ -65,7 +66,7 @@ func (t *EthTxStore) add(hash types.SHA256Hash, content types.TxContent, shortID if validate && !t.HasContent(hash) { // If validate is true we got the tx from gw or cloud-api (with content). - // If we don't know this hash or we don't have its content we we should validate + // If we don't know this hash, or we don't have its content we should validate // it and extract the sender (so we pass EmptySender) transaction.SetContent(content) blockchainTx, err = transaction.BlockchainTransaction(types.EmptySender) @@ -83,6 +84,8 @@ func (t *EthTxStore) add(hash types.SHA256Hash, content types.TxContent, shortID return result } copy(sender[:], ethTx.From.Bytes()) + } else { + log.Tracef("adding transaction %v with sender %v", hash, sender) } result = t.BxTxStore.Add(hash, content, shortID, network, false, transaction.Flags(), timestamp, networkChainID, sender) @@ -138,7 +141,7 @@ type trackedTx struct { type nonceTracker struct { clock utils.Clock - addressNonceToTx cmap.ConcurrentMap + addressNonceToTx *syncmap.SyncMap[string, trackedTx] cleanInterval time.Duration networkConfig sdnmessage.BlockchainNetworks quit chan bool @@ -156,7 +159,7 @@ func newNonceTracker(clock utils.Clock, networkConfig sdnmessage.BlockchainNetwo nt := nonceTracker{ clock: clock, networkConfig: networkConfig, - addressNonceToTx: cmap.New(), + addressNonceToTx: syncmap.NewStringMapOf[trackedTx](), cleanInterval: cleanInterval, quit: make(chan bool), } @@ -166,11 +169,11 @@ func newNonceTracker(clock utils.Clock, networkConfig sdnmessage.BlockchainNetwo func (nt *nonceTracker) getTransaction(from *common.Address, nonce uint64) (*trackedTx, bool) { k := fromNonceKey(from, nonce) - utx, ok := nt.addressNonceToTx.Get(k) + utx, ok := nt.addressNonceToTx.Load(k) if !ok { return nil, ok } - tx := utx.(trackedTx) + tx := utx return &tx, ok } @@ -192,7 +195,7 @@ func (nt *nonceTracker) setTransaction(tx *types.EthTransaction, network types.N gasFeeCap: intGasFeeCap, gasTipCap: intGasTipCap, } - nt.addressNonceToTx.Set(fromNonceKey(tx.From, tx.Nonce), tracked) + nt.addressNonceToTx.Store(fromNonceKey(tx.From, tx.Nonce), tracked) } // isReuseNonceActive returns whether reuse nonce tracking is active @@ -232,14 +235,16 @@ func (nt *nonceTracker) cleanLoop() { func (nt *nonceTracker) clean() { currentTime := nt.clock.Now() - sizeBefore := nt.addressNonceToTx.Count() + sizeBefore := nt.addressNonceToTx.Size() removed := 0 - for item := range nt.addressNonceToTx.IterBuffered() { - tracked := item.Val.(trackedTx) + + nt.addressNonceToTx.Range(func(key string, tracked trackedTx) bool { if currentTime.After(tracked.expireTime) { - nt.addressNonceToTx.Remove(item.Key) + nt.addressNonceToTx.Delete(key) removed++ } - } + return true + }) + log.Tracef("nonceTracker Cleanup done. Size at start %v, cleaned %v", sizeBefore, removed) } diff --git a/services/firewall_test.go b/services/firewall_test.go index 270c109..362fa9d 100644 --- a/services/firewall_test.go +++ b/services/firewall_test.go @@ -4,7 +4,6 @@ import ( "github.com/bloXroute-Labs/gateway/v2/sdnmessage" "github.com/bloXroute-Labs/gateway/v2/types" "github.com/bloXroute-Labs/gateway/v2/utils" - uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" "testing" "time" @@ -146,13 +145,13 @@ func TestFirewall_ConnectionAllowed(t *testing.T) { } func generateRandAccountID() types.AccountID { - id := uuid.NewV1() + id := utils.GenerateUUID() accountID := types.AccountID(id.String()) return accountID } func generateRandNodeID() types.NodeID { - id := uuid.NewV1() + id := utils.GenerateUUID() nodeID := types.NodeID(id.String()) return nodeID } diff --git a/services/hashhistory.go b/services/hashhistory.go index b5e67e1..1443a2b 100644 --- a/services/hashhistory.go +++ b/services/hashhistory.go @@ -1,10 +1,11 @@ package services import ( + "time" + log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/bloXroute-Labs/gateway/v2/utils" - cmap "github.com/orcaman/concurrent-map" - "time" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" ) // HashHistory holds hashes that we have seen in the past @@ -12,7 +13,7 @@ type HashHistory struct { name string // for logging clock utils.Clock cleanupFreq time.Duration - data cmap.ConcurrentMap + data *syncmap.SyncMap[string, int64] } // NewHashHistory creates a new object @@ -25,7 +26,7 @@ func newHashHistory(name string, clock utils.Clock, cleanupFreq time.Duration) H name: name, clock: clock, cleanupFreq: cleanupFreq, - data: cmap.New(), + data: syncmap.NewStringMapOf[int64](), } go hh.cleanup() return hh @@ -33,23 +34,25 @@ func newHashHistory(name string, clock utils.Clock, cleanupFreq time.Duration) H // Add adds the hash for the duration func (hh HashHistory) Add(hash string, expiration time.Duration) { - hh.data.Set(hash, hh.clock.Now().Add(expiration).UnixNano()) + hh.data.Store(hash, hh.clock.Now().Add(expiration).UnixNano()) } // Remove removes the hash from the data func (hh HashHistory) Remove(hash string) { - hh.data.Remove(hash) + hh.data.Delete(hash) } // SetIfAbsent Sets the given value under the specified key if no value was associated with it. func (hh HashHistory) SetIfAbsent(hash string, expiration time.Duration) bool { - return hh.data.SetIfAbsent(hash, hh.clock.Now().Add(expiration).UnixNano()) + _, exists := hh.data.LoadOrStore(hash, hh.clock.Now().Add(expiration).UnixNano()) + + return !exists } // Exists checks if hash is in history func (hh HashHistory) Exists(hash string) bool { - if val, ok := hh.data.Get(hash); ok { - expiration := val.(int64) + if val, ok := hh.data.Load(hash); ok { + expiration := val if hh.clock.Now().UnixNano() < expiration { return true } @@ -59,7 +62,7 @@ func (hh HashHistory) Exists(hash string) bool { // Count provides the size of the history func (hh HashHistory) Count() int { - return hh.data.Count() + return hh.data.Size() } func (hh HashHistory) cleanup() { @@ -76,12 +79,14 @@ func (hh HashHistory) cleanup() { func (hh HashHistory) clean() int { historyCleaned := 0 timeNow := hh.clock.Now().UnixNano() - for item := range hh.data.IterBuffered() { - expiration := item.Val.(int64) + + hh.data.Range(func(key string, expiration int64) bool { if timeNow > expiration { - hh.data.Remove(item.Key) + hh.data.Delete(key) historyCleaned++ } - } + return true + }) + return historyCleaned } diff --git a/services/loggers/txtracelogger.go b/services/loggers/txtracelogger.go index 7d95f4f..a2fc316 100644 --- a/services/loggers/txtracelogger.go +++ b/services/loggers/txtracelogger.go @@ -32,10 +32,10 @@ func NewTxTrace(txTraceLogger *log.Logger) TxTrace { func (tt txTrace) Log(hash types.SHA256Hash, source connections.Conn) { var sourceName string - if source.Info().ConnectionType == utils.Blockchain { + if source.GetConnectionType() == utils.Blockchain { sourceName = "Blockchain" } else { sourceName = "BDN" } - tt.logger.Tracef("%v - %v %v", hash, sourceName, source.Info().PeerIP) + tt.logger.Tracef("%v - %v %v", hash, sourceName, source.GetPeerIP()) } diff --git a/services/statistics/fluentdstats.go b/services/statistics/fluentdstats.go index 9aaf23a..3679d73 100644 --- a/services/statistics/fluentdstats.go +++ b/services/statistics/fluentdstats.go @@ -63,12 +63,12 @@ func (NoStats) AddTxsByShortIDsEvent(name string, source connections.Conn, txInf startTime time.Time, priority bxmessage.SendPriority, debugData interface{}) { } -//LogSubscribeStats does nothing +// LogSubscribeStats does nothing func (NoStats) LogSubscribeStats(subscriptionID *uuid.UUID, accountID types.AccountID, feedName types.FeedType, tierName sdnmessage.AccountTier, ip string, networkNum types.NetworkNum, feedInclude []string, feedFilter string, feedProject string) { } -//LogUnsubscribeStats does nothing +// LogUnsubscribeStats does nothing func (NoStats) LogUnsubscribeStats(subscriptionID *uuid.UUID, feedName types.FeedType, networkNum types.NetworkNum, accountID types.AccountID, tierName sdnmessage.AccountTier) { } @@ -92,7 +92,8 @@ func (s FluentdStats) AddTxsByShortIDsEvent(name string, source connections.Conn now := time.Now() logic := EventLogicNone - notFromBDN := source.Info().IsGateway() || source.Info().IsCloudAPI() + connectionType := source.GetConnectionType() + notFromBDN := connections.IsGateway(connectionType) || connections.IsCloudAPI(connectionType) switch { // if from external source case name == "TxProcessedByRelayProxyFromPeer" && notFromBDN: @@ -113,7 +114,7 @@ func (s FluentdStats) AddTxsByShortIDsEvent(name string, source connections.Conn StartDateTime: startTime.Format(DateFormat), SentGatewayPeers: sentGatewayPeers, ExtraData: txExtraData{ - MoreInfo: fmt.Sprintf("source: %v - %v, priority %v, sent: %v (%v), duration: %v, debug data: %v", source, source.Info().ConnectionType.FormatShortNodeType(), priority, sentPeers, sentGatewayPeers, now.Sub(startTime), debugData), + MoreInfo: fmt.Sprintf("source: %v - %v, priority %v, sent: %v (%v), duration: %v, debug data: %v", source, connectionType.FormatShortNodeType(), priority, sentPeers, sentGatewayPeers, now.Sub(startTime), debugData), ShortID: shortID, NetworkNum: txInfo.NetworkNum(), SourceID: sourceID, @@ -140,12 +141,12 @@ func (s FluentdStats) AddBlockEvent(name string, source connections.Conn, blockH NodeID: s.NodeID, EventName: name, NetworkNum: networkNum, - SourceID: source.Info().NodeID, + SourceID: source.GetNodeID(), StartDateTime: startTime.Format(DateFormat), EndDateTime: now.Format(DateFormat), SentGatewayPeers: sentGatewayPeers, ExtraData: blockExtraData{ - MoreInfo: fmt.Sprintf("source: %v - %v, sent: %v", source, source.Info().ConnectionType.FormatShortNodeType(), sentPeers), + MoreInfo: fmt.Sprintf("source: %v - %v, sent: %v", source, source.GetConnectionType().FormatShortNodeType(), sentPeers), }, BeaconBlockHash: beaconBlockHash.String(), }, @@ -166,12 +167,12 @@ func (s FluentdStats) AddGatewayBlockEvent(name string, source connections.Conn, NodeID: s.NodeID, EventName: name, NetworkNum: networkNum, - SourceID: source.Info().NodeID, + SourceID: source.GetNodeID(), StartDateTime: startTime.Format(DateFormat), EndDateTime: now.Format(DateFormat), SentGatewayPeers: sentGatewayPeers, ExtraData: blockExtraData{ - MoreInfo: fmt.Sprintf("source: %v - %v, sent: %v", source, source.Info().ConnectionType.FormatShortNodeType(), sentPeers), + MoreInfo: fmt.Sprintf("source: %v - %v, sent: %v", source, source.GetConnectionType().FormatShortNodeType(), sentPeers), }, OriginalSize: originalSize, CompressSize: compressSize, diff --git a/services/subscriptionservices.go b/services/subscriptionservices.go index 5d4011a..aea02ae 100644 --- a/services/subscriptionservices.go +++ b/services/subscriptionservices.go @@ -2,6 +2,7 @@ package services import ( "github.com/bloXroute-Labs/gateway/v2/sdnmessage" + baseutils "github.com/bloXroute-Labs/gateway/v2/utils" uuid "github.com/satori/go.uuid" ) @@ -38,7 +39,7 @@ func (n NoOpSubscriptionServices) SendSubscriptionResetNotification([]sdnmessage return } -// GenerateSubscriptionID generates random subscription ID +// GenerateSubscriptionID generate uuid func (n NoOpSubscriptionServices) GenerateSubscriptionID() uuid.UUID { - return uuid.NewV4() + return baseutils.GenerateUUID() } diff --git a/test/bxmock/beaconblock.go b/test/bxmock/beaconblock.go index e361770..dcd89c5 100644 --- a/test/bxmock/beaconblock.go +++ b/test/bxmock/beaconblock.go @@ -2,16 +2,16 @@ package bxmock import ( "encoding/hex" + "github.com/prysmaticlabs/go-bitfield" "strings" "testing" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/prysmaticlabs/go-bitfield" - "github.com/prysmaticlabs/prysm/v3/config/params" - "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/config/params" + "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/stretchr/testify/assert" ) diff --git a/test/sdnhttpmock/mock_sdnhttp.go b/test/sdnhttpmock/mock_sdnhttp.go index be78748..4407dca 100644 --- a/test/sdnhttpmock/mock_sdnhttp.go +++ b/test/sdnhttpmock/mock_sdnhttp.go @@ -46,6 +46,15 @@ func (m *MockSDNHTTP) AccountModel() sdnmessage.Account { return ret0 } +func (m *MockSDNHTTP) GetQuotaUsage(accountID string) (*connections.QuotaResponseBody, error) { + res := connections.QuotaResponseBody{ + AccountID: accountID, + QuotaFilled: 1, + QuotaLimit: 2, + } + return &res, nil +} + // AccountModel indicates an expected call of AccountModel. func (mr *MockSDNHTTPMockRecorder) AccountModel() *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/types/blocknotification.go b/types/blocknotification.go index 1f160d1..e607240 100644 --- a/types/blocknotification.go +++ b/types/blocknotification.go @@ -8,9 +8,9 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" - ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v3/runtime/version" + "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/runtime/version" ) // NewBeaconBlockNotification creates beacon block notification diff --git a/types/bxtransaction_test.go b/types/bxtransaction_test.go index 87cb2c4..a4eea9e 100644 --- a/types/bxtransaction_test.go +++ b/types/bxtransaction_test.go @@ -7,12 +7,12 @@ import ( "time" ) -//example of real tx -//{"jsonrpc":"2.0","id":null,"method":"subscribe","params":{"subscription":"65770230-8550-46cd-8059-4f6e13484c83", -//"result":{"txContents":{"from":"0x832f166799a407275500430b61b622f0058f15d6","gas":"0x13880","gasPrice":"0x1bf08eb000", -//"hash":"0xed2b4580a766bc9d81c73c35a8496f0461e9c261621cb9f4565ae52ade56056d","input":"0x","nonce":"0x1b7f8", -//"value":"0x50b32f902486000","v":"0x1c","r":"0xaa803263146bda76a58ebf9f54be589280e920616bc57e7bd68248821f46fd0c", -//"s":"0x40266f84a2ecd4719057b0633cc80e3e0b3666f6f6ec1890a920239634ec6531","to":"0xb877c7e556d50b0027053336b90f36becf67b3dd"}}}} +// example of real tx +// {"jsonrpc":"2.0","id":null,"method":"subscribe","params":{"subscription":"65770230-8550-46cd-8059-4f6e13484c83", +// "result":{"txContents":{"from":"0x832f166799a407275500430b61b622f0058f15d6","gas":"0x13880","gasPrice":"0x1bf08eb000", +// "hash":"0xed2b4580a766bc9d81c73c35a8496f0461e9c261621cb9f4565ae52ade56056d","input":"0x","nonce":"0x1b7f8", +// "value":"0x50b32f902486000","v":"0x1c","r":"0xaa803263146bda76a58ebf9f54be589280e920616bc57e7bd68248821f46fd0c", +// "s":"0x40266f84a2ecd4719057b0633cc80e3e0b3666f6f6ec1890a920239634ec6531","to":"0xb877c7e556d50b0027053336b90f36becf67b3dd"}}}} var testNetworkNum = NetworkNum(5) func TestValidContentParsing(t *testing.T) { diff --git a/types/feedtype.go b/types/feedtype.go index 07c49b4..bba45cd 100644 --- a/types/feedtype.go +++ b/types/feedtype.go @@ -12,7 +12,6 @@ const ( OnBlockFeed FeedType = "ethOnBlock" TxReceiptsFeed FeedType = "txReceipts" TransactionStatusFeed FeedType = "transactionStatus" - CloudAPIFeed FeedType = "cloudAPIFeed" ) // Beacon blocks diff --git a/types/newtxnotification.go b/types/newtxnotification.go index bfe5a9e..c088e88 100644 --- a/types/newtxnotification.go +++ b/types/newtxnotification.go @@ -60,7 +60,7 @@ func (newTransactionNotification *NewTransactionNotification) MakeBlockchainTran return nil } -//Filters - creates BlockchainTransaction if needs and returns a map of requested fields and their value for evaluation +// Filters - creates BlockchainTransaction if needs and returns a map of requested fields and their value for evaluation func (newTransactionNotification *NewTransactionNotification) Filters(filters []string) map[string]interface{} { err := newTransactionNotification.MakeBlockchainTransaction() if err != nil { diff --git a/types/txflags.go b/types/txflags.go index 68a22f7..faa2951 100644 --- a/types/txflags.go +++ b/types/txflags.go @@ -18,6 +18,7 @@ const ( TFReusedNonce TFNextValidator TFNextValidatorRebroadcast + TFFrontRunningProtection TFStatusTrack = TFStatusMonitoring | TFPaidTx TFNonceTrack = TFNonceMonitoring | TFStatusTrack @@ -53,12 +54,17 @@ func (f TxFlags) IsDeliverToNode() bool { return f&TFDeliverToNode != 0 } -//IsNextValidator return true if transaction marked for next validator +// IsNextValidator return true if transaction marked for next validator func (f TxFlags) IsNextValidator() bool { return f&TFNextValidator != 0 } -//IsNextValidatorRebroadcast return true if transaction is fallback tx of next validator tx +// IsNextValidatorRebroadcast return true if transaction is fallback tx of next validator tx func (f TxFlags) IsNextValidatorRebroadcast() bool { return f&TFNextValidatorRebroadcast != 0 } + +// IsFrontRunningProtection return true if transaction is enabled with front runnig protection +func (f TxFlags) IsFrontRunningProtection() bool { + return f&TFFrontRunningProtection != 0 +} diff --git a/utils/flags.go b/utils/flags.go index 3ab1c14..c9fd988 100644 --- a/utils/flags.go +++ b/utils/flags.go @@ -393,4 +393,16 @@ var ( Usage: "calling method of the forwarding transaction to rpc endpoint", Value: "", } + BSCTransactionHoldDuration = &cli.IntFlag{ + Name: "bsc-transaction-hold-duration", + Usage: "number of millisecond before next block, marking the starting window time for processing front-running protection transaction on " + + "validator gateway", + Value: 500, + } + BSCTransactionPassedDueDuration = &cli.IntFlag{ + Name: "bsc-transaction-slot-end-duration", + Usage: "number of millisecond before next block, marking the ending window time for processing front-running protection transaction on " + + "validator gateway", + Value: 200, + } ) diff --git a/utils/ptr/ptr.go b/utils/ptr/ptr.go new file mode 100644 index 0000000..4a77326 --- /dev/null +++ b/utils/ptr/ptr.go @@ -0,0 +1,4 @@ +package ptr + +// New returns pointer to provided value. +func New[T any](val T) *T { return &val } diff --git a/utils/syncmap/hasher.go b/utils/syncmap/hasher.go new file mode 100644 index 0000000..e51ee3c --- /dev/null +++ b/utils/syncmap/hasher.go @@ -0,0 +1,39 @@ +package syncmap + +import ( + "hash/maphash" + + log "github.com/bloXroute-Labs/gateway/v2/logger" + + ethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/bloXroute-Labs/gateway/v2/types" +) + +// Hasher type of hasher functions +type Hasher[K comparable] func(maphash.Seed, K) uint64 + +// AccountIDHasher hasher function for AccountIDHasher key type. +// converts AccountIDHasher to string and returns Sum64 uint64 +func AccountIDHasher(seed maphash.Seed, key types.AccountID) uint64 { + return writeStringHash(seed, string(key)) +} + +// EthCommonHasher hasher function to hash EthCommonHasher +func EthCommonHasher(seed maphash.Seed, key ethcommon.Hash) uint64 { + return writeStringHash(seed, key.String()) +} + +// writeStringHash writes string hash and returns sum64 +func writeStringHash(seed maphash.Seed, key string) uint64 { + var h maphash.Hash + + h.SetSeed(seed) + + // it always writes all of s and never fails; the count and error result are for implementing io.StringWriter. + if _, err := h.WriteString(key); err != nil { + log.Warn("Can't write string to hash", err) + } + + return h.Sum64() +} diff --git a/utils/syncmap/sync_map.go b/utils/syncmap/sync_map.go index 9511fe0..fd4d5f7 100644 --- a/utils/syncmap/sync_map.go +++ b/utils/syncmap/sync_map.go @@ -1,65 +1,98 @@ package syncmap -import "sync" +import ( + "github.com/puzpuzpuz/xsync/v2" +) -// SyncMap is concurrent safe map with generics +// SyncMap is concurrent safe map with generics / arbitrarily typed keys +// Documentation: https://pkg.go.dev/github.com/puzpuzpuz/xsync type SyncMap[K comparable, V any] struct { - mx *sync.RWMutex + m *xsync.MapOf[K, V] +} + +// NewIntegerMapOf new map of integer keys +func NewIntegerMapOf[K xsync.IntegerConstraint, V any]() *SyncMap[K, V] { + return &SyncMap[K, V]{ + m: xsync.NewIntegerMapOf[K, V](), + } +} - m map[K]V +// NewStringMapOf new map of string keys +func NewStringMapOf[V any]() *SyncMap[string, V] { + return &SyncMap[string, V]{ + m: xsync.NewMapOf[V](), + } } -// New creates a new SyncMap. -func New[K comparable, V any]() *SyncMap[K, V] { +// NewTypedMapOf new map with arbitrary keys +func NewTypedMapOf[K comparable, V any](hasher Hasher[K]) *SyncMap[K, V] { return &SyncMap[K, V]{ - mx: new(sync.RWMutex), - m: make(map[K]V), + m: xsync.NewTypedMapOf[K, V](hasher), } } -// Get looks for the given key, and returns the value associated with it. -// The boolean it returns says whether the key is present in the map. -func (m *SyncMap[K, V]) Get(key K) (V, bool) { - m.mx.RLock() - val, exists := m.m[key] - m.mx.RUnlock() +// Load loads value by key +func (m *SyncMap[K, V]) Load(key K) (val V, exists bool) { + return m.m.Load(key) +} - return val, exists +// Store stores key and value in a map +func (m *SyncMap[K, V]) Store(key K, val V) { + m.m.Store(key, val) } -// Set sets the key-value pair. -func (m *SyncMap[K, V]) Set(key K, val V) { - m.mx.Lock() - m.m[key] = val - m.mx.Unlock() +// LoadOrStore load or store key values +func (m *SyncMap[K, V]) LoadOrStore(key K, val V) (actual V, loaded bool) { + return m.m.LoadOrStore(key, val) } -// Del removes the key-value pair. -func (m *SyncMap[K, V]) Del(key K) { - m.mx.Lock() - delete(m.m, key) - m.mx.Unlock() +// LoadAndStore load and store key values +func (m *SyncMap[K, V]) LoadAndStore(key K, val V) (actual V, loaded bool) { + return m.m.LoadAndStore(key, val) } -// Len returns the length of the map. -func (m *SyncMap[K, V]) Len() int { - m.mx.RLock() - l := len(m.m) - m.mx.RUnlock() +// LoadAndDelete load and delete object +func (m *SyncMap[K, V]) LoadAndDelete(key K) (val V, loaded bool) { + return m.m.LoadAndDelete(key) +} - return l +// Clear empty everything +func (m *SyncMap[K, V]) Clear() { + m.m.Clear() } -// Keys returns slice of all keys in map. -func (m *SyncMap[K, V]) Keys() []K { - m.mx.RLock() +// Delete delete specific object/value by key +func (m *SyncMap[K, V]) Delete(key K) { + m.m.Delete(key) +} - keys := make([]K, 0, len(m.m)) - for key := range m.m { - keys = append(keys, key) - } +// Size returns the size of a map +func (m *SyncMap[K, V]) Size() int { + return m.m.Size() +} - m.mx.RUnlock() +// Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration. +func (m *SyncMap[K, V]) Range(f func(key K, value V) bool) { + m.m.Range(f) +} + +// Keys returns slice of all keys in map. +func (m *SyncMap[K, V]) Keys() (keys []K) { + m.Range(func(key K, value V) bool { + keys = append(keys, key) + return true + }) return keys } + +// Compute either sets the computed new value for the key or deletes the value for the key. +func (m *SyncMap[K, V]) Compute(key K, valueFn func(oldValue V, loaded bool) (newValue V, delete bool)) (actual V, loaded bool) { + return m.m.Compute(key, valueFn) +} + +// Has checks whether object exists or not +func (m *SyncMap[K, V]) Has(key K) (exists bool) { + _, exists = m.m.Load(key) + return +} diff --git a/utils/syncmap/sync_map_test.go b/utils/syncmap/sync_map_test.go new file mode 100644 index 0000000..f135da6 --- /dev/null +++ b/utils/syncmap/sync_map_test.go @@ -0,0 +1,247 @@ +package syncmap_test + +import ( + "strconv" + "sync" + "testing" + + "github.com/bloXroute-Labs/gateway/v2/sdnmessage" + "github.com/bloXroute-Labs/gateway/v2/types" + "github.com/bloXroute-Labs/gateway/v2/utils/syncmap" +) + +const goroutineCount = 100 + +func BenchmarkSyncMap(b *testing.B) { + b.Run("sync.Map", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sm := new(sync.Map) + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read and write operations + for j := 0; j < goroutineCount; j++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := strconv.Itoa(id*1000 + k) + sm.Store(key, id) + sm.Load(key) + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) + + b.Run("syncmap.SyncMap", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sm := syncmap.NewStringMapOf[int]() + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read and write operations + for j := 0; j < goroutineCount; j++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := strconv.Itoa(id*1000 + k) + sm.Store(key, id) + sm.Load(key) + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) + + b.Run("sync.Map[joint-keys]", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sm := new(sync.Map) + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read and write operations + for j := 0; j < goroutineCount; j++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := strconv.Itoa(1000 + k) + sm.Store(key, id) + sm.Load(key) + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) + + b.Run("syncmap.SyncMap[joint-keys]", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sm := syncmap.NewStringMapOf[int]() + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read and write operations + for j := 0; j < goroutineCount; j++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := strconv.Itoa(1000 + k) + sm.Store(key, id) + sm.Load(key) + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) +} + +func BenchmarkSyncMapRead(b *testing.B) { + b.Run("sync.Map", func(b *testing.B) { + sm := new(sync.Map) + + // Initialize the sync.Map with keys and values + for i := 0; i < 10000; i++ { + key := strconv.Itoa(i) + sm.Store(key, i) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read operations + for j := 0; j < 10; j++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := strconv.Itoa(i*1000 + k) + sm.Load(key) + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) + + b.Run("syncmap.SyncMap", func(b *testing.B) { + sm := syncmap.NewStringMapOf[int]() + + // Initialize the syncmap.SyncMap with keys and values + for i := 0; i < 10000; i++ { + key := strconv.Itoa(i) + sm.Store(key, i) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read operations + for j := 0; j < 10; j++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := strconv.Itoa(i*1000 + k) + sm.Load(key) + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) +} + +func BenchmarkSyncMapReadWithConversion(b *testing.B) { + b.Run("sync.Map", func(b *testing.B) { + sm := new(sync.Map) + + // Initialize the sync.Map with keys and values + for i := 0; i < 10000; i++ { + key := types.AccountID(strconv.Itoa(i)) + sm.Store(key, &sdnmessage.Account{}) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read operations + for j := 0; j < 10; j++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := types.AccountID(strconv.Itoa(i*1000 + k)) + if value, ok := sm.Load(key); ok { + account := value.(*sdnmessage.Account) + _ = account + } + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) + + b.Run("syncmap.SyncMap", func(b *testing.B) { + sm := syncmap.NewTypedMapOf[types.AccountID, *sdnmessage.Account](syncmap.AccountIDHasher) + + // Initialize the syncmap.SyncMap with keys and values + for i := 0; i < 10000; i++ { + key := types.AccountID(strconv.Itoa(i)) + sm.Store(key, &sdnmessage.Account{}) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var wg sync.WaitGroup + + // Spawn multiple goroutines to perform concurrent read operations + for j := 0; j < 10; j++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + for k := 0; k < 1000; k++ { + key := types.AccountID(strconv.Itoa(i*1000 + k)) + account, _ := sm.Load(key) + _ = account + } + }(j) + } + + // Wait for all goroutines to finish + wg.Wait() + } + }) +} diff --git a/utils/util.go b/utils/util.go index 2473e9f..2db866f 100644 --- a/utils/util.go +++ b/utils/util.go @@ -14,6 +14,7 @@ import ( log "github.com/bloXroute-Labs/gateway/v2/logger" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" + uuid "github.com/satori/go.uuid" ) // Abs returns the absolute value of an integer @@ -128,3 +129,9 @@ func getTypeString(t reflect.Type) string { } return t.String() } + +// GenerateUUID generates random subscription ID +func GenerateUUID() uuid.UUID { + newUUID, _ := uuid.NewV4() + return newUUID +}