Skip to content

Commit

Permalink
Parachain Relayer V2 (#1321)
Browse files Browse the repository at this point in the history
* v2 initial commit

* latest changes

* Flesh out dispatch logic for inbound messages

* Add reward address

* make code compile

* Split initializer impl into a library to reduce contract size

* Major refactor

* Update tests

* add scripts back

* Update scripts

* Finish outbound messaging

* Implement token registration for V2

* Make functions payable

* improve docs

* Parachain relayer V2

* Rename to PendingOrder

* Decode compact int

* comments

* Improve doc

* review feedback

* cleanups

* Add initial tests for V2

* Make `rewardAddress` an indexed event parameter

* Remove unused

* Submit delivery proof

* Rename to InboundMessageV2

* Mark as view function

* Filter with reward address

* Clean up interfaces

* Make WETH address configurable

* Autowrap ether

* V2 smoke tests (#1327)

* Update contracts for smoke tests

* Remove unused

* Decode test

* Update subxt

* Register PNA with OutbountQueueV2

* Fix storage key

* Comment out config

* Revert contract changes

* Fix function

* Regenerate binds & update package path

* Format

* Format

* Add todo

* Update bindings

* Update script

* auto-unwrap ether

* Update smoke tests

* Add view keyword

* More refactoring

* Verify V2 digest for commitment

* Call Contract with value

* improve

* improve

* allow unlocking native ether

* improve token registration flows

* improve docs

* improve

* improve

* nit

* Fix smoke test for V2

* Fix test

* Fix unlock WETH

* Sync with contract

* Fix smoke test

* Update contracts/src/v1/Calls.sol

Co-authored-by: Clara van Staden <[email protected]>

* Apply suggestions from code review

Co-authored-by: Alistair Singh <[email protected]>

* Fix unlock WETH

* Suppress Error (6243): The "tload" instruction

* Merge recent changes

* Fix breaking tests

* Fix tests

* Filter order by rewardAddress to handle own delivery proof

* Fix test

* Add ofac check

---------

Co-authored-by: Vincent Geddes <[email protected]>
Co-authored-by: Clara van Staden <[email protected]>
Co-authored-by: Alistair Singh <[email protected]>
  • Loading branch information
4 people authored Feb 3, 2025
1 parent 2cfa585 commit 08e2bb0
Show file tree
Hide file tree
Showing 27 changed files with 13,464 additions and 3,625 deletions.
5 changes: 5 additions & 0 deletions relayer/chain/parachain/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"sync"
"time"

"github.com/snowfork/go-substrate-rpc-client/v4/rpc/author"
"github.com/snowfork/go-substrate-rpc-client/v4/types"
Expand Down Expand Up @@ -48,6 +49,10 @@ func NewParachainWriter(
}

func (wr *ParachainWriter) Start(ctx context.Context, eg *errgroup.Group) error {
err := wr.conn.ConnectWithHeartBeat(ctx, 30*time.Second)
if err != nil {
return err
}
nonce, err := wr.queryAccountNonce()
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions relayer/relays/parachain/digest_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ func ExtractCommitmentFromDigest(digest types.Digest) (*types.H256, error) {
for _, digestItem := range digest {
if digestItem.IsOther {
digestItemRawBytes := digestItem.AsOther
// Prefix 0 reserved for snowbridge
if digestItemRawBytes[0] == 0 {
// Prefix 1 reserved for snowbridge V2
if digestItemRawBytes[0] == 1 {
var commitment types.H256
err := types.DecodeFromBytes(digestItemRawBytes[1:], &commitment)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions relayer/relays/parachain/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import (
"github.com/snowfork/snowbridge/relayer/crypto/secp256k1"
"github.com/snowfork/snowbridge/relayer/crypto/sr25519"

"github.com/snowfork/snowbridge/relayer/ofac"
"github.com/snowfork/snowbridge/relayer/relays/beacon/header"
"github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer/api"
"github.com/snowfork/snowbridge/relayer/relays/beacon/protocol"
"github.com/snowfork/snowbridge/relayer/relays/beacon/store"
"github.com/snowfork/snowbridge/relayer/ofac"

log "github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -145,10 +145,10 @@ func (relay *Relay) Start(ctx context.Context, eg *errgroup.Group) error {
return err
}

//err = relay.startDeliverProof(ctx, eg)
//if err != nil {
// return err
//}
err = relay.startDeliverProof(ctx, eg)
if err != nil {
return err
}

log.Info("Current relay's ID:", relay.config.Schedule.ID)

Expand Down
213 changes: 213 additions & 0 deletions relayer/relays/parachain/parachain-writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package parachain

import (
"context"
"errors"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
"github.com/snowfork/snowbridge/relayer/chain/ethereum"
"github.com/snowfork/snowbridge/relayer/chain/parachain"
"github.com/snowfork/snowbridge/relayer/contracts"
"github.com/snowfork/snowbridge/relayer/relays/beacon/header"
"github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer/scale"
"github.com/snowfork/snowbridge/relayer/relays/util"
"golang.org/x/sync/errgroup"
)

func (relay *Relay) startDeliverProof(ctx context.Context, eg *errgroup.Group) error {
eg.Go(func() error {
for {
select {
case <-ctx.Done():
return nil
case <-time.After(60 * time.Second):
orders, err := relay.beefyListener.scanner.findOrderUndelivered(ctx)
if err != nil {
return fmt.Errorf("find undelivered order: %w", err)
}
rewardAddress, err := util.HexStringTo32Bytes(relay.config.RewardAddress)
if err != nil {
return fmt.Errorf("convert to reward address: %w", err)
}
for _, order := range orders {
event, err := relay.findEvent(ctx, order.Nonce)
if err != nil {
return fmt.Errorf("find event GatewayInboundMessageDispatched: %w", err)
}
if event.RewardAddress != rewardAddress {
log.Info("order is not from the current relayer, just ignore")
continue
}
err = relay.doSubmit(ctx, event)
if err != nil {
return fmt.Errorf("submit delivery proof for GatewayInboundMessageDispatched: %w", err)
}
}
}
}
})
return nil
}

func (relay *Relay) findEvent(
ctx context.Context,
nonce uint64,
) (*contracts.GatewayInboundMessageDispatched, error) {

const BlocksPerQuery = 4096

var event *contracts.GatewayInboundMessageDispatched

blockNumber, err := relay.ethereumConnWriter.Client().BlockNumber(ctx)
if err != nil {
return event, fmt.Errorf("get last block number: %w", err)
}

done := false

for {
var begin uint64
if blockNumber < BlocksPerQuery {
begin = 0
} else {
begin = blockNumber - BlocksPerQuery
}

opts := bind.FilterOpts{
Start: begin,
End: &blockNumber,
Context: ctx,
}

iter, err := relay.ethereumChannelWriter.gateway.FilterInboundMessageDispatched(&opts, []uint64{nonce})
if err != nil {
return event, fmt.Errorf("iter dispatch event: %w", err)
}

for {
more := iter.Next()
if !more {
err = iter.Error()
if err != nil {
return event, fmt.Errorf("iter dispatch event: %w", err)
}
break
}
if iter.Event.Nonce == nonce {
event = iter.Event
done = true
break
}
}

if done {
iter.Close()
}

blockNumber = begin

if done || begin == 0 {
break
}
}

return event, nil
}

func (relay *Relay) makeInboundMessage(
ctx context.Context,
headerCache *ethereum.HeaderCache,
event *contracts.GatewayInboundMessageDispatched,
) (*parachain.Message, error) {
receiptTrie, err := headerCache.GetReceiptTrie(ctx, event.Raw.BlockHash)
if err != nil {
log.WithFields(logrus.Fields{
"blockHash": event.Raw.BlockHash.Hex(),
"blockNumber": event.Raw.BlockNumber,
"txHash": event.Raw.TxHash.Hex(),
}).WithError(err).Error("Failed to get receipt trie for event")
return nil, err
}

msg, err := ethereum.MakeMessageFromEvent(&event.Raw, receiptTrie)
if err != nil {
log.WithFields(logrus.Fields{
"address": event.Raw.Address.Hex(),
"blockHash": event.Raw.BlockHash.Hex(),
"blockNumber": event.Raw.BlockNumber,
"txHash": event.Raw.TxHash.Hex(),
}).WithError(err).Error("Failed to generate message from ethereum event")
return nil, err
}

log.WithFields(logrus.Fields{
"blockHash": event.Raw.BlockHash.Hex(),
"blockNumber": event.Raw.BlockNumber,
"txHash": event.Raw.TxHash.Hex(),
}).Info("found message")

return msg, nil
}

func (relay *Relay) doSubmit(ctx context.Context, ev *contracts.GatewayInboundMessageDispatched) error {
inboundMsg, err := relay.makeInboundMessage(ctx, relay.headerCache, ev)
if err != nil {
return fmt.Errorf("make outgoing message: %w", err)
}

logger := log.WithFields(log.Fields{
"ethNonce": ev.Nonce,
"msgNonce": ev.Nonce,
"address": ev.Raw.Address.Hex(),
"blockHash": ev.Raw.BlockHash.Hex(),
"blockNumber": ev.Raw.BlockNumber,
"txHash": ev.Raw.TxHash.Hex(),
"txIndex": ev.Raw.TxIndex,
})

nextBlockNumber := new(big.Int).SetUint64(ev.Raw.BlockNumber + 1)

blockHeader, err := relay.ethereumConnWriter.Client().HeaderByNumber(ctx, nextBlockNumber)
if err != nil {
return fmt.Errorf("get block header: %w", err)
}

proof, err := relay.beaconHeader.FetchExecutionProof(*blockHeader.ParentBeaconRoot, false)
if errors.Is(err, header.ErrBeaconHeaderNotFinalized) || proof.HeaderPayload.ExecutionBranch == nil {
logger.Info("event block is not finalized yet")
return nil
}
if err != nil {
return fmt.Errorf("fetch execution header proof: %w", err)
}

err = relay.writeToParachain(ctx, proof, inboundMsg)
if err != nil {
return fmt.Errorf("write to parachain: %w", err)
}

logger.Info("v2 inbound message executed successfully")

return nil
}

func (relay *Relay) writeToParachain(ctx context.Context, proof scale.ProofPayload, inboundMsg *parachain.Message) error {
inboundMsg.Proof.ExecutionProof = proof.HeaderPayload

log.WithFields(logrus.Fields{
"EventLog": inboundMsg.EventLog,
"Proof": inboundMsg.Proof,
}).Debug("Generated message from Ethereum log")

err := relay.parachainWriter.WriteToParachainAndWatch(ctx, "EthereumOutboundQueueV2.submit_delivery_proof", inboundMsg)
if err != nil {
return fmt.Errorf("submit message to outbound queue v2: %w", err)
}

return nil
}
Loading

0 comments on commit 08e2bb0

Please sign in to comment.