mirror of
https://github.com/trezor/blockbook.git
synced 2026-02-20 00:51:39 +01:00
Merge pull request #1407 from trezor/base-newHeads-subscription-fix
fix: avoid Base newHeads bursts
This commit is contained in:
@@ -66,18 +66,22 @@ type Configuration struct {
|
|||||||
// EthereumRPC is an interface to JSON-RPC eth service.
|
// EthereumRPC is an interface to JSON-RPC eth service.
|
||||||
type EthereumRPC struct {
|
type EthereumRPC struct {
|
||||||
*bchain.BaseChain
|
*bchain.BaseChain
|
||||||
Client bchain.EVMClient
|
Client bchain.EVMClient
|
||||||
RPC bchain.EVMRPCClient
|
RPC bchain.EVMRPCClient
|
||||||
MainNetChainID Network
|
MainNetChainID Network
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
Parser *EthereumParser
|
Parser *EthereumParser
|
||||||
PushHandler func(bchain.NotificationType)
|
PushHandler func(bchain.NotificationType)
|
||||||
OpenRPC func(string, string) (bchain.EVMRPCClient, bchain.EVMClient, error)
|
OpenRPC func(string, string) (bchain.EVMRPCClient, bchain.EVMClient, error)
|
||||||
Mempool *bchain.MempoolEthereumType
|
Mempool *bchain.MempoolEthereumType
|
||||||
mempoolInitialized bool
|
mempoolInitialized bool
|
||||||
bestHeaderLock sync.Mutex
|
bestHeaderLock sync.Mutex
|
||||||
bestHeader bchain.EVMHeader
|
bestHeader bchain.EVMHeader
|
||||||
bestHeaderTime time.Time
|
bestHeaderTime time.Time
|
||||||
|
// newBlockNotifyCh coalesces bursts of newHeads events into a single wake-up.
|
||||||
|
// This keeps the subscription reader unblocked while we refresh the canonical tip.
|
||||||
|
newBlockNotifyCh chan struct{}
|
||||||
|
newBlockNotifyOnce sync.Once
|
||||||
NewBlock bchain.EVMNewBlockSubscriber
|
NewBlock bchain.EVMNewBlockSubscriber
|
||||||
newBlockSubscription bchain.EVMClientSubscription
|
newBlockSubscription bchain.EVMClientSubscription
|
||||||
NewTx bchain.EVMNewTxSubscriber
|
NewTx bchain.EVMNewTxSubscriber
|
||||||
@@ -113,6 +117,8 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
|
|||||||
BaseChain: &bchain.BaseChain{},
|
BaseChain: &bchain.BaseChain{},
|
||||||
ChainConfig: &c,
|
ChainConfig: &c,
|
||||||
}
|
}
|
||||||
|
// 1-slot buffer ensures we only queue one "refresh tip" signal at a time.
|
||||||
|
s.newBlockNotifyCh = make(chan struct{}, 1)
|
||||||
|
|
||||||
ProcessInternalTransactions = c.ProcessInternalTransactions
|
ProcessInternalTransactions = c.ProcessInternalTransactions
|
||||||
|
|
||||||
@@ -342,16 +348,17 @@ func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthereumRPC) subscribeEvents() error {
|
func (b *EthereumRPC) subscribeEvents() error {
|
||||||
|
b.newBlockNotifyOnce.Do(func() {
|
||||||
|
go b.newBlockNotifier()
|
||||||
|
})
|
||||||
// new block notifications handling
|
// new block notifications handling
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
h, ok := b.NewBlock.Read()
|
_, ok := b.NewBlock.Read()
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
b.UpdateBestHeader(h)
|
b.signalNewBlock()
|
||||||
// notify blockbook
|
|
||||||
b.PushHandler(bchain.NotificationNewBlock)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -608,11 +615,69 @@ func (b *EthereumRPC) getBestHeader() (bchain.EVMHeader, error) {
|
|||||||
|
|
||||||
// UpdateBestHeader keeps track of the latest block header confirmed on chain
|
// UpdateBestHeader keeps track of the latest block header confirmed on chain
|
||||||
func (b *EthereumRPC) UpdateBestHeader(h bchain.EVMHeader) {
|
func (b *EthereumRPC) UpdateBestHeader(h bchain.EVMHeader) {
|
||||||
|
if h == nil || h.Number() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
glog.V(2).Info("rpc: new block header ", h.Number().Uint64())
|
glog.V(2).Info("rpc: new block header ", h.Number().Uint64())
|
||||||
|
b.setBestHeader(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *EthereumRPC) signalNewBlock() {
|
||||||
|
// Non-blocking send: one pending signal is enough to refresh the tip.
|
||||||
|
select {
|
||||||
|
case b.newBlockNotifyCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *EthereumRPC) newBlockNotifier() {
|
||||||
|
for range b.newBlockNotifyCh {
|
||||||
|
updated, err := b.refreshBestHeaderFromChain()
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("refreshBestHeaderFromChain ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if updated {
|
||||||
|
b.PushHandler(bchain.NotificationNewBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *EthereumRPC) refreshBestHeaderFromChain() (bool, error) {
|
||||||
|
if b.Client == nil {
|
||||||
|
return false, errors.New("rpc client not initialized")
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
h, err := b.Client.HeaderByNumber(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if h == nil || h.Number() == nil {
|
||||||
|
return false, errors.New("best header is nil")
|
||||||
|
}
|
||||||
|
return b.setBestHeader(h), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *EthereumRPC) setBestHeader(h bchain.EVMHeader) bool {
|
||||||
|
if h == nil || h.Number() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
b.bestHeaderLock.Lock()
|
b.bestHeaderLock.Lock()
|
||||||
|
defer b.bestHeaderLock.Unlock()
|
||||||
|
changed := false
|
||||||
|
if b.bestHeader == nil || b.bestHeader.Number() == nil {
|
||||||
|
changed = true
|
||||||
|
} else {
|
||||||
|
prevNum := b.bestHeader.Number().Uint64()
|
||||||
|
newNum := h.Number().Uint64()
|
||||||
|
if prevNum != newNum || b.bestHeader.Hash() != h.Hash() {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
b.bestHeader = h
|
b.bestHeader = h
|
||||||
b.bestHeaderTime = time.Now()
|
b.bestHeaderTime = time.Now()
|
||||||
b.bestHeaderLock.Unlock()
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBestBlockHash returns hash of the tip of the best-block-chain
|
// GetBestBlockHash returns hash of the tip of the best-block-chain
|
||||||
|
|||||||
Reference in New Issue
Block a user