Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions relayer/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func init() {
rootCmd.AddCommand(storeBeaconStateCmd())
rootCmd.AddCommand(importBeaconStateCmd())
rootCmd.AddCommand(listBeaconStateCmd())
rootCmd.AddCommand(syncBeefyCommitmentCmd())
}

func Execute() {
Expand Down
64 changes: 64 additions & 0 deletions relayer/cmd/sync_beefy_commitment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cmd

import (
"fmt"
"log"

"github.com/sirupsen/logrus"
"github.com/snowfork/snowbridge/relayer/chain/ethereum"
"github.com/snowfork/snowbridge/relayer/relays/beefy"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func syncBeefyCommitmentCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "sync-latest-beefy-commitment",
Short: "Sync beefy commitment on demand",
Args: cobra.ExactArgs(0),
RunE: SyncBeefyCommitmentFn,
}

cmd.Flags().String("config", "/tmp/snowbridge/beefy-relay.json", "Path to configuration file")
cmd.Flags().String("private-key", "", "Ethereum private key")
cmd.Flags().String("private-key-file", "", "The file from which to read the private key")
cmd.Flags().Uint64P("block-number", "b", 0, "Relay block number which contains a Parachain message")
cmd.MarkFlagRequired("block-number")
return cmd
}

func SyncBeefyCommitmentFn(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()

log.SetOutput(logrus.WithFields(logrus.Fields{"logger": "stdlib"}).WriterLevel(logrus.InfoLevel))
logrus.SetLevel(logrus.DebugLevel)

configFile, err := cmd.Flags().GetString("config")
viper.SetConfigFile(configFile)
if err := viper.ReadInConfig(); err != nil {
return err
}

var config beefy.Config
err = viper.Unmarshal(&config)
if err != nil {
return err
}
privateKey, _ := cmd.Flags().GetString("private-key")
privateKeyFile, _ := cmd.Flags().GetString("private-key-file")
if privateKey == "" && privateKeyFile == "" {
return fmt.Errorf("missing private key")
}
keypair, err := ethereum.ResolvePrivateKey(privateKey, privateKeyFile)
if err != nil {
return err
}

relay, err := beefy.NewRelay(&config, keypair)
if err != nil {
return err
}
blockNumber, _ := cmd.Flags().GetUint64("block-number")
err = relay.OneShotSync(ctx, blockNumber)
return err
}
38 changes: 21 additions & 17 deletions relayer/relays/beefy/ethereum-writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,6 @@ func NewEthereumWriter(
}

func (wr *EthereumWriter) Start(ctx context.Context, eg *errgroup.Group, requests <-chan Request) error {
address := common.HexToAddress(wr.config.Contracts.BeefyClient)
contract, err := contracts.NewBeefyClient(address, wr.conn.Client())
if err != nil {
return fmt.Errorf("create beefy client: %w", err)
}
wr.contract = contract

callOpts := bind.CallOpts{
Context: ctx,
}
blockWaitPeriod, err := wr.contract.RandaoCommitDelay(&callOpts)
if err != nil {
return fmt.Errorf("create randao commit delay: %w", err)
}
wr.blockWaitPeriod = blockWaitPeriod.Uint64()
log.WithField("randaoCommitDelay", wr.blockWaitPeriod).Trace("Fetched randaoCommitDelay")

// launch task processor
eg.Go(func() error {
for {
Expand Down Expand Up @@ -295,3 +278,24 @@ func (wr *EthereumWriter) doSubmitFinal(ctx context.Context, commitmentHash [32]

return tx, nil
}

func (wr *EthereumWriter) initialize(ctx context.Context) error {
address := common.HexToAddress(wr.config.Contracts.BeefyClient)
contract, err := contracts.NewBeefyClient(address, wr.conn.Client())
if err != nil {
return fmt.Errorf("create beefy client: %w", err)
}
wr.contract = contract

callOpts := bind.CallOpts{
Context: ctx,
}
blockWaitPeriod, err := wr.contract.RandaoCommitDelay(&callOpts)
if err != nil {
return fmt.Errorf("create randao commit delay: %w", err)
}
wr.blockWaitPeriod = blockWaitPeriod.Uint64()
log.WithField("randaoCommitDelay", wr.blockWaitPeriod).Trace("Fetched randaoCommitDelay")

return nil
}
95 changes: 76 additions & 19 deletions relayer/relays/beefy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import (

"golang.org/x/sync/errgroup"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/snowfork/snowbridge/relayer/chain/ethereum"
"github.com/snowfork/snowbridge/relayer/chain/relaychain"
"github.com/snowfork/snowbridge/relayer/contracts"
"github.com/snowfork/snowbridge/relayer/crypto/secp256k1"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -56,49 +53,109 @@ func (relay *Relay) Start(ctx context.Context, eg *errgroup.Group) error {
if err != nil {
return fmt.Errorf("create ethereum connection: %w", err)
}
err = relay.ethereumWriter.initialize(ctx)
if err != nil {
return fmt.Errorf("initialize ethereum writer: %w", err)
}

initialBeefyBlock, initialValidatorSetID, err := relay.getInitialState(ctx)
initialState, err := relay.ethereumWriter.queryBeefyClientState(ctx)
if err != nil {
return fmt.Errorf("fetch BeefyClient current state: %w", err)
}
log.WithFields(log.Fields{
"beefyBlock": initialBeefyBlock,
"validatorSetID": initialValidatorSetID,
"beefyBlock": initialState.LatestBeefyBlock,
"validatorSetID": initialState.CurrentValidatorSetID,
}).Info("Retrieved current BeefyClient state")

requests, err := relay.polkadotListener.Start(ctx, eg, initialBeefyBlock, initialValidatorSetID)
requests, err := relay.polkadotListener.Start(ctx, eg, initialState.LatestBeefyBlock, initialState.CurrentValidatorSetID)
if err != nil {
return fmt.Errorf("initialize polkadot listener: %w", err)
}

err = relay.ethereumWriter.Start(ctx, eg, requests)
if err != nil {
return fmt.Errorf("initialize ethereum writer: %w", err)
return fmt.Errorf("start ethereum writer: %w", err)
}

return nil
}

func (relay *Relay) getInitialState(ctx context.Context) (uint64, uint64, error) {
address := common.HexToAddress(relay.config.Sink.Contracts.BeefyClient)
beefyClient, err := contracts.NewBeefyClient(address, relay.ethereumConn.Client())
func (relay *Relay) OneShotSync(ctx context.Context, blockNumber uint64) error {
// Initialize relaychainConn
err := relay.relaychainConn.Connect(ctx)
if err != nil {
return 0, 0, err
return fmt.Errorf("create relaychain connection: %w", err)
}

callOpts := bind.CallOpts{
Context: ctx,
// Initialize ethereumConn
err = relay.ethereumConn.Connect(ctx)
if err != nil {
return fmt.Errorf("create ethereum connection: %w", err)
}
err = relay.ethereumWriter.initialize(ctx)
if err != nil {
return fmt.Errorf("initialize EthereumWriter: %w", err)
}

latestBeefyBlock, err := beefyClient.LatestBeefyBlock(&callOpts)
state, err := relay.ethereumWriter.queryBeefyClientState(ctx)
if err != nil {
return 0, 0, err
return fmt.Errorf("query beefy client state: %w", err)
}
// Ignore relay block already synced
if blockNumber <= state.LatestBeefyBlock {
log.WithFields(log.Fields{
"validatorSetID": state.CurrentValidatorSetID,
"beefyBlock": state.LatestBeefyBlock,
"relayBlock": blockNumber,
}).Info("Relay block already synced, just ignore")
return nil
}

currentValidatorSet, err := beefyClient.CurrentValidatorSet(&callOpts)
// generate beefy update for that specific relay block
task, err := relay.polkadotListener.generateBeefyUpdate(ctx, blockNumber)
if err != nil {
return 0, 0, err
return fmt.Errorf("fail to generate next beefy request: %w", err)
}

return latestBeefyBlock, currentValidatorSet.Id.Uint64(), nil
// Ignore commitment earlier than LatestBeefyBlock which is outdated
if task.SignedCommitment.Commitment.BlockNumber <= uint32(state.LatestBeefyBlock) {
log.WithFields(log.Fields{
"latestBeefyBlock": state.LatestBeefyBlock,
"currentValidatorSetID": state.CurrentValidatorSetID,
"nextValidatorSetID": state.NextValidatorSetID,
"blockNumberToSync": task.SignedCommitment.Commitment.BlockNumber,
}).Info("Commitment outdated, just ignore")
return nil
}
if task.SignedCommitment.Commitment.ValidatorSetID > state.NextValidatorSetID {
log.WithFields(log.Fields{
"latestBeefyBlock": state.LatestBeefyBlock,
"currentValidatorSetID": state.CurrentValidatorSetID,
"nextValidatorSetID": state.NextValidatorSetID,
"validatorSetIDToSync": task.SignedCommitment.Commitment.ValidatorSetID,
}).Warn("Task unexpected, wait for mandatory updates to catch up first")
return nil
}

// Submit the task
if task.SignedCommitment.Commitment.ValidatorSetID == state.CurrentValidatorSetID {
task.ValidatorsRoot = state.CurrentValidatorSetRoot
} else {
task.ValidatorsRoot = state.NextValidatorSetRoot
}
err = relay.ethereumWriter.submit(ctx, task)
if err != nil {
return fmt.Errorf("fail to submit beefy update: %w", err)
}

updatedState, err := relay.ethereumWriter.queryBeefyClientState(ctx)
if err != nil {
return fmt.Errorf("query beefy client state: %w", err)
}
log.WithFields(log.Fields{
"latestBeefyBlock": updatedState.LatestBeefyBlock,
"currentValidatorSetID": updatedState.CurrentValidatorSetID,
"nextValidatorSetID": updatedState.NextValidatorSetID,
}).Info("Sync beefy update success")
return nil
}
Loading