diff --git a/relayer/cmd/generate_beacon_data.go b/relayer/cmd/generate_beacon_data.go index 06202d84e5..fddc2034d1 100644 --- a/relayer/cmd/generate_beacon_data.go +++ b/relayer/cmd/generate_beacon_data.go @@ -556,7 +556,7 @@ func getEthereumEvent(ctx context.Context, gatewayContract *contracts.Gateway, c for event == nil { log.Info("looking for Ethereum event") - iter, err := gatewayContract.FilterOutboundMessageAccepted(&opts, [][32]byte{channelID}, [][32]byte{}) + iter, err := gatewayContract.FilterOutboundMessageAccepted(&opts) if err != nil { return nil, err } diff --git a/relayer/cmd/run/parachain/command.go b/relayer/cmd/run/parachain/command.go index 7cda7872d5..e388f00db7 100644 --- a/relayer/cmd/run/parachain/command.go +++ b/relayer/cmd/run/parachain/command.go @@ -14,6 +14,7 @@ import ( "github.com/mitchellh/mapstructure" "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/parachain" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -21,10 +22,11 @@ import ( ) var ( - configFile string - privateKey string - privateKeyFile string - privateKeyID string + configFile string + privateKey string + privateKeyFile string + privateKeyID string + parachainPrivateKey string ) func Command() *cobra.Command { @@ -42,6 +44,8 @@ func Command() *cobra.Command { 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") + cmd.Flags().StringVar(¶chainPrivateKey, "substrate.private-key", "", "substrate private key") + return cmd } @@ -70,7 +74,12 @@ func run(_ *cobra.Command, _ []string) error { return err } - relay, err := parachain.NewRelay(&config, keypair) + keypair2, err := para.ResolvePrivateKey(parachainPrivateKey, "", "") + if err != nil { + return err + } + + relay, err := parachain.NewRelay(&config, keypair, keypair2) if err != nil { return err } diff --git a/relayer/relays/execution/main.go b/relayer/relays/execution/main.go index 80b5bbd8b1..a34bef6361 100644 --- a/relayer/relays/execution/main.go +++ b/relayer/relays/execution/main.go @@ -118,12 +118,12 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { "channelId": r.config.Source.ChannelID, }).Info("Polling Nonces") - paraNonces, err := r.fetchUnprocessedParachainNonces() + ethNonce, err := r.fetchEthereumNonce(ctx) if err != nil { return err } - ethNonce, err := r.fetchEthereumNonce(ctx) + paraNonces, err := r.fetchUnprocessedParachainNonces(ethNonce) if err != nil { return err } @@ -195,7 +195,8 @@ func (r *Relay) writeToParachain(ctx context.Context, proof scale.ProofPayload, return nil } -func (r *Relay) fetchUnprocessedParachainNonces() ([]uint64, error) { +func (r *Relay) fetchUnprocessedParachainNonces(latest uint64) ([]uint64, error) { + log.WithField("latest", latest).Info("latest nonce is") unprocessedNonces := []uint64{} startKey, err := types.CreateStorageKey(r.paraconn.Metadata(), "EthereumInboundQueue", "NonceBitmap", nil) if err != nil { @@ -221,10 +222,18 @@ func (r *Relay) fetchUnprocessedParachainNonces() ([]uint64, error) { continue // Skip empty buckets } - unprocessedNonces = extractUnprocessedNonces(value) + unprocessedNonces = extractUnprocessedNonces(value, latest) + + if len(unprocessedNonces) > 0 && unprocessedNonces[len(unprocessedNonces)-1] >= latest { + break + } + fmt.Printf("Unprocessed nonces for bucket %s: %v\n", key.Hex(), unprocessedNonces) } + log.WithFields(logrus.Fields{ + "unprocessedNonces": unprocessedNonces, + }).Debug("unprocessed nonces") return unprocessedNonces, nil } @@ -256,9 +265,13 @@ func checkBitState(bucketValue types.U128, bitPosition uint64) bool { return new(big.Int).And(bucketValue.Int, mask).Cmp(big.NewInt(0)) != 0 } -func extractUnprocessedNonces(bitmap types.U128) []uint64 { +func extractUnprocessedNonces(bitmap types.U128, latest uint64) []uint64 { var unprocessed []uint64 for i := 0; i < 128; i++ { + if uint64(i) > latest { + break // Stop processing if index exceeds `latest` + } + mask := new(big.Int).Lsh(big.NewInt(1), uint(i)) bit := new(big.Int).And(bitmap.Int, mask) if bit.Cmp(big.NewInt(0)) == 0 { @@ -496,7 +509,7 @@ func (r *Relay) doSubmit(ctx context.Context, ev *contracts.GatewayOutboundMessa // isMessageProcessed checks if the provided event nonce has already been processed on-chain. func (r *Relay) isMessageProcessed(eventNonce uint64) (bool, error) { - paraNonces, err := r.fetchUnprocessedParachainNonces() + paraNonces, err := r.fetchUnprocessedParachainNonces(eventNonce) if err != nil { return false, fmt.Errorf("fetch latest parachain nonce: %w", err) } diff --git a/relayer/relays/parachain/beefy-listener.go b/relayer/relays/parachain/beefy-listener.go index 41d0e5e292..7cc0f6e586 100644 --- a/relayer/relays/parachain/beefy-listener.go +++ b/relayer/relays/parachain/beefy-listener.go @@ -160,7 +160,7 @@ func (li *BeefyListener) doScan(ctx context.Context, beefyBlockNumber uint64) er } for _, task := range tasks { paraNonce := (*task.MessageProofs)[0].Message.Nonce - waitingPeriod := (paraNonce + li.scheduleConfig.TotalRelayerCount - li.scheduleConfig.ID) % li.scheduleConfig.TotalRelayerCount + waitingPeriod := (uint64(paraNonce) + li.scheduleConfig.TotalRelayerCount - li.scheduleConfig.ID) % li.scheduleConfig.TotalRelayerCount err = li.waitAndSend(ctx, task, waitingPeriod) if err != nil { return fmt.Errorf("wait task for nonce %d: %w", paraNonce, err) @@ -326,11 +326,11 @@ func (li *BeefyListener) waitAndSend(ctx context.Context, task *Task, waitingPer var cnt uint64 var err error for { - ethInboundNonce, err := li.scanner.findLatestNonce(ctx) + isRelayed, err := li.scanner.isNonceRelayed(ctx, uint64(paraNonce)) if err != nil { return err } - if ethInboundNonce >= paraNonce { + if isRelayed { log.Info(fmt.Sprintf("nonce %d picked up by another relayer, just skip", paraNonce)) return nil } diff --git a/relayer/relays/parachain/config.go b/relayer/relays/parachain/config.go index 97a9ebd3dd..eecd4084f8 100644 --- a/relayer/relays/parachain/config.go +++ b/relayer/relays/parachain/config.go @@ -5,20 +5,22 @@ import ( "fmt" "github.com/snowfork/snowbridge/relayer/config" + beaconconf "github.com/snowfork/snowbridge/relayer/relays/beacon/config" ) type Config struct { - Source SourceConfig `mapstructure:"source"` - Sink SinkConfig `mapstructure:"sink"` - Schedule ScheduleConfig `mapstructure:"schedule"` + Source SourceConfig `mapstructure:"source"` + Sink SinkConfig `mapstructure:"sink"` + Schedule ScheduleConfig `mapstructure:"schedule"` + RewardAddress string `mapstructure:"reward-address"` } type SourceConfig struct { - Polkadot config.PolkadotConfig `mapstructure:"polkadot"` - Parachain config.ParachainConfig `mapstructure:"parachain"` - Ethereum config.EthereumConfig `mapstructure:"ethereum"` - Contracts SourceContractsConfig `mapstructure:"contracts"` - ChannelID ChannelID `mapstructure:"channel-id"` + Polkadot config.PolkadotConfig `mapstructure:"polkadot"` + Parachain config.ParachainConfig `mapstructure:"parachain"` + Ethereum config.EthereumConfig `mapstructure:"ethereum"` + Contracts SourceContractsConfig `mapstructure:"contracts"` + Beacon beaconconf.BeaconConfig `mapstructure:"beacon"` } type SourceContractsConfig struct { @@ -76,9 +78,6 @@ func (c Config) Validate() error { if c.Source.Contracts.Gateway == "" { return fmt.Errorf("source contracts setting [Gateway] is not set") } - if c.Source.ChannelID == [32]byte{} { - return fmt.Errorf("source setting [channel-id] is not set") - } // Sink err = c.Sink.Ethereum.Validate() @@ -95,5 +94,9 @@ func (c Config) Validate() error { return fmt.Errorf("relay config: %w", err) } + if c.RewardAddress == "" { + return fmt.Errorf("reward address is not set") + } + return nil } diff --git a/relayer/relays/parachain/ethereum-writer.go b/relayer/relays/parachain/ethereum-writer.go index 1e4db8165d..772f925eb1 100644 --- a/relayer/relays/parachain/ethereum-writer.go +++ b/relayer/relays/parachain/ethereum-writer.go @@ -17,6 +17,7 @@ import ( "github.com/snowfork/snowbridge/relayer/chain/ethereum" "github.com/snowfork/snowbridge/relayer/contracts" "github.com/snowfork/snowbridge/relayer/crypto/keccak" + "github.com/snowfork/snowbridge/relayer/relays/util" gsrpcTypes "github.com/snowfork/go-substrate-rpc-client/v4/types" @@ -24,23 +25,26 @@ import ( ) type EthereumWriter struct { - config *SinkConfig - conn *ethereum.Connection - gateway *contracts.Gateway - tasks <-chan *Task - gatewayABI abi.ABI + config *SinkConfig + conn *ethereum.Connection + gateway *contracts.Gateway + tasks <-chan *Task + gatewayABI abi.ABI + relayConfig *Config } func NewEthereumWriter( config *SinkConfig, conn *ethereum.Connection, tasks <-chan *Task, + relayConfig *Config, ) (*EthereumWriter, error) { return &EthereumWriter{ - config: config, - conn: conn, - gateway: nil, - tasks: tasks, + config: config, + conn: conn, + gateway: nil, + tasks: tasks, + relayConfig: relayConfig, }, nil } @@ -143,8 +147,13 @@ func (wr *EthereumWriter) WriteChannel( LeafProofOrder: new(big.Int).SetUint64(proof.MMRProof.MerkleProofOrder), } - tx, err := wr.gateway.SubmitV1( - options, message, commitmentProof.Proof.InnerHashes, verificationProof, + rewardAddress, err := util.HexStringTo32Bytes(wr.relayConfig.RewardAddress) + if err != nil { + return fmt.Errorf("convert to reward address: %w", err) + } + + tx, err := wr.gateway.V2Submit( + options, message, commitmentProof.Proof.InnerHashes, verificationProof, rewardAddress, ) if err != nil { return fmt.Errorf("send transaction Gateway.submit: %w", err) @@ -183,9 +192,8 @@ func (wr *EthereumWriter) WriteChannel( return fmt.Errorf("unpack event log: %w", err) } log.WithFields(log.Fields{ - "channelID": Hex(holder.ChannelID[:]), - "nonce": holder.Nonce, - "success": holder.Success, + "nonce": holder.Nonce, + "success": holder.Success, }).Info("Message dispatched") } } diff --git a/relayer/relays/parachain/logger.go b/relayer/relays/parachain/logger.go index 7f154e8846..7b450a73dc 100644 --- a/relayer/relays/parachain/logger.go +++ b/relayer/relays/parachain/logger.go @@ -43,10 +43,9 @@ func (wr *EthereumWriter) logFieldsForSubmission( params := log.Fields{ "message": log.Fields{ - "channelID": Hex(message.ChannelID[:]), - "nonce": message.Nonce, - "command": message.Command, - "params": Hex(message.Params), + "nonce": message.Nonce, + "commands": message.Commands, + "origin": Hex(message.Origin[:]), }, "messageProof": messageProofHexes, "proof": log.Fields{ diff --git a/relayer/relays/parachain/main.go b/relayer/relays/parachain/main.go index d66da938fd..87d98334f5 100644 --- a/relayer/relays/parachain/main.go +++ b/relayer/relays/parachain/main.go @@ -11,6 +11,12 @@ import ( "github.com/snowfork/snowbridge/relayer/chain/parachain" "github.com/snowfork/snowbridge/relayer/chain/relaychain" "github.com/snowfork/snowbridge/relayer/crypto/secp256k1" + "github.com/snowfork/snowbridge/relayer/crypto/sr25519" + + "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" log "github.com/sirupsen/logrus" ) @@ -23,9 +29,12 @@ type Relay struct { ethereumConnBeefy *ethereum.Connection ethereumChannelWriter *EthereumWriter beefyListener *BeefyListener + parachainWriter *parachain.ParachainWriter + beaconHeader *header.Header + headerCache *ethereum.HeaderCache } -func NewRelay(config *Config, keypair *secp256k1.Keypair) (*Relay, error) { +func NewRelay(config *Config, keypair *secp256k1.Keypair, keypair2 *sr25519.Keypair) (*Relay, error) { log.Info("Creating worker") parachainConn := parachain.NewConnection(config.Source.Parachain.Endpoint, nil) @@ -41,6 +50,7 @@ func NewRelay(config *Config, keypair *secp256k1.Keypair) (*Relay, error) { &config.Sink, ethereumConnWriter, tasks, + config, ) if err != nil { return nil, err @@ -55,6 +65,30 @@ func NewRelay(config *Config, keypair *secp256k1.Keypair) (*Relay, error) { tasks, ) + parachainWriterConn := parachain.NewConnection(config.Source.Parachain.Endpoint, keypair2.AsKeyringPair()) + + parachainWriter := parachain.NewParachainWriter( + parachainWriterConn, + 8, + ) + headerCache, err := ethereum.NewHeaderBlockCache( + ðereum.DefaultBlockLoader{Conn: ethereumConnWriter}, + ) + if err != nil { + return nil, err + } + p := protocol.New(config.Source.Beacon.Spec, 20) + store := store.New(config.Source.Beacon.DataStore.Location, config.Source.Beacon.DataStore.MaxEntries, *p) + store.Connect() + beaconAPI := api.NewBeaconClient(config.Source.Beacon.Endpoint, config.Source.Beacon.StateEndpoint) + beaconHeader := header.New( + parachainWriter, + beaconAPI, + config.Source.Beacon.Spec, + &store, + p, + 0, // setting is not used in the execution relay + ) return &Relay{ config: config, parachainConn: parachainConn, @@ -63,6 +97,9 @@ func NewRelay(config *Config, keypair *secp256k1.Keypair) (*Relay, error) { ethereumConnBeefy: ethereumConnBeefy, ethereumChannelWriter: ethereumChannelWriter, beefyListener: beefyListener, + parachainWriter: parachainWriter, + beaconHeader: &beaconHeader, + headerCache: headerCache, }, nil } @@ -99,6 +136,16 @@ func (relay *Relay) Start(ctx context.Context, eg *errgroup.Group) error { return err } + err = relay.parachainWriter.Start(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) return nil diff --git a/relayer/relays/parachain/scanner.go b/relayer/relays/parachain/scanner.go index 287ca93118..a97097c6cd 100644 --- a/relayer/relays/parachain/scanner.go +++ b/relayer/relays/parachain/scanner.go @@ -24,18 +24,12 @@ type Scanner struct { relayConn *relaychain.Connection paraConn *parachain.Connection paraID uint32 - tasks chan<- *Task } -// Scans for all parachain message commitments for the configured parachain channelID that need to be relayed and can be +// Scans for all parachain message commitments that need to be relayed and can be // proven using the MMR root at the specified beefyBlockNumber of the relay chain. -// -// The algorithm works roughly like this: -// 1. Fetch channel nonce on both sides of the bridge and compare them -// 2. If the nonce on the parachain side is larger that means messages need to be relayed. If not then exit early. -// 3. Scan parachain blocks to figure out exactly which commitments need to be relayed. -// 4. For all the parachain blocks with unsettled commitments, determine the relay chain block number in which the -// parachain block was included. +// The algorithm fetch PendingOrders storage in OutboundQueue of BH and +// just relay each order which has not been processed on Ethereum yet. func (s *Scanner) Scan(ctx context.Context, beefyBlockNumber uint64) ([]*Task, error) { // fetch last parachain header that was finalized *before* the BEEFY block beefyBlockMinusOneHash, err := s.relayConn.API().RPC.Chain.GetBlockHash(uint64(beefyBlockNumber - 1)) @@ -57,7 +51,7 @@ func (s *Scanner) Scan(ctx context.Context, beefyBlockNumber uint64) ([]*Task, e return nil, fmt.Errorf("fetch parachain block hash for block %v: %w", paraBlockNumber, err) } - tasks, err := s.findTasks(ctx, paraBlockNumber, paraBlockHash) + tasks, err := s.findTasks(ctx, paraBlockHash) if err != nil { return nil, err } @@ -68,49 +62,32 @@ func (s *Scanner) Scan(ctx context.Context, beefyBlockNumber uint64) ([]*Task, e // findTasks finds all the message commitments which need to be relayed func (s *Scanner) findTasks( ctx context.Context, - paraBlock uint64, paraHash types.Hash, ) ([]*Task, error) { - // Fetch latest nonce in ethereum gateway - ethInboundNonce, err := s.findLatestNonce(ctx) - log.WithFields(log.Fields{ - "nonce": ethInboundNonce, - "channelID": s.config.ChannelID, - }).Info("Checked latest nonce delivered to ethereum gateway") - - // Fetch latest nonce in parachain outbound queue - paraNonceKey, err := types.CreateStorageKey(s.paraConn.Metadata(), "EthereumOutboundQueue", "Nonce", s.config.ChannelID[:], nil) + // Fetch PendingOrders storage in parachain outbound queue + storageKey := types.NewStorageKey(types.CreateStorageKeyPrefix("EthereumOutboundQueueV2", "PendingOrders")) + keys, err := s.paraConn.API().RPC.State.GetKeys(storageKey, paraHash) if err != nil { - return nil, fmt.Errorf("create storage key for parachain outbound queue nonce with channelID '%v': %w", s.config.ChannelID, err) + return nil, fmt.Errorf("fetch nonces from PendingOrders start with key '%v' and hash '%v': %w", storageKey, paraHash, err) } - var paraNonce types.U64 - ok, err := s.paraConn.API().RPC.State.GetStorage(paraNonceKey, ¶Nonce, paraHash) - if err != nil { - return nil, fmt.Errorf("fetch nonce from parachain outbound queue with key '%v' and hash '%v': %w", paraNonceKey, paraHash, err) - } - if !ok { - log.WithFields(log.Fields{ - "nonceKey": paraNonceKey, - "blockHash": paraHash, - }).Info("Fetched empty nonce from parachain outbound queue") - paraNonce = 0 - } - log.WithFields(log.Fields{ - "nonce": uint64(paraNonce), - "channelID": s.config.ChannelID, - }).Info("Checked latest nonce generated by parachain outbound queue") - - if !(uint64(paraNonce) > ethInboundNonce) { - return nil, nil + var pendingOrders []PendingOrder + for _, key := range keys { + var pendingOrder PendingOrder + value, err := s.paraConn.API().RPC.State.GetStorageRaw(key, paraHash) + if err != nil { + return nil, fmt.Errorf("fetch value of pendingOrder with key '%v' and hash '%v': %w", key, paraHash, err) + } + decoder := scale.NewDecoder(bytes.NewReader(*value)) + err = decoder.Decode(&pendingOrder) + if err != nil { + return nil, fmt.Errorf("decode order error: %w", err) + } + pendingOrders = append(pendingOrders, pendingOrder) } - log.Info("Nonces are mismatched, scanning for commitments that need to be relayed") - - tasks, err := s.findTasksImpl( + tasks, err := s.filterTasks( ctx, - paraBlock, - types.H256(s.config.ChannelID), - ethInboundNonce+1, + pendingOrders, ) if err != nil { return nil, err @@ -121,33 +98,36 @@ func (s *Scanner) findTasks( return tasks, nil } -// Searches from the given parachain block number backwards on the given channel (landID) for all outstanding -// commitments until it finds the given startingNonce -func (s *Scanner) findTasksImpl( - _ context.Context, - lastParaBlockNumber uint64, - channelID types.H256, - startingNonce uint64, +// Filter profitable and undelivered orders, convert to tasks +// Todo: check order is profitable or not with some price oracle +// or some fee estimation api +func (s *Scanner) filterTasks( + ctx context.Context, + pendingNonces []PendingOrder, ) ([]*Task, error) { - log.WithFields(log.Fields{ - "channelID": channelID, - "nonce": startingNonce, - "latestBlockNumber": lastParaBlockNumber, - }).Debug("Searching backwards from latest block on parachain to find block with nonce") - - messagesKey, err := types.CreateStorageKey(s.paraConn.Metadata(), "EthereumOutboundQueue", "Messages", nil, nil) - if err != nil { - return nil, fmt.Errorf("create storage key: %w", err) - } - scanOutboundQueueDone := false var tasks []*Task - for currentBlockNumber := lastParaBlockNumber; currentBlockNumber > 0; currentBlockNumber-- { - if scanOutboundQueueDone { - break + for _, pending := range pendingNonces { + + isRelayed, err := s.isNonceRelayed(ctx, uint64(pending.Nonce)) + if err != nil { + return nil, fmt.Errorf("check nonce relayed: %w", err) + } + if isRelayed { + log.WithFields(log.Fields{ + "nonce": uint64(pending.Nonce), + }).Debug("already relayed, just skip") + continue } + messagesKey, err := types.CreateStorageKey(s.paraConn.Metadata(), "EthereumOutboundQueueV2", "Messages", nil, nil) + if err != nil { + return nil, fmt.Errorf("create storage key: %w", err) + } + + currentBlockNumber := uint64(pending.BlockNumber) + log.WithFields(log.Fields{ "blockNumber": currentBlockNumber, }).Debug("Checking header") @@ -196,16 +176,12 @@ func (s *Scanner) findTasksImpl( s.paraConn.API(), blockHash, *commitmentHash, - startingNonce, - channelID, messages, ) if err != nil { return nil, err } - scanOutboundQueueDone = result.scanDone - if len(result.proofs) > 0 { task := Task{ Header: header, @@ -217,11 +193,6 @@ func (s *Scanner) findTasksImpl( } } - // Reverse tasks, effectively sorting by ascending block number - for i, j := 0, len(tasks)-1; i < j; i, j = i+1, j-1 { - tasks[i], tasks[j] = tasks[j], tasks[i] - } - return tasks, nil } @@ -324,52 +295,15 @@ func scanForOutboundQueueProofs( api *gsrpc.SubstrateAPI, blockHash types.Hash, commitmentHash types.H256, - startingNonce uint64, - channelID types.H256, messages []OutboundQueueMessage, ) (*struct { - proofs []MessageProof - scanDone bool + proofs []MessageProof }, error) { - var scanDone bool proofs := []MessageProof{} - // There are 4 cases here: - // 1. There are no messages to relay, continue - // 2. All messages have been relayed, halt - // 3. There are messages to relay and *none* have been sent, continue - // 4. There are messages to relay and *some* have been sent, continue - - // Messages are sorted by nonce ascending. Traverse them backwards to get nonce descending. - // This allows us to distinguish between cases 2 & 4 above: - // - When nonce is ascending, we find a message where messageNonce < startingNonce but later messages may have a - // higher nonce. - // - When nonce is descending, we either find the first message has messageNonce < startingNonce (all messages have - // been relayed) or we reach messageNonce == startingNonce, potentially in an earlier block. - // - // eg. m1 has nonce 1 and has been relayed. We're looking for messages from nonce 2 upwards in [m1, m2, m3] (m2 and - // m3). With nonce ascending, m1.nonce < 2 but we can't assume case 2 yet (where all messages have been relayed). - // With nonce descending, we find m3, then m2 where m2.nonce == 2. - for i := len(messages) - 1; i >= 0; i-- { message := messages[i] - if message.ChannelID != channelID { - continue - } - - messageNonce := message.Nonce - - // This case will be hit when there are no new messages to relay. - if messageNonce < startingNonce { - log.Debugf( - "Halting scan for channelID '%v'. Messages not committed yet on outbound channel", - message.ChannelID.Hex(), - ) - scanDone = true - break - } - messageProof, err := fetchMessageProof(api, blockHash, uint64(i), message) if err != nil { return nil, err @@ -377,8 +311,7 @@ func scanForOutboundQueueProofs( // Check that the merkle root in the proof is the same as the digest hash from the header if messageProof.Proof.Root != commitmentHash { return nil, fmt.Errorf( - "Halting scan for channelID '%v'. Outbound queue proof root '%v' doesn't match digest item's commitment hash '%v'", - message.ChannelID.Hex(), + "Halting scan Outbound queue proof root '%v' doesn't match digest item's commitment hash '%v'", messageProof.Proof.Root, commitmentHash, ) @@ -386,24 +319,12 @@ func scanForOutboundQueueProofs( // Collect these commitments proofs = append(proofs, messageProof) - - if messageNonce == startingNonce { - // Terminate scan - scanDone = true - } - } - - // Reverse proofs, effectively sorting by nonce ascending - for i, j := 0, len(proofs)-1; i < j; i, j = i+1, j-1 { - proofs[i], proofs[j] = proofs[j], proofs[i] } return &struct { - proofs []MessageProof - scanDone bool + proofs []MessageProof }{ - proofs: proofs, - scanDone: scanDone, + proofs: proofs, }, nil } @@ -414,53 +335,89 @@ func fetchMessageProof( message OutboundQueueMessage, ) (MessageProof, error) { var proofHex string + var proof MessageProof params, err := types.EncodeToHexString(messageIndex) if err != nil { - return MessageProof{}, fmt.Errorf("encode params: %w", err) + return proof, fmt.Errorf("encode params: %w", err) } - err = api.Client.Call(&proofHex, "state_call", "OutboundQueueApi_prove_message", params, blockHash.Hex()) + err = api.Client.Call(&proofHex, "state_call", "OutboundQueueApiV2_prove_message", params, blockHash.Hex()) if err != nil { - return MessageProof{}, fmt.Errorf("call RPC OutboundQueueApi_prove_message(%v, %v): %w", messageIndex, blockHash, err) + return proof, fmt.Errorf("call RPC OutboundQueueApi_prove_message(%v, %v): %w", messageIndex, blockHash, err) } var optionRawMerkleProof OptionRawMerkleProof err = types.DecodeFromHexString(proofHex, &optionRawMerkleProof) if err != nil { - return MessageProof{}, fmt.Errorf("decode merkle proof: %w", err) + return proof, fmt.Errorf("decode merkle proof: %w", err) } if !optionRawMerkleProof.HasValue { - return MessageProof{}, fmt.Errorf("retrieve proof failed") + return proof, fmt.Errorf("retrieve proof failed") } - proof, err := NewMerkleProof(optionRawMerkleProof.Value) + merkleProof, err := NewMerkleProof(optionRawMerkleProof.Value) if err != nil { - return MessageProof{}, fmt.Errorf("decode merkle proof: %w", err) + return proof, fmt.Errorf("decode merkle proof: %w", err) } - return MessageProof{Message: message, Proof: proof}, nil + return MessageProof{Message: message, Proof: merkleProof}, nil } -func (s *Scanner) findLatestNonce(ctx context.Context) (uint64, error) { - // Fetch latest nonce in ethereum gateway +func (s *Scanner) isNonceRelayed(ctx context.Context, nonce uint64) (bool, error) { + var isRelayed bool gatewayAddress := common.HexToAddress(s.config.Contracts.Gateway) gatewayContract, err := contracts.NewGateway( gatewayAddress, s.ethConn.Client(), ) if err != nil { - return 0, fmt.Errorf("create gateway contract for address '%v': %w", gatewayAddress, err) + return isRelayed, fmt.Errorf("create gateway contract for address '%v': %w", gatewayAddress, err) } options := bind.CallOpts{ Pending: true, Context: ctx, } - ethInboundNonce, _, err := gatewayContract.ChannelNoncesOf(&options, s.config.ChannelID) + isRelayed, err = gatewayContract.V2IsDispatched(&options, nonce) + if err != nil { + return isRelayed, fmt.Errorf("check nonce from gateway contract: %w", err) + } + return isRelayed, nil +} + +func (s *Scanner) findOrderUndelivered( + ctx context.Context, +) ([]*PendingOrder, error) { + storageKey := types.NewStorageKey(types.CreateStorageKeyPrefix("EthereumOutboundQueueV2", "PendingOrders")) + keys, err := s.paraConn.API().RPC.State.GetKeysLatest(storageKey) if err != nil { - return 0, fmt.Errorf("fetch nonce from gateway contract for channelID '%v': %w", s.config.ChannelID, err) + return nil, fmt.Errorf("fetch nonces from PendingOrders start with key '%v': %w", storageKey, err) } - return ethInboundNonce, err + var undeliveredOrders []*PendingOrder + for _, key := range keys { + var undeliveredOrder PendingOrder + value, err := s.paraConn.API().RPC.State.GetStorageRawLatest(key) + if err != nil { + return nil, fmt.Errorf("fetch value of pendingOrder with key '%v': %w", key, err) + } + decoder := scale.NewDecoder(bytes.NewReader(*value)) + err = decoder.Decode(&undeliveredOrder) + if err != nil { + return nil, fmt.Errorf("decode order error: %w", err) + } + isRelayed, err := s.isNonceRelayed(ctx, uint64(undeliveredOrder.Nonce)) + if err != nil { + return nil, fmt.Errorf("check nonce relayed: %w", err) + } + if isRelayed { + log.WithFields(log.Fields{ + "nonce": uint64(undeliveredOrder.Nonce), + }).Debug("Relayed but not delivered to BH") + undeliveredOrders = append(undeliveredOrders, &undeliveredOrder) + } + } + + return undeliveredOrders, nil } diff --git a/relayer/relays/parachain/types.go b/relayer/relays/parachain/types.go index 0e5da77d39..078d5dcfbe 100644 --- a/relayer/relays/parachain/types.go +++ b/relayer/relays/parachain/types.go @@ -85,82 +85,65 @@ func NewMerkleProof(rawProof RawMerkleProof) (MerkleProof, error) { } type OutboundQueueMessage struct { - ChannelID types.H256 - Nonce uint64 - Command uint8 - Params []byte - MaxDispatchGas uint64 - MaxFeePerGas types.U128 - Reward types.U128 - ID types.Bytes32 + Origin types.H256 + Nonce types.U64 + Commands []CommandWrapper +} + +type CommandWrapper struct { + Kind types.U8 + MaxDispatchGas types.U64 + Params types.Bytes +} + +func (r CommandWrapper) IntoCommand() contracts.Command { + return contracts.Command{ + Kind: uint8(r.Kind), + Gas: uint64(r.MaxDispatchGas), + Payload: r.Params, + } } func (m OutboundQueueMessage) IntoInboundMessage() contracts.InboundMessage { + var commands []contracts.Command + for _, command := range m.Commands { + commands = append(commands, command.IntoCommand()) + } return contracts.InboundMessage{ - ChannelID: m.ChannelID, - Nonce: m.Nonce, - Command: m.Command, - Params: m.Params, - MaxDispatchGas: m.MaxDispatchGas, - MaxFeePerGas: m.MaxFeePerGas.Int, - Reward: m.Reward.Int, - Id: m.ID, + Origin: m.Origin, + Nonce: uint64(m.Nonce), + Commands: commands, } } -func (m OutboundQueueMessage) Encode(encoder scale.Encoder) error { - encoder.Encode(m.ChannelID) - encoder.EncodeUintCompact(*big.NewInt(0).SetUint64(m.Nonce)) - encoder.Encode(m.Command) - encoder.Encode(m.Params) - encoder.EncodeUintCompact(*big.NewInt(0).SetUint64(m.MaxDispatchGas)) - encoder.EncodeUintCompact(*m.MaxFeePerGas.Int) - encoder.EncodeUintCompact(*m.Reward.Int) - encoder.Encode(m.ID) - return nil +type MessageProof struct { + Message OutboundQueueMessage + Proof MerkleProof } -func (m *OutboundQueueMessage) Decode(decoder scale.Decoder) error { - err := decoder.Decode(&m.ChannelID) - if err != nil { - return err - } - decoded, err := decoder.DecodeUintCompact() - if err != nil { - return err - } - m.Nonce = decoded.Uint64() - err = decoder.Decode(&m.Command) - if err != nil { - return err - } - err = decoder.Decode(&m.Params) - if err != nil { - return err - } - decoded, err = decoder.DecodeUintCompact() - if err != nil { - return err - } - m.MaxDispatchGas = decoded.Uint64() - decoded, err = decoder.DecodeUintCompact() +type PendingOrder struct { + Nonce uint64 + BlockNumber uint32 + Fee big.Int +} + +func (p *PendingOrder) Decode(decoder scale.Decoder) error { + var nonce types.U64 + err := decoder.Decode(&nonce) if err != nil { return err } - m.MaxFeePerGas = types.U128{Int: decoded} - decoded, err = decoder.DecodeUintCompact() + p.Nonce = uint64(nonce) + var blockNumber types.U32 + err = decoder.Decode(&blockNumber) if err != nil { return err } - m.Reward = types.U128{Int: decoded} - err = decoder.Decode(&m.ID) + p.BlockNumber = uint32(blockNumber) + decoded, err := decoder.DecodeUintCompact() if err != nil { return err } + p.Fee = *types.U128{Int: decoded}.Int return nil } - -type MessageProof struct { - Message OutboundQueueMessage - Proof MerkleProof -} diff --git a/smoketest/src/constants.rs b/smoketest/src/constants.rs index ccd251270b..9e047586da 100644 --- a/smoketest/src/constants.rs +++ b/smoketest/src/constants.rs @@ -26,7 +26,7 @@ pub const ETHEREUM_ADDRESS: [u8; 20] = hex!("90A987B944Cb1dCcE5564e5FDeCD7a54D3d // The deployment addresses of the following contracts are stable in our E2E env, unless we modify // the order in contracts are deployed in DeployScript.sol. pub const DEFAULT_GATEWAY_PROXY_CONTRACT: [u8; 20] = - hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); + hex!("8cf6147918a5cbb672703f879f385036f8793a24"); pub const DEFAULT_WETH_CONTRACT: [u8; 20] = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23"); pub const AGENT_EXECUTOR_CONTRACT: [u8; 20] = hex!("Fc97A6197dc90bef6bbEFD672742Ed75E9768553"); diff --git a/smoketest/src/helper.rs b/smoketest/src/helper.rs index 136067d959..23cd1ad3e3 100644 --- a/smoketest/src/helper.rs +++ b/smoketest/src/helper.rs @@ -1,6 +1,6 @@ use crate::{ constants::*, - contracts::i_gateway, + contracts::i_gateway_v1 as i_gateway, parachains::{ bridgehub::{self, api::runtime_types::snowbridge_core::outbound::v1::OperatingMode}, relaychain, @@ -36,9 +36,9 @@ use std::{ops::Deref, sync::Arc, time::Duration}; use subxt::{ config::DefaultExtrinsicParams, events::StaticEvent, - ext::sp_core::{sr25519::Pair, Pair as PairT, H160}, + ext::sp_core::{sr25519::Pair, Pair as PairT}, tx::{PairSigner, Payload}, - utils::H256, + utils::{H160, H256}, Config, OnlineClient, PolkadotConfig, }; @@ -127,7 +127,7 @@ pub async fn wait_for_bridgehub_event( pub async fn wait_for_ethereum_event(ethereum_client: &Box>>) { let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); - let gateway = i_gateway::IGateway::new(gateway_addr, (*ethereum_client).deref().clone()); + let gateway = i_gateway::IGatewayV1::new(gateway_addr, (*ethereum_client).deref().clone()); let wait_for_blocks = 500; let mut stream = ethereum_client.subscribe_blocks().await.unwrap().take(wait_for_blocks); @@ -278,7 +278,7 @@ pub async fn fund_agent(agent_id: [u8; 32]) -> Result<(), Box() { + println!("Created event found in assethub block {}.", block.number()); + let created = created.unwrap(); + assert_eq!(created.asset_id.encode(), expected_asset_id.encode()); + assert_eq!(created.creator, expected_creator); + assert_eq!(created.owner, expected_owner); + created_event_found = true; + } + if created_event_found { + break + } + } + assert!(created_event_found) +} diff --git a/web/packages/test-helpers/src/generateContractInfo.ts b/web/packages/test-helpers/src/generateContractInfo.ts index 26859445ca..cb2baab594 100644 --- a/web/packages/test-helpers/src/generateContractInfo.ts +++ b/web/packages/test-helpers/src/generateContractInfo.ts @@ -25,11 +25,13 @@ const run = async () => { let contractName: string = transaction.contractName if (contractName) { let contractInfo: ContractInfo = { address: transaction.contractAddress } + let contactNameWithoutVersion = contractName.replace(/v\d+$/i, ""); + let contractPath = path.join(BuildInfoDir, contractName + ".sol", contractName + ".json"); + if (!fs.existsSync(contractPath)) { + contractPath = path.join(BuildInfoDir, contactNameWithoutVersion + ".sol", contractName + ".json"); + } let contractBuildingInfo = JSON.parse( - fs.readFileSync( - path.join(BuildInfoDir, contractName + ".sol", contractName + ".json"), - "utf8" - ) + fs.readFileSync(contractPath, "utf8") ) contractInfo.abi = contractBuildingInfo.abi contracts[contractName] = contractInfo diff --git a/web/packages/test/scripts/set-env.sh b/web/packages/test/scripts/set-env.sh index 1ab1d6c278..aa1f9bfd51 100755 --- a/web/packages/test/scripts/set-env.sh +++ b/web/packages/test/scripts/set-env.sh @@ -113,7 +113,7 @@ export REMOTE_REWARD="${REMOTE_REWARD:-1000000000000000}" export BRIDGE_HUB_INITIAL_DEPOSIT="${ETH_BRIDGE_HUB_INITIAL_DEPOSIT:-10000000000000000000}" export GATEWAY_STORAGE_KEY="${GATEWAY_STORAGE_KEY:-0xaed97c7854d601808b98ae43079dafb3}" -export GATEWAY_PROXY_CONTRACT="${GATEWAY_PROXY_CONTRACT:-0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d}" +export GATEWAY_PROXY_CONTRACT="${GATEWAY_PROXY_CONTRACT:-0x8cf6147918a5cbb672703f879f385036f8793a24}" address_for() { jq -r ".contracts.${1}.address" "$output_dir/contracts.json" diff --git a/web/packages/test/scripts/start-relayer.sh b/web/packages/test/scripts/start-relayer.sh index ef08981fbc..e44efbb84f 100755 --- a/web/packages/test/scripts/start-relayer.sh +++ b/web/packages/test/scripts/start-relayer.sh @@ -4,135 +4,6 @@ set -eu source scripts/set-env.sh config_relayer() { - # Configure beefy relay - jq \ - --arg k1 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .sink.contracts.BeefyClient = $k1 - | .sink.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum."gas-limit" = $eth_gas_limit - ' \ - config/beefy-relay.json >$output_dir/beefy-relay.json - - # Configure parachain relay (primary governance) - jq \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg k2 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_writer_endpoint $eth_writer_endpoint \ - --arg channelID $PRIMARY_GOVERNANCE_CHANNEL_ID \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .source.contracts.Gateway = $k1 - | .source.contracts.BeefyClient = $k2 - | .sink.contracts.Gateway = $k1 - | .source.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum.endpoint = $eth_writer_endpoint - | .sink.ethereum."gas-limit" = $eth_gas_limit - | .source."channel-id" = $channelID - ' \ - config/parachain-relay.json >$output_dir/parachain-relay-bridge-hub-01.json - - # Configure parachain relay (secondary governance) - jq \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg k2 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_writer_endpoint $eth_writer_endpoint \ - --arg channelID $SECONDARY_GOVERNANCE_CHANNEL_ID \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .source.contracts.Gateway = $k1 - | .source.contracts.BeefyClient = $k2 - | .sink.contracts.Gateway = $k1 - | .source.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum.endpoint = $eth_writer_endpoint - | .sink.ethereum."gas-limit" = $eth_gas_limit - | .source."channel-id" = $channelID - ' \ - config/parachain-relay.json >$output_dir/parachain-relay-bridge-hub-02.json - - # Configure parachain relay (asset hub)-0 - jq \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg k2 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_writer_endpoint $eth_writer_endpoint \ - --arg channelID $ASSET_HUB_CHANNEL_ID \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .source.contracts.Gateway = $k1 - | .source.contracts.BeefyClient = $k2 - | .sink.contracts.Gateway = $k1 - | .source.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum.endpoint = $eth_writer_endpoint - | .sink.ethereum."gas-limit" = $eth_gas_limit - | .source."channel-id" = $channelID - | .schedule.id = 0 - ' \ - config/parachain-relay.json >$output_dir/parachain-relay-asset-hub-0.json - - # Configure parachain relay (asset hub)-1 - jq \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg k2 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_writer_endpoint $eth_writer_endpoint \ - --arg channelID $ASSET_HUB_CHANNEL_ID \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .source.contracts.Gateway = $k1 - | .source.contracts.BeefyClient = $k2 - | .sink.contracts.Gateway = $k1 - | .source.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum.endpoint = $eth_writer_endpoint - | .sink.ethereum."gas-limit" = $eth_gas_limit - | .source."channel-id" = $channelID - | .schedule.id = 1 - ' \ - config/parachain-relay.json >$output_dir/parachain-relay-asset-hub-1.json - - # Configure parachain relay (asset hub)-2 - jq \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg k2 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_writer_endpoint $eth_writer_endpoint \ - --arg channelID $ASSET_HUB_CHANNEL_ID \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .source.contracts.Gateway = $k1 - | .source.contracts.BeefyClient = $k2 - | .sink.contracts.Gateway = $k1 - | .source.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum.endpoint = $eth_writer_endpoint - | .sink.ethereum."gas-limit" = $eth_gas_limit - | .source."channel-id" = $channelID - | .schedule.id = 2 - ' \ - config/parachain-relay.json >$output_dir/parachain-relay-asset-hub-2.json - - # Configure parachain relay (penpal) - jq \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg k2 "$(address_for BeefyClient)" \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg eth_writer_endpoint $eth_writer_endpoint \ - --arg channelID $PENPAL_CHANNEL_ID \ - --arg eth_gas_limit $eth_gas_limit \ - ' - .source.contracts.Gateway = $k1 - | .source.contracts.BeefyClient = $k2 - | .sink.contracts.Gateway = $k1 - | .source.ethereum.endpoint = $eth_endpoint_ws - | .sink.ethereum.endpoint = $eth_writer_endpoint - | .sink.ethereum."gas-limit" = $eth_gas_limit - | .source."channel-id" = $channelID - ' \ - config/parachain-relay.json >$output_dir/parachain-relay-penpal.json - # Configure beacon relay local deneb_forked_epoch=132608 if [ "$eth_fast_mode" == "true" ]; then @@ -147,7 +18,7 @@ config_relayer() { ' \ config/beacon-relay.json >$output_dir/beacon-relay.json - # Configure execution relay for assethub-0 + # Configure execution relay jq \ --arg eth_endpoint_ws $eth_endpoint_ws \ --arg k1 "$(address_for GatewayProxy)" \ @@ -158,140 +29,11 @@ config_relayer() { | .source."channel-id" = $channelID | .schedule.id = 0 ' \ - config/execution-relay.json >$output_dir/execution-relay-asset-hub-0.json - - # Configure execution relay for assethub-1 - jq \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg channelID $ASSET_HUB_CHANNEL_ID \ - ' - .source.ethereum.endpoint = $eth_endpoint_ws - | .source.contracts.Gateway = $k1 - | .source."channel-id" = $channelID - | .schedule.id = 1 - ' \ - config/execution-relay.json >$output_dir/execution-relay-asset-hub-1.json - - # Configure execution relay for assethub-2 - jq \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg channelID $ASSET_HUB_CHANNEL_ID \ - ' - .source.ethereum.endpoint = $eth_endpoint_ws - | .source.contracts.Gateway = $k1 - | .source."channel-id" = $channelID - | .schedule.id = 2 - ' \ - config/execution-relay.json >$output_dir/execution-relay-asset-hub-2.json - - # Configure execution relay for penpal - jq \ - --arg eth_endpoint_ws $eth_endpoint_ws \ - --arg k1 "$(address_for GatewayProxy)" \ - --arg channelID $PENPAL_CHANNEL_ID \ - ' - .source.ethereum.endpoint = $eth_endpoint_ws - | .source.contracts.Gateway = $k1 - | .source."channel-id" = $channelID - ' \ - config/execution-relay.json >$output_dir/execution-relay-penpal.json + config/execution-relay.json >$output_dir/execution-relay.json } start_relayer() { echo "Starting relay services" - # Launch beefy relay - ( - : >"$output_dir"/beefy-relay.log - while :; do - echo "Starting beefy relay at $(date)" - "${relay_bin}" run beefy \ - --config "$output_dir/beefy-relay.json" \ - --ethereum.private-key $beefy_relay_eth_key \ - >>"$output_dir"/beefy-relay.log 2>&1 || true - sleep 20 - done - ) & - - # Launch parachain relay for bridgehub (primary governance) - ( - : >"$output_dir"/parachain-relay-bridge-hub-01.log - while :; do - echo "Starting parachain-relay (primary governance) at $(date)" - "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay-bridge-hub-01.json" \ - --ethereum.private-key $parachain_relay_primary_gov_eth_key \ - >>"$output_dir"/parachain-relay-bridge-hub-01.log 2>&1 || true - sleep 20 - done - ) & - - # Launch parachain relay for bridgehub (secondary governance) - ( - : >"$output_dir"/parachain-relay-bridge-hub-02.log - while :; do - echo "Starting parachain-relay (secondary governance) at $(date)" - "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay-bridge-hub-02.json" \ - --ethereum.private-key $parachain_relay_secondary_gov_eth_key \ - >>"$output_dir"/parachain-relay-bridge-hub-02.log 2>&1 || true - sleep 20 - done - ) & - - # Launch parachain relay 0 for assethub - ( - : >"$output_dir"/parachain-relay-asset-hub-0.log - while :; do - echo "Starting parachain relay (asset-hub) at $(date)" - "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay-asset-hub-0.json" \ - --ethereum.private-key $parachain_relay_assethub_eth_key \ - >>"$output_dir"/parachain-relay-asset-hub-0.log 2>&1 || true - sleep 20 - done - ) & - - # Launch parachain relay 1 for assethub - ( - : >"$output_dir"/parachain-relay-asset-hub-1.log - while :; do - echo "Starting parachain relay (asset-hub) at $(date)" - "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay-asset-hub-1.json" \ - --ethereum.private-key $parachain_relay_primary_gov_eth_key \ - >>"$output_dir"/parachain-relay-asset-hub-1.log 2>&1 || true - sleep 20 - done - ) & - - # Launch parachain relay 2 for assethub - ( - : >"$output_dir"/parachain-relay-asset-hub-2.log - while :; do - echo "Starting parachain relay (asset-hub) at $(date)" - "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay-asset-hub-2.json" \ - --ethereum.private-key $parachain_relay_secondary_gov_eth_key \ - >>"$output_dir"/parachain-relay-asset-hub-2.log 2>&1 || true - sleep 20 - done - ) & - - # Launch parachain relay for parachain penpal - ( - : >"$output_dir"/parachain-relay-penpal.log - while :; do - echo "Starting parachain-relay (penpal) at $(date)" - "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay-penpal.json" \ - --ethereum.private-key $parachain_relay_penpal_eth_key \ - >>"$output_dir"/parachain-relay-penpal.log 2>&1 || true - sleep 20 - done - ) & - # Launch beacon relay ( : >"$output_dir"/beacon-relay.log @@ -305,57 +47,19 @@ start_relayer() { done ) & - # Launch execution relay for assethub-0 - ( - : >$output_dir/execution-relay-asset-hub-0.log - while :; do - echo "Starting execution relay (asset-hub-0) at $(date)" - "${relay_bin}" run execution \ - --config $output_dir/execution-relay-asset-hub-0.json \ - --substrate.private-key "//ExecutionRelayAssetHub" \ - >>"$output_dir"/execution-relay-asset-hub-0.log 2>&1 || true - sleep 20 - done - ) & - - # Launch execution relay for assethub-1 + # Launch execution relay ( - : >$output_dir/execution-relay-asset-hub-1.log + : >$output_dir/execution-relay.log while :; do - echo "Starting execution relay (asset-hub-1) at $(date)" + echo "Starting execution relay at $(date)" "${relay_bin}" run execution \ - --config $output_dir/execution-relay-asset-hub-1.json \ + --config $output_dir/execution-relay.json \ --substrate.private-key "//Alice" \ - >>"$output_dir"/execution-relay-asset-hub-1.log 2>&1 || true + >>"$output_dir"/execution-relay.log 2>&1 || true sleep 20 done ) & - # Launch execution relay for assethub-2 - ( - : >$output_dir/execution-relay-asset-hub-2.log - while :; do - echo "Starting execution relay (asset-hub-2) at $(date)" - "${relay_bin}" run execution \ - --config $output_dir/execution-relay-asset-hub-2.json \ - --substrate.private-key "//Bob" \ - >>"$output_dir"/execution-relay-asset-hub-2.log 2>&1 || true - sleep 20 - done - ) & - - # Launch execution relay for penpal - ( - : >$output_dir/execution-relay-penpal.log - while :; do - echo "Starting execution relay (penpal) at $(date)" - "${relay_bin}" run execution \ - --config $output_dir/execution-relay-penpal.json \ - --substrate.private-key "//ExecutionRelayPenpal" \ - >>"$output_dir"/execution-relay-penpal.log 2>&1 || true - sleep 20 - done - ) & } build_relayer() {