Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f364999
Benchmark beefy by signatures
yrong Apr 18, 2025
6d17159
More samples
yrong Apr 18, 2025
aeef3a2
Cleanup
yrong Apr 23, 2025
e3734b3
SubmitFiatShamir
yrong Apr 25, 2025
2d55aa0
Add CreateFiatShamirFinalBitfield
yrong Apr 25, 2025
5bea664
Make requiredSignatures bounded
yrong Apr 28, 2025
f2ed8d2
Beefy relayer
yrong Apr 28, 2025
4f978c0
Cleanup
yrong Apr 28, 2025
bb5c9d2
Merge branch 'ron/benchmark-beefy-by-signatures' into ron/submitFiatS…
yrong Apr 30, 2025
a61d4ca
Generate test fixture
yrong Apr 30, 2025
a34b83d
Merge branch 'main' into ron/benchmark-beefy-by-signatures
yrong Apr 30, 2025
1458b8c
Merge branch 'ron/benchmark-beefy-by-signatures' into ron/submitFiatS…
yrong Apr 30, 2025
e48f5c4
testSubmitFiatShamirWithHandOver
yrong May 2, 2025
afc04c1
Fix compile error
yrong May 2, 2025
ec230b8
Use sha256 as hash function
yrong May 6, 2025
9da095f
Update test fixture
yrong May 6, 2025
e277eb7
Update quorum function
yrong May 6, 2025
b94851e
Switch to sha256d
yrong May 11, 2025
dcc7588
Initialize fiatShamirRequiredSignatures via the constructor
yrong May 11, 2025
b844164
Add an environment variable for local setup
yrong May 12, 2025
98b57d1
Merge branch 'main' into ron/submitFiatShamir
yrong Sep 8, 2025
042e546
Update contract binding
yrong Sep 8, 2025
2aeb930
Submit FiatShamir on demand
yrong Sep 8, 2025
b7ffc9e
Check for outdated commitments in the two-phase commit
yrong Sep 9, 2025
ee36155
Merge branch 'main' into ron/submitFiatShamir
yrong Sep 10, 2025
49cdc61
Add instant syncer
yrong Sep 14, 2025
3e9cb0c
on-demand relayer
yrong Sep 14, 2025
686ae24
Fix command
yrong Sep 15, 2025
ef72c48
Merge branch 'main' into ron/submitFiatShamir
yrong Nov 19, 2025
9b33056
Fix subsample
yrong Nov 19, 2025
dfaca90
Merge branch 'ron/submitFiatShamir' into ron/submitFiatShamir-relay
yrong Nov 19, 2025
0a20d9f
Remove relayer change
yrong Nov 19, 2025
2a44fca
Merge branch 'ron/submitFiatShamir' into ron/submitFiatShamir-relay
yrong Nov 19, 2025
0b48f8b
Revert "Remove relayer change"
yrong Nov 19, 2025
e411810
Polish
yrong Nov 19, 2025
5a696c1
Change interval
yrong Nov 19, 2025
0efa2b3
Merge branch 'main' into ron/submitFiatShamir-relay
yrong Jan 7, 2026
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
2 changes: 1 addition & 1 deletion contracts/test/data/beefy-fiat-shamir-bitfield.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
],
"finalBitFieldRaw": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000d00400900006c00180090000040c00380c3020020022031020402800188008200049450c0111200c8d1001202300c03008a001080140060004000810600d000000000000000000000000000000000000000000000480820a940410001a060"
}
}
}
2 changes: 1 addition & 1 deletion contracts/test/data/beefy-fiat-shamir-proof.json

Large diffs are not rendered by default.

50 changes: 40 additions & 10 deletions relayer/cmd/run/parachain/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/snowfork/snowbridge/relayer/chain/ethereum"
para "github.com/snowfork/snowbridge/relayer/chain/parachain"
"github.com/snowfork/snowbridge/relayer/relays/beefy"
"github.com/snowfork/snowbridge/relayer/relays/parachain"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -25,6 +26,8 @@ var (
parachainPrivateKey string
parachainPrivateKeyFile string
parachainPrivateKeyID string
beefyConfigFile string
onDemand bool
)

func Command() *cobra.Command {
Expand All @@ -38,6 +41,8 @@ func Command() *cobra.Command {
cmd.Flags().StringVar(&configFile, "config", "", "Path to configuration file")
cmd.MarkFlagRequired("config")

cmd.Flags().StringVar(&beefyConfigFile, "beefy.config", "", "Path to beefy configuration file")

cmd.Flags().StringVar(&privateKey, "ethereum.private-key", "", "Ethereum private key")
cmd.Flags().StringVar(&privateKeyFile, "ethereum.private-key-file", "", "The file from which to read the private key")
cmd.Flags().StringVar(&privateKeyID, "ethereum.private-key-id", "", "The secret id to lookup the private key in AWS Secrets Manager")
Expand All @@ -46,6 +51,9 @@ func Command() *cobra.Command {
cmd.Flags().StringVar(&parachainPrivateKeyFile, "substrate.private-key-file", "", "The file from which to read the private key")
cmd.Flags().StringVar(&parachainPrivateKeyID, "substrate.private-key-id", "", "The secret id to lookup the private key in AWS Secrets Manager")

cmd.Flags().StringVar(&beefyConfigFile, "beefy.config", "", "Path to beefy configuration file")
cmd.Flags().BoolVarP(&onDemand, "on-demand", "", false, "Synchronize beefy commitments on demand together with parachain messages")

return cmd
}

Expand Down Expand Up @@ -79,11 +87,6 @@ func run(_ *cobra.Command, _ []string) error {
return err
}

relay, err := parachain.NewRelay(&config, keypair, keypair2)
if err != nil {
return err
}

ctx, cancel := context.WithCancel(context.Background())
eg, ctx := errgroup.WithContext(ctx)

Expand All @@ -103,11 +106,38 @@ func run(_ *cobra.Command, _ []string) error {
return nil
})

err = relay.Start(ctx, eg)
if err != nil {
logrus.WithError(err).Fatal("Unhandled error")
cancel()
return err
if !onDemand {
relay, err := parachain.NewRelay(&config, keypair, keypair2)
if err != nil {
return err
}
err = relay.Start(ctx, eg)
if err != nil {
logrus.WithError(err).Fatal("Unhandled error")
cancel()
return err
}
} else {
viper.SetConfigFile(beefyConfigFile)
if err := viper.ReadInConfig(); err != nil {
return err
}

var beefyConfig beefy.Config
err = viper.UnmarshalExact(&beefyConfig)
if err != nil {
return err
}
relay, err := parachain.NewOnDemandRelay(&config, &beefyConfig, keypair)
if err != nil {
return err
}
err = relay.Start(ctx, eg)
if err != nil {
logrus.WithError(err).Fatal("Unhandled error")
cancel()
return err
}
}

err = eg.Wait()
Expand Down
115 changes: 115 additions & 0 deletions relayer/relays/beefy/ethereum-writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,21 @@ func (wr *EthereumWriter) submit(ctx context.Context, task *Request) error {
return fmt.Errorf("Failed to wait for RandaoCommitDelay: %w", err)
}

state, err := wr.queryBeefyClientState(ctx)
if err != nil {
return fmt.Errorf("query beefy client state: %w", err)
}

// Ignore beefy block already synced
if uint64(task.SignedCommitment.Commitment.BlockNumber) <= state.LatestBeefyBlock {
log.WithFields(log.Fields{
"validatorSetID": state.CurrentValidatorSetID,
"beefyBlock": state.LatestBeefyBlock,
"relayBlock": task.SignedCommitment.Commitment.BlockNumber,
}).Info("Beefy block already synced, just ignore")
return nil
}

Comment on lines +143 to +157
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check with the light client whether this update is outdated.

commitmentHash, err := task.CommitmentHash()
if err != nil {
return fmt.Errorf("generate commitment hash: %w", err)
Expand Down Expand Up @@ -172,6 +187,19 @@ func (wr *EthereumWriter) submit(ctx context.Context, task *Request) error {
}).Info("SubmitFinal is skipped, indicating that a newer update is already in progress.")
return nil
}
state, err = wr.queryBeefyClientState(ctx)
if err != nil {
return fmt.Errorf("query beefy client state: %w", err)
}

if uint64(task.SignedCommitment.Commitment.BlockNumber) <= state.LatestBeefyBlock {
log.WithFields(log.Fields{
"validatorSetID": state.CurrentValidatorSetID,
"beefyBlock": state.LatestBeefyBlock,
"relayBlock": task.SignedCommitment.Commitment.BlockNumber,
}).Info("Beefy block already synced, just ignore")
return nil
}
// Final submission
tx, err = wr.doSubmitFinal(ctx, *commitmentHash, initialBitfield, task)
if err != nil {
Expand Down Expand Up @@ -320,3 +348,90 @@ func (wr *EthereumWriter) initialize(ctx context.Context) error {

return nil
}

func (wr *EthereumWriter) submitFiatShamir(ctx context.Context, task Request) error {
signedValidators := []*big.Int{}
for i, signature := range task.SignedCommitment.Signatures {
if signature.IsSome() {
signedValidators = append(signedValidators, big.NewInt(int64(i)))
}
}
validatorCount := big.NewInt(int64(len(task.SignedCommitment.Signatures)))

// Pick a random validator who signs beefy commitment
chosenValidator := signedValidators[rand.Intn(len(signedValidators))].Int64()

log.WithFields(logrus.Fields{
"validatorCount": validatorCount,
"signedValidators": signedValidators,
"signedValidatorCount": len(signedValidators),
"chosenValidator": chosenValidator,
}).Info("Creating initial bitfield")

initialBitfield, err := wr.contract.CreateInitialBitfield(
&bind.CallOpts{
Pending: true,
From: wr.conn.Keypair().CommonAddress(),
},
signedValidators, validatorCount,
)
if err != nil {
return fmt.Errorf("create initial bitfield: %w", err)
}

commitment := toBeefyClientCommitment(&task.SignedCommitment.Commitment)

finalBitfield, err := wr.contract.CreateFiatShamirFinalBitfield(
&bind.CallOpts{
Pending: true,
From: wr.conn.Keypair().CommonAddress(),
},
*commitment,
initialBitfield,
)

if err != nil {
return fmt.Errorf("create validator final bitfield: %w", err)
}

validatorIndices := bitfield.New(finalBitfield).Members()

params, err := task.MakeSubmitFinalParams(validatorIndices, initialBitfield)
if err != nil {
return fmt.Errorf("make submit final params: %w", err)
}

logFields, err := wr.makeSubmitFinalLogFields(&task, params)
if err != nil {
return fmt.Errorf("logging params: %w", err)
}

tx, err := wr.contract.SubmitFiatShamir(
wr.conn.MakeTxOpts(ctx),
params.Commitment,
params.Bitfield,
params.Proofs,
params.Leaf,
params.LeafProof,
params.LeafProofOrder,
)
if err != nil {
return fmt.Errorf("SubmitFiatShamir: %w", err)
}

log.WithField("txHash", tx.Hash().Hex()).
WithFields(logFields).
Info("Sent SubmitFiatShamir transaction")

_, err = wr.conn.WatchTransaction(ctx, tx, 0)
if err != nil {
return fmt.Errorf("Wait receipt for SubmitFiatShamir: %w", err)
}

log.WithFields(logrus.Fields{
"tx": tx.Hash().Hex(),
"blockNumber": task.SignedCommitment.Commitment.BlockNumber,
}).Debug("Transaction submitFiatShamir succeeded")

return nil
}
66 changes: 65 additions & 1 deletion relayer/relays/beefy/on-demand-sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ func (relay *OnDemandRelay) OneShotStart(ctx context.Context, beefyBlockNumber u

log.Info("Performing sync")

err = relay.sync(ctx, beefyBlockNumber)
err = relay.syncFiatShamir(ctx, beefyBlockNumber)
if err != nil {
return fmt.Errorf("Sync failed: %w", err)
}
Expand Down Expand Up @@ -716,3 +716,67 @@ func (relay *OnDemandRelay) queueAll(ctx context.Context) error {
}
return nil
}

func (relay *OnDemandRelay) syncFiatShamir(ctx context.Context, blockNumber uint64) error {
state, err := relay.ethereumWriter.queryBeefyClientState(ctx)
if err != nil {
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
}

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

// 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.submitFiatShamir(ctx, task)
if err != nil {
return fmt.Errorf("SubmitFiatShamir 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
Loading