-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #63 from keep-network/pipe-it
New Ethereum event subscription API, background event pull loop # Overview There are two major changes to Ethereum subscription API proposed here: - new subscription API with `OnEvent` and `Pipe` functions, - background monitoring loop pulling past events from the chain. The first change should allow implementing some handler logic easier and to avoid complex logic leading to bugs such as keep-network/keep-core#1333 or keep-network/keep-core#2052. The second change should improve client responsiveness for operators running their nodes against Ethereum deployments that are not very reliable on the event delivery front. This code has been integrated with ECDSA keep client in `keep-ecdsa` repository and can be tested there on the branch `pipe-it` (keep-network/keep-ecdsa#671). # New API Event subscription API has been refactored to resemble the proposition from keep-network/keep-core#491. The new event subscription mechanism allows installing event callback handler function with `OnEvent` function as well as piping events from a subscription to a channel with `Pipe` function. Example usage of `OnEvent`: ``` handlerFn := func( submittingMember common.Address, conflictingPublicKey []byte, blockNumber uint64, ) { // (...) } subscription := keepContract.ConflictingPublicKeySubmitted( nil, // default SubscribeOpts nil, // no filtering on submitting member ).OnEvent(handlerFn) ``` The same subscription but with a `Pipe`: ``` sink := make(chan *abi.BondedECDSAKeepConflictingPublicKeySubmitted) subscription := keepContract.ConflictingPublicKeySubmitted( nil, // default SubscribeOpts nil, // no filtering on submitting member ).Pipe(sink) ``` Currently, all our event subscriptions use function handlers. While it is convenient in some cases, in some other cases it is the opposite. For example, `OnBondedECDSAKeepCreated` handler in ECDSA client works perfectly fine as a function. It triggers the protocol and does not have to constantly monitor the state of the chain. On the other hand, `OnDKGResultSubmitted` handler from the beacon client needs to monitor the chain and exit the process of event publication in case another node has published the result. In this case, the code could be better structured with a channel-based subscription that would allow listening for block counter events and events from DKG result submitted subscription in one for-select loop. # Background monitoring loop Some nodes in the network are running against Ethereum setups that are not particularly reliable in delivering events. Events are not delivered, nodes are not starting key-generation, or are not participating in redemption signing. Another problem is the stability of the event subscription mechanism (see #62). If the web socket connection is dropped too often, the resubscription mechanism is not enough to receive events emitted when the connection was in a weird, stale state. To address this problem, we introduce a background loop periodically pulling past events from the chain next to a regular `watchLogs` subscription. How often events are pulled and how many blocks are taken into account can be configured with `SubscribeOpts` parameters. This way, even if the event was lost by `watchLogs` subscription for whatever reason, it should be pulled by a background monitoring loop later. An extremely important implication of this change is that handlers should have a logic in place allowing them to de-duplicate received events even if a lot of time passed between receiving the original event and the duplicate. I have been experimenting with various options here, including de-duplication events in the chain implementation layer, but none of them proved to be successful as the correct de-duplication algorithm requires domain knowledge about a certain type of an event and in what circumstances identical event emitted later should or should not be identified as a duplicate. De-duplicator implementations should be added to `keep-core` and `keep-ecdsa` clients and are out of the scope of `keep-common` and this PR.
- Loading branch information
Showing
8 changed files
with
375 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package ethutil | ||
|
||
import "time" | ||
|
||
const ( | ||
// DefaultSubscribeOptsTick is the default duration with which | ||
// past events are pulled from the chain by the subscription monitoring | ||
// mechanism if no other value is provided in SubscribeOpts when creating | ||
// the subscription. | ||
DefaultSubscribeOptsTick = 15 * time.Minute | ||
|
||
// DefaultSubscribeOptsPastBlocks is the default number of past blocks | ||
// pulled from the chain by the subscription monitoring mechanism if no | ||
// other value is provided in SubscribeOpts when creating the subscription. | ||
DefaultSubscribeOptsPastBlocks = 100 | ||
|
||
// SubscriptionBackoffMax is the maximum backoff time between event | ||
// resubscription attempts. | ||
SubscriptionBackoffMax = 2 * time.Minute | ||
|
||
// SubscriptionAlertThreshold is time threshold below which event | ||
// resubscription emits an error to the logs. | ||
// WS connection can be dropped at any moment and event resubscription will | ||
// follow. However, if WS connection for event subscription is getting | ||
// dropped too often, it may indicate something is wrong with Ethereum | ||
// client. This constant defines the minimum lifetime of an event | ||
// subscription required before the subscription failure happens and | ||
// resubscription follows so that the resubscription does not emit an error | ||
// to the logs alerting about potential problems with Ethereum client | ||
// connection. | ||
SubscriptionAlertThreshold = 15 * time.Minute | ||
) | ||
|
||
// SubscribeOpts specifies optional configuration options that can be passed | ||
// when creating Ethereum event subscription. | ||
type SubscribeOpts struct { | ||
|
||
// Tick is the duration with which subscription monitoring mechanism | ||
// pulls events from the chain. This mechanism is an additional process | ||
// next to a regular watchLogs subscription making sure no events are lost | ||
// even in case the regular subscription missed them because of, for | ||
// example, connectivity problems. | ||
Tick time.Duration | ||
|
||
// PastBlocks is the number of past blocks subscription monitoring mechanism | ||
// takes into consideration when pulling past events from the chain. | ||
// This event pull mechanism is an additional process next to a regular | ||
// watchLogs subscription making sure no events are lost even in case the | ||
// regular subscription missed them because of, for example, connectivity | ||
// problems. | ||
PastBlocks uint64 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.