Skip to content

Conversation

@nkuba
Copy link
Member

@nkuba nkuba commented Jun 17, 2022

Closes: #3006
Depends on: #3016

In this PR we register an operator in the Random Beacon's Sortition Pool.

We introduced a sortition package that defines a function for registration in the sortition pool. The package defines an interface for sortition pool-related functions that should be implemented by the contract handle (in this case RandomBeacon, but later we will have to do the same for WalletRegistry).

@nkuba nkuba added this to the v2.0.0-m1 milestone Jun 17, 2022
@nkuba nkuba linked an issue Jun 17, 2022 that may be closed by this pull request
@nkuba nkuba force-pushed the sortition-pool-register branch from 0182690 to 688277b Compare June 23, 2022 10:35
@nkuba nkuba requested review from dimpar, lukasz-zimnoch and pdyraga and removed request for dimpar, lukasz-zimnoch and pdyraga June 23, 2022 12:31
@nkuba nkuba force-pushed the sortition-pool-register branch from fb2ae37 to 05c030f Compare June 27, 2022 20:32
nkuba added 11 commits June 29, 2022 08:51
We export Ethereum Chain handle to use in other packages as we are
aiming for packages structure refactoring and we expect to use the
common chain handle in multiple modules.

We also renamed the struct to `Chain` as it's defined in `ethereum`
pacakge and will be used externally as `ethereum.Chain`, which is better
than `ethereum.EthereumChain`.
We need getters for chain handle properites to be able to use them in
other packages raleted to chain integration.
Implemented chain handle for Random Beacon's Sortition Pool contract.
The interface should be used by the beacon and ecdsa handles to run
sortition pool related functions.
Implement functions for sortition pool in the random beacon's handle.
If we need to fetch the config for V2 we need to implement this function
again.
We need to update the code for V2
The code is based on keep-ecdsa register.go code, with some
improvements.

We want the operator to join the sortition pool if it's eligible and the
operator is registered by a staking provider.
Start sortition pool registration once a client starts.
@nkuba nkuba force-pushed the sortition-pool-register branch from 05c030f to cf474f0 Compare June 29, 2022 06:51
This is a common package that will be used by beacon and ecdsa modules.
@nkuba nkuba force-pushed the sortition-pool-register branch from 902ab51 to d31e60a Compare June 29, 2022 07:26
@nkuba nkuba marked this pull request as ready for review June 29, 2022 13:37
Base automatically changed from go-bindings-for-v2-contracts to main June 29, 2022 13:38
// "is correct and it has KEEP tokens delegated and the operator " +
// "contract has been authorized to operate on the stake",
// )
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove it. It is hard to say what we should wait for here - stake, authorization, or maybe staking provider<>operator mapping?

chainProvider,
netProvider,
persistence,
beaconChainHandle,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing it as a separate parameter, I would replace ThresholdRelay() in chain.Handle with this interface. In case it is too complicated to replace ThresholdRelay() now, I would introduce it next to ThresholdRelay() and deprecated ThresholdRelay() use.

@@ -1 +1 @@
This directory contains legacy code from V1. It should be removed.
This directory contains legacy code from V1. It should be moved to fit the new directories structure.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would clear up this directory and remove everything that is not needed for v2, assuming there is a way to make the compilation pass.

Comment on lines -95 to +98
ec := &ethereumChain{
config: config,
c := &Chain{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two questions:

  1. Why not keep it as ethereumChain and have ethereumBeaconChain and ethereumEcdsaChain? I think it's easier to navigate through implementation files when it's implicitly denoted this is Ethereum implementation.
  2. Why do we export it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was exported as it's used under random beacon's ethereum handle:
https://github.com/keep-network/keep-core/blob/fcc72bc88d90c714564caec45d60fe0e2b43b984/pkg/chain/random-beacon/ethereum.go#L23-L24

I'm open to other approaches.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was made in the initial implementation approach I've taken for this PR to reuse common Ethereum chain implementation in the random beacon and ecdsa packages, along with keeping it functional for the legacy code.

pdyraga added a commit that referenced this pull request Jul 5, 2022
This change is greatly inspired by the work of @nkuba in
#3028 but it is just a subset of
the original work with some changes introduced.

First, `sortition.Chain` handle interface is introduced. In opposite to v1
approach, where we had a global chain handle, the new proposed approach is to
keep a chain handle per module. This should make the chain code less complicated
and allow introducing custom behavior to functions, if needed be the logic of
the given module.

Second, `chain.Address` type based on `string` is introduced. We need
a chain-agnostic implementation of a chain address to use in interfaces. So far,
it's been an array of bytes, as presented in `Signing` interface, but `string`
seems easier to use in most cases.

Last but not least, a local implementation of `sortition.Chain` handle is
introduced.

Also, a set of simple test utils for assertions is added. Most of the time, such
simple assertions are all we need, and having them extracted to a single place
lets us avoid the assertion error message formatting boilerplate in test files.
Long-term, if needed, we may use some more sophisticated assertion library.
@lukasz-zimnoch
Copy link
Member

I'm a bit lost with the chain package structure we aim for. Especially, I don't understand the point of declaring upfront interfaces in the same package as the implementation of that interface. That seems to match more the Java-style than the Go-style interfaces. There are also some issues with that approach, for example:

  • pkg/chain/random-beacon/chain.go: There is a Handle interface declared in the beacon package. The client code will use it as beacon.Handle. That suggests this is a handle to the beacon instance, not the chain subset used by the random beacon. Looks like there can be more naming-related problems like that once we add more functions and interfaces.
  • Such chain interfaces with a default Ethereum implementation living nearby will be likely polluted with chain-specific types soon. Also, if we wanted to add another chain, we would be forced to add the new chain implementation in the same package. That will make the package messy
  • The implementation of each chain will be scattered through the module-specific packages. For example, if we want to implement Ethereum, we need to add ethereum.go in pkg/chain/random-beacon, pkg/chain/ecdsa and so on. This doesn't look like a bad idea at the moment but, at the same time, we need to create an additional package for common features and import them from there. That makes the package structure more complicated. There is also a risk that we will need to do more juggling in order to avoid code duplication in the future.
  • Module packages containing the domain logic like pkg/beacon and future pkg/ecdsa will not control their chain interfaces. That makes them dependent on external packages that define those chain interfaces. Also, those external packages contain a default chain implementation that can strongly affect the interface. The direction of dependency seems to be wrong. The intuition says that the domain logic should declare its own chain interface and the external implementation should depend on it.

That said, I'm wondering whether we shouldn't go with something like:

  • pkg/beacon declares its own chain.go where it defines the chain interface needed by that package. Actually, it already has the pkg/beacon/relay/chain/chain.go file with the relay chain interface. We can flatten that a little bit and remove the relay sub-package.
  • pkg/ecdsa will declare its own chain.go as well.
  • We will have a single pkg/chain/ethereum package containing the entire Ethereum chain implementation split into several module-specific files like: beacon.go, ecdsa.go and containing the common features like stake_monitor.go, signing.go and so on. This is also the place where contract bindings will live.
  • We will remove pkg/chain/chain.go. It looks like this is an artificial grouping of the BlockCounter, StakeMonitor and Signing interfaces. We can push those interfaces to separate files in pkg/chain package or move them to pkg/beacon and pkg/ecdsa altogether.

What are your thoughts guys?

@pdyraga
Copy link
Member

pdyraga commented Jul 5, 2022

I think your proposal makes sense but I also see some advantages of keeping chain implementations per module. I think we need to do some more experimentation and then discuss one more time before we decide to go this way or another.

A couple of quick thoughts:

Yes, keeping ethereum.go next to chain.go inside a module feels weird at first. On the other hand, if we treat the module like a chain-agnostic building block providing some functionality, it could make sense. Say, we have sortition module with sortition.go, chain.go, ethereum.go and algorand.go. The code outside of sortition.go does not care about most of sortition's chain.go functions because they are needed only in this context. Also, if there is something sortition-specific that must be included in ethereum.go implementation of one function, so that ethereum.go is not just a thin layer or read-writes, having it inside the interested module makes sense.

In my opinion, the problem with ethereum/ethereum.go is that it's a bag of functions without a context of how they are used and sometimes the context is needed to understand some specific logic inside of ethereum.go. For instance, why some call is retried and some other is not?

On the other hand, I think that instantiating one handle and then passing it everywhere is easier than instantiating multiple per-module handles that can even depend on each other. Also, using a set of common functions should be easier with a single handle, as you say.

Regardless of the decision here, I think keeping local.go per module makes sense. Usually, there is some logic in local.go needed by the tests of the given module that does not make sense without the context. For example, why do we return an error from JoinSortitionPool call if eligible stake is 0 or if the operator <> staking provider mapping is not set. This is very specific to sortition.

@pdyraga
Copy link
Member

pdyraga commented Jul 8, 2022

Closing in favor of #3056, #3067, #3070, #3077

@pdyraga pdyraga closed this Jul 8, 2022
@pdyraga pdyraga deleted the sortition-pool-register branch July 8, 2022 11:26
@pdyraga pdyraga removed the request for review from dimpar July 15, 2022 12:09
@pdyraga pdyraga modified the milestones: v2.0.0-m0, v2.0.0-m4 Sep 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Register in Random Beacon sortition pool

3 participants