Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip beefy mandatory commitment if possible #1139

Closed
Closed
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
6 changes: 5 additions & 1 deletion relayer/cmd/scan_beefy.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ func ScanBeefyFn(cmd *cobra.Command, _ []string) error {
"validator-set-id": validatorSetID,
}).Info("Connected to relaychain.")

commitments, err := polkadotListener.Start(ctx, eg, beefyBlock, validatorSetID)
var currentState beefy.BeefyState
currentState.CurrentValidatorSetId = validatorSetID
currentState.LatestBeefyBlock = beefyBlock

commitments, err := polkadotListener.Start(ctx, eg, currentState)
if err != nil {
logrus.WithError(err).Fatalf("could not start")
}
Expand Down
31 changes: 22 additions & 9 deletions relayer/relays/beefy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,15 @@ func (relay *Relay) Start(ctx context.Context, eg *errgroup.Group) error {
return fmt.Errorf("create ethereum connection: %w", err)
}

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

requests, err := relay.polkadotListener.Start(ctx, eg, initialBeefyBlock, initialValidatorSetID)
requests, err := relay.polkadotListener.Start(ctx, eg, currentState)
if err != nil {
return fmt.Errorf("initialize polkadot listener: %w", err)
}
Expand All @@ -79,11 +78,12 @@ func (relay *Relay) Start(ctx context.Context, eg *errgroup.Group) error {
return nil
}

func (relay *Relay) getInitialState(ctx context.Context) (uint64, uint64, error) {
func (relay *Relay) getCurrentState(ctx context.Context) (BeefyState, error) {
var currentState BeefyState
address := common.HexToAddress(relay.config.Sink.Contracts.BeefyClient)
beefyClient, err := contracts.NewBeefyClient(address, relay.ethereumConn.Client())
if err != nil {
return 0, 0, err
return currentState, err
}

callOpts := bind.CallOpts{
Expand All @@ -92,13 +92,26 @@ func (relay *Relay) getInitialState(ctx context.Context) (uint64, uint64, error)

latestBeefyBlock, err := beefyClient.LatestBeefyBlock(&callOpts)
if err != nil {
return 0, 0, err
return currentState, err
}

currentValidatorSet, err := beefyClient.CurrentValidatorSet(&callOpts)
if err != nil {
return 0, 0, err
return currentState, err
}

return latestBeefyBlock, currentValidatorSet.Id.Uint64(), nil
nextValidatorSet, err := beefyClient.NextValidatorSet(&callOpts)
if err != nil {
return currentState, err
}

currentState = BeefyState{
LatestBeefyBlock: latestBeefyBlock,
CurrentValidatorSetId: currentValidatorSet.Id.Uint64(),
CurrentValidatorSetRoot: currentValidatorSet.Root,
NextValidatorSetId: nextValidatorSet.Id.Uint64(),
NextValidatorSetRoot: nextValidatorSet.Root,
}

return currentState, nil
}
8 changes: 8 additions & 0 deletions relayer/relays/beefy/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,11 @@ func proofToLog(proof contracts.BeefyClientValidatorProof) logrus.Fields {
"Proof": hexProof,
}
}

type BeefyState struct {
LatestBeefyBlock uint64
CurrentValidatorSetId uint64
CurrentValidatorSetRoot [32]byte
NextValidatorSetId uint64
NextValidatorSetRoot [32]byte
}
53 changes: 34 additions & 19 deletions relayer/relays/beefy/polkadot-listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func NewPolkadotListener(
func (li *PolkadotListener) Start(
ctx context.Context,
eg *errgroup.Group,
currentBeefyBlock uint64,
currentValidatorSetID uint64,
currentState BeefyState,
) (<-chan Request, error) {
storageKey, err := types.CreateStorageKey(li.conn.Metadata(), "Beefy", "Authorities", nil, nil)
if err != nil {
Expand All @@ -45,7 +44,7 @@ func (li *PolkadotListener) Start(

eg.Go(func() error {
defer close(requests)
err := li.scanCommitments(ctx, currentBeefyBlock, currentValidatorSetID, requests)
err := li.scanCommitments(ctx, currentState, requests)
if err != nil {
return err
}
Expand All @@ -57,16 +56,15 @@ func (li *PolkadotListener) Start(

func (li *PolkadotListener) scanCommitments(
ctx context.Context,
currentBeefyBlock uint64,
currentValidatorSet uint64,
currentState BeefyState,
requests chan<- Request,
) error {
in, err := ScanSafeCommitments(ctx, li.conn.Metadata(), li.conn.API(), currentBeefyBlock+1)
lastSyncedBeefyBlock := currentState.LatestBeefyBlock
currentValidatorSet := currentState.CurrentValidatorSetId
in, err := ScanSafeCommitments(ctx, li.conn.Metadata(), li.conn.API(), lastSyncedBeefyBlock+1)
if err != nil {
return fmt.Errorf("scan commitments: %w", err)
}
lastSyncedBeefyBlock := currentBeefyBlock

for {
select {
case <-ctx.Done():
Expand All @@ -83,43 +81,44 @@ func (li *PolkadotListener) scanCommitments(
validatorSetID := result.SignedCommitment.Commitment.ValidatorSetID
nextValidatorSetID := uint64(result.MMRProof.Leaf.BeefyNextAuthoritySet.ID)

if validatorSetID != currentValidatorSet && validatorSetID != currentValidatorSet+1 {
return fmt.Errorf("commitment has unexpected validatorSetID: blockNumber=%v validatorSetID=%v expectedValidatorSetID=%v",
committedBeefyBlock,
validatorSetID,
currentValidatorSet,
)
}

logEntry := log.WithFields(log.Fields{
"commitment": log.Fields{
"blockNumber": committedBeefyBlock,
"validatorSetID": validatorSetID,
"nextValidatorSetID": nextValidatorSetID,
},
"validatorSetID": currentValidatorSet,
"IsHandover": validatorSetID == currentValidatorSet+1,
"IsHandover": validatorSetID > currentValidatorSet,
"IsMandatory": result.IsMandatory,
"lastSyncedBeefyBlock": lastSyncedBeefyBlock,
})

validators, err := li.queryBeefyAuthorities(result.BlockHash)
if err != nil {
return fmt.Errorf("fetch beefy authorities at block %v: %w", result.BlockHash, err)
}
currentAuthoritySet, err := li.queryBeefyAuthoritySet(result.BlockHash)
if err != nil {
return fmt.Errorf("fetch beefy authoritie set at block %v: %w", result.BlockHash, err)
}
task := Request{
Validators: validators,
SignedCommitment: result.SignedCommitment,
Proof: result.MMRProof,
}

if validatorSetID == currentValidatorSet+1 && validatorSetID == nextValidatorSetID-1 {
if validatorSetID > currentValidatorSet {
if currentAuthoritySet.Root == currentState.NextValidatorSetRoot && committedBeefyBlock < lastSyncedBeefyBlock+li.config.UpdatePeriod {
logEntry.Info("Discarded commitment with beefy authorities not change")
continue
}
task.IsHandover = true
select {
case <-ctx.Done():
return ctx.Err()
case requests <- task:
logEntry.Info("New commitment with handover added to channel")
currentValidatorSet++
currentValidatorSet = validatorSetID
lastSyncedBeefyBlock = committedBeefyBlock
}
} else if validatorSetID == currentValidatorSet {
Expand Down Expand Up @@ -175,3 +174,19 @@ func (li *PolkadotListener) queryBeefyNextAuthoritySet(blockHash types.Hash) (ty

return nextAuthoritySet, nil
}

type BeefyAuthoritySet = types.BeefyNextAuthoritySet

func (li *PolkadotListener) queryBeefyAuthoritySet(blockHash types.Hash) (BeefyAuthoritySet, error) {
var authoritySet BeefyAuthoritySet
storageKey, err := types.CreateStorageKey(li.conn.Metadata(), "MmrLeaf", "BeefyAuthorities", nil, nil)
ok, err := li.conn.API().RPC.State.GetStorage(storageKey, &authoritySet, blockHash)
if err != nil {
return authoritySet, err
}
if !ok {
return authoritySet, fmt.Errorf("beefy AuthoritySet not found")
}

return authoritySet, nil
}
28 changes: 26 additions & 2 deletions relayer/relays/beefy/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package beefy

import (
"context"
"encoding/binary"
"fmt"
"time"

Expand Down Expand Up @@ -81,6 +82,7 @@ type ScanCommitmentsResult struct {
BlockNumber uint64
BlockHash types.Hash
Depth uint64
IsMandatory bool
Error error
}

Expand Down Expand Up @@ -129,6 +131,21 @@ func scanCommitments(ctx context.Context, api *gsrpc.SubstrateAPI, startBlock ui
return
}

isMandatory := false
for _, digest := range block.Block.Header.Digest {
if !digest.IsConsensus {
continue
}
consensus := digest.AsConsensus
// Mandatory commitments contain a BEEF digest of type ConsensusLog::AuthoritiesChange (0x01)
// which signifies the the change of session authorities.
// https://github.com/paritytech/polkadot-sdk/blob/6a168ad57ad13ea0896f7684120f4fa15bfef474/substrate/primitives/consensus/beefy/src/lib.rs#L254C2-L254C19
if decodeEngineId(uint32(consensus.ConsensusEngineID)) == "BEEF" && consensus.Bytes[0] == 0x01 {
isMandatory = true
break
}
}

var commitment *types.SignedCommitment
for j := range block.Justifications {
sc := types.OptionalSignedCommitment{}
Expand Down Expand Up @@ -156,7 +173,7 @@ func scanCommitments(ctx context.Context, api *gsrpc.SubstrateAPI, startBlock ui
select {
case <-ctx.Done():
return
case out <- ScanCommitmentsResult{BlockNumber: result.BlockNumber, BlockHash: result.BlockHash, SignedCommitment: *commitment, Depth: result.Depth}:
case out <- ScanCommitmentsResult{BlockNumber: result.BlockNumber, BlockHash: result.BlockHash, SignedCommitment: *commitment, Depth: result.Depth, IsMandatory: isMandatory}:
}
}
}
Expand All @@ -167,6 +184,7 @@ type ScanSafeCommitmentsResult struct {
MMRProof merkle.SimplifiedMMRProof
BlockHash types.Hash
Depth uint64
IsMandatory bool
Error error
}

Expand Down Expand Up @@ -231,7 +249,7 @@ func scanSafeCommitments(ctx context.Context, meta *types.Metadata, api *gsrpc.S
select {
case <-ctx.Done():
return
case out <- ScanSafeCommitmentsResult{result.SignedCommitment, proof, blockHash, result.Depth, nil}:
case out <- ScanSafeCommitmentsResult{result.SignedCommitment, proof, blockHash, result.Depth, result.IsMandatory, nil}:
}

}
Expand Down Expand Up @@ -293,3 +311,9 @@ func verifyProof(meta *types.Metadata, api *gsrpc.SubstrateAPI, proof merkle.Sim

return actualRoot == expectedRoot, nil
}

func decodeEngineId(engineId uint32) string {
idAsBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(idAsBytes, engineId)
return string(idAsBytes)
}
2 changes: 1 addition & 1 deletion web/packages/test/config/beefy-relay.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
},
"beefy-activation-block": 0,
"fast-forward-depth": 20,
"update-period": 0
"update-period": 20
},
"sink": {
"ethereum": {
Expand Down
3 changes: 1 addition & 2 deletions web/packages/test/scripts/build-binary.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ build_binaries() {
# Check that all 3 binaries are available and no changes made in the polkadot and substrate dirs
if [[ ! -e "target/release/polkadot" || ! -e "target/release/polkadot-execute-worker" || ! -e "target/release/polkadot-prepare-worker" || "$changes_detected" -eq 1 ]]; then
echo "Building polkadot binary, due to changes detected in polkadot or substrate, or binaries not found"
# Increase session length to 2 mins
ROCOCO_EPOCH_DURATION=20 cargo build --release --locked --bin polkadot --bin polkadot-execute-worker --bin polkadot-prepare-worker
cargo build --release --locked --bin polkadot --bin polkadot-execute-worker --bin polkadot-prepare-worker
else
echo "No changes detected in polkadot or substrate and binaries are available, not rebuilding relaychain binaries."
fi
Expand Down