Skip to content

Conversation

@lukasz-zimnoch
Copy link
Member

@lukasz-zimnoch lukasz-zimnoch commented Jul 6, 2022

Depends on: #3034

This pull request refactors the chain package structure to match the expectations of v2. I recommend going commit by commit to grasp the changes in an easy way.

Planned packages structure for v2

We aim for the following structure of packages:

pkg
    beacon
        beacon.go <-- main package file exposing `beacon.Initialize` function
        chain.go  <-- declares `beacon.Chain` interface that sets expectations for the required subset of on-chain functions
    tbtc
        tbtc.go   <-- main package file exposing `tbtc.Initialize` function
        chain.go  <-- declares `tbtc.Chain` interface that sets expectations for the required subset of on-chain functions
    chain
        ethereum
            beacon/gen  <-- beacon contract bindings
            tbtc/gen    <-- tbtc contract bindings
            ethereum.go <-- defines `ethereum.Chain` which is a base non-app-specific chain exposing generic features
            beacon.go   <-- defines `ethereum.BeaconChain` which embeds `ethereum.Chain` and implements `beacon.Chain`
            tbtc.go     <-- defines `ethereum.TbtcChain` which embeds `ethereum.Chain` and implements `tbtc.Chain`
            ...         <-- other files like stake_monitor.go, key.go, etc.
        block_counter.go <-- declares chain-agnostic `chain.BlockCounter` interface
        signing.go       <-- declares chain-agnostic `chain.Singing` interface
        stake_monitor.go <-- declares chain-agnostic `chain.StakeMonitor` interface
        ...              <-- other chain-agnostic cross-application interfaces and types

This structure allows each application package (pkg/beacon and pkg/tbtc) to set and control their own expectations regarding the chain. The pkg/chain contains chain implementations that must implement application-specific chain interfaces. This way the chain implementation is decoupled from the application domain code and does not affect it. Chain-agnostic interfaces declared directly in the pkg/chain package represents cross-application chain concepts that are shared by all applications.

Steps towards the desired structure done in this PR

This PR makes the following steps to reach the desired structure:

  1. It renames the legacy pkg/chain/ethereum package to pkg/chain/ethereum_v1 and adjusts all the references
  2. Adds the stub of the new Ethereum implementation in pkg/chain/ethereum that consists of the base ethereum.Chain chain handle placed in ethereum.go and app-specific chain handles ethereum.BeaconChain and ethereum.TbtcChain placed in beacon.go and tbtc.go respectively.
  3. Removes the utility chain functionality to reduce the complexity of chain code
  4. Removes the chain.Handle interface that was living in pkg/chain in favor of small specialized interfaces scattered through separate files like stake_monitor.go, block_counter.go and so on.
  5. Removes the chain.Staker thus the entire concept of staker as it no longer relevant for v2
  6. Moves the Ethereum key code from the legacy to the new package as it remains unchanged
  7. Adds the generic features implementations to the new pkg/chain/ethereum package. Those are the block counter, the stake monitor and the signer.

Next steps that need to be done

To completely transition towards the new structure we still need to:

  1. Move v2 contract bindings under pkg/chain/ethereum
  2. Simplify the pkg/beacon structure to have chain.go at the root
  3. Extend pkg/chain/ethereum to contain references to v2 contract bindings and all the required features
  4. Gradually replace usages of the legacy pkg/chain/ethereum_v1 package in favor of pkg/chain/ethereum.

`pkg/chain/ethereum_v1`

We need to make a transition towards the new
chain package structure required for V2. Here
is the first step.
@lukasz-zimnoch lukasz-zimnoch added this to the v2.0.0-m1 milestone Jul 6, 2022
@lukasz-zimnoch lukasz-zimnoch self-assigned this Jul 6, 2022
@lukasz-zimnoch lukasz-zimnoch requested a review from pdyraga July 6, 2022 11:41
This is no longer relevant for v2
@lukasz-zimnoch lukasz-zimnoch force-pushed the pkg-chain-refactoring branch from 5bac6d2 to 2dd5922 Compare July 6, 2022 12:01
Copy link
Member

@pdyraga pdyraga left a comment

Choose a reason for hiding this comment

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

This is really good start. I would also consider moving:

  • pkg/chain/random-beacon
  • pkg/chain/tbtc-v2
  • pkg/chain/threshold-network
  • pkg/chain/ecdsa

under ethereum. But maybe in a separate PR to unblock the other work?

@@ -1,104 +0,0 @@
package cmd
Copy link
Member

Choose a reason for hiding this comment

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

Let's add an issue to the last milestone to implement the relay command with a reference to this PR where we temporarily remove it.

This interface was an artificial bag for
more specialized chain interfaces. The client
code rarely needs all of them at once. In order
to make things simpler, we decided to remove the
`chain.Handle` interface and let the client
code depend on specific chain interfaces.
# Conflicts:
#	pkg/chain/ethereum/ethereum.go
The v2 client should operate on the concept of
the operator and staking provider. That said,
the concept of staker is no longer needed.
We take an opportunity and cleanup it here.
Base automatically changed from dkg-loop to main July 6, 2022 13:13

func (esm *ethereumStakeMonitor) HasMinimumStake(
// TODO: Real implementation with v2 contracts.
func (sm *stakeMonitor) HasMinimumStake(
Copy link
Member

Choose a reason for hiding this comment

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

@tomaszslabon Your #3060 will need this function. The proposed approach is: the firewall package should define an interface it is expecting, or import chain.StakeMonitor. I think the latter is fine for the time being. Per #3010, we no longer want to check HasMinimumStake but HasStakeDelegation (we should rename this function). And the logic checking staking provider <> operator mapping and the TokenStaking roles should be checked inside HasStakeDelegated.

I think we should check both RandomBeacon and WalletRegistry for staking provider <> operator mapping. We want to have one network of nodes for both applications.


// BlockCounter creates a BlockCounter that uses the block number in Ethereum.
func (c *Chain) BlockCounter() (chain.BlockCounter, error) {
return c.blockCounter, nil
Copy link
Member

Choose a reason for hiding this comment

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

What is the idea behind this function? THe block counter comes from keep-common, right? Maybe this function should be a part of ethereum.go? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

We could put in ethereum.go but wanted to keep it in the same way as others generic features and align with how interfaces are declared in pkg/chain. Also, if we want to merge keep-common one day, the block counter will live here.

@lukasz-zimnoch lukasz-zimnoch marked this pull request as ready for review July 6, 2022 15:18
func NewChain(
ctx context.Context,
config *ethereum.Config,
client ethutil.EthereumClient,
Copy link
Member

@pdyraga pdyraga Jul 7, 2022

Choose a reason for hiding this comment

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

This should probably be *ethclient.Client, from github.com/ethereum/go-ethereum/ethclient. We need (in the next PR, I am working on it) obtain ChainID with a call to chainID, err := client.ChainID(context.Background()) and this is not possible with ethutil.EthereumClient.

Copy link
Member

Choose a reason for hiding this comment

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

I did this change in #3067 at commit 012e362. Started building on a top of this PR.

config *ethereum.Config,
client ethutil.EthereumClient,
) (*BeaconChain, error) {
chain, err := NewChain(ctx, config, client)
Copy link
Member

Choose a reason for hiding this comment

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

The problem with creating a new instance of the base chain in New* function of the application chain is that we will end up with multiple instances of the base chain: one for Random Beacon and one for TBTC. If the base chain holds the transaction mutex - and this feels like the right place - it means we have two mutexes.

To avoid it, I would consider keeping the base chain as a singleton and creating application chains using public functions of the base chain.

I did this change in a3ba22a in the followup PR of #3067.

Copy link
Member

Choose a reason for hiding this comment

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

Please also see fa75502 that deals with Connect function.

Current `golint` CI job raised a lot of
errors in packages that will be removed
soon. In the same time `golint` does not allow
for ignoring those errors and became deprecated.
We decided to remove it in favor of `gofmt`
to maintain formatting rules. We will add
a new linting tool soon.
Copy link
Member

@pdyraga pdyraga left a comment

Choose a reason for hiding this comment

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

Should we modify our pre-commit hooks ?

- name: Lint Go
uses: keep-network/golint-action@v1.0.2
- name: Go format
uses: Jerome1337/gofmt-action@v1.0.4
Copy link
Member

Choose a reason for hiding this comment

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

Non-blocking but we should fork in case this repository gets deleted one day.

@lukasz-zimnoch
Copy link
Member Author

Should we modify our pre-commit hooks ?

Yeah! See 059ec4f

@pdyraga pdyraga merged commit 2a1b355 into main Jul 7, 2022
@pdyraga pdyraga deleted the pkg-chain-refactoring branch July 7, 2022 11:53
pdyraga added a commit that referenced this pull request Jul 8, 2022
Cleanup of the `pkg/beacon` package

This pull request is the next step of the package refactoring started in #3062.
It simplifies the structure of the `pkg/beacon` and makes some general cleanup.
Specific changes are described below. Please review commit by commit to easily
understand all changes.

### Remove the intermediary `relay` package

So far, all the beacon code have lived under `pkg/beacon/relay/*`. The
intermediary `relay` package turned out to be redundant and just made the
package structure more complicated. This PR removes the `relay` package and
places everything directly in the root `pkg/beacon` package. This makes sense
because the core logic now lives there.

### Embed the `pkg/beacon/relay.go` file into the `pkg/beacon/node.go` file.

There is a `Node` structure that encapsulates the behavior of the beacon node.
So far, their code was scattered through two files `relay.go` and `node.go`. The
rules behind that split were unclear so we decided to merge them together into
a single `node.go` file that defines the `Node` type along with all their
methods.

### Make the `Node` type package-private

The aforementioned `Node` type is used internally by the `pkg/beacon` and should
never be instantiated manually outside of the `pkg/beacon` package. External
clients should use `beacon.Initialize` function. That said, we decided to make
the `Node` type package-private. 

### Adjustments around beacon chain interface

So far, the beacon chain interface was living in the `pkg/beacon/relay/chain`
package and was divided into two files `chain.go` and `result.go`. Since the
`relay` package was removed, the new path is `pkg/beacon/chain` which is more
straightforward. To adhere to this change,  all code references that used
`relaychain.Interface` are now using `beaconchain.Interface`. Also, the
`result.go` file content was merged into the `chain.go` file, and all the
nomenclature was adjusted to say about the "beacon chain interface" instead of
the "relay chain". The interface was also aligned to be compatible with the new
`pkg/chain/ethereum` package that we plan to use for v2. 

Ideally, the beacon chain interface should live directly in the `pkg/beacon` and
should be named `beacon.Chain`. Unfortunately, that causes import cycles with
the `dkg` and `entry` packages since `pkg/beacon` triggers the logic from there.
Making that work would require a bigger refactoring that is described in the
last section of this PR (see **Ideas for further refactoring**)

### Remove deprecated `StakerAddress` and replace it with `chain.Address`

Since v2 doesn't use the concept of `staker`, the `StakerAddress` type was
deprecated recently. Here we remove it and replace all occurrences with the new
`chain.Address` type introduced recently. The `chain.Address` type represents
a chain-agnostic address and is a good fit to replace the obsolete `StakerAddress`.

### Ideas for further refactoring

As mentioned above, the best place for the beacon chain interface is the root
`pkg/beacon`. However, that approach brings some import cycles. If we transform
the `pkg/beacon/chain.Interface` to be `pkg/beacon.Chain`, the `pkg/beacon/dkg`
and `pkg/beacon/entry` packages still need to import it. At the same time, the
`node` type living in the `pkg/beacon` triggers the `pkg/beacon/dkg` and
`pkg/beacon/entry` logic directly. To break the cycle, we need to make the root
`pkg/beacon` independent of their sub-packages that hold the implementation of
the DKG and entry signing logic. In order to do so, we would need to:
- Move the `dkg.ThresholdSigner` to the `pkg/beacon` package and rename it to
  `beacon.Signer`. This type seems to belong to the core beacon logic and should
  not be tied to the specific DKG implementation provided by the `pkg/beacon/dkg`
  package
- Decouple `pkg/beacon` from `pkg/beacon/dkg` and `pkg/beacon/entry` using the
  `beacon.DkgExecutor` and `beacon.RelayEntryExecutor` interfaces:

    ```
    package beacon

    // Former `beaconchain.Interface` moved from `pkg/beacon/chain`
    type Chain interface {
       ...
    }

    // Former `dkg.ThresholdSigner` moved from `pkg/beacon/dkg`
    type Signer struct {
       ...
    }
    
    type DkgExecutor interface {
        Execute(chain Chain, channel net.BroadcastChannel, ...) (*Signer, error)
    }

    type RelayEntryExecutor interface {
        Execute(chain Chain, channel net.BroadcastChannel, signer *Signer, ...) error
    }
    ```
- Make `beacon.Intialize` accepting `beacon.DkgExecutor` and
  `beacon.RelayEntryExecutor` interfaces and use them to trigger DKG and relay
  entry from within `beacon.node`. Those interfaces would be implemented by
  `pkg/beacon/dkg` and `pkg/beacon/entry` respectively. Those implementation
  would be injected in the `cmd/start.go` file.

Those changes will introduce a proper direction of dependencies in the entire
`pkg/beacon` package and will allow to easily test the `pkg/beacon` code by
providing DKG and entry stubs.
@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.

2 participants