diff --git a/.github/workflows/release-relayer.yml b/.github/workflows/release-relayer.yml index 0fae3ff1ea..114c99ed10 100644 --- a/.github/workflows/release-relayer.yml +++ b/.github/workflows/release-relayer.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - release-v1.0.0 workflow_dispatch: env: diff --git a/relayer/cmd/generate_beacon_data.go b/relayer/cmd/generate_beacon_data.go index dfa13e860a..06202d84e5 100644 --- a/relayer/cmd/generate_beacon_data.go +++ b/relayer/cmd/generate_beacon_data.go @@ -132,7 +132,7 @@ func generateBeaconCheckpoint(cmd *cobra.Command, _ []string) error { return err } - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) store.Connect() defer store.Close() @@ -193,7 +193,7 @@ func generateBeaconTestFixture(cmd *cobra.Command, _ []string) error { return err } - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) err = store.Connect() @@ -504,7 +504,7 @@ func generateExecutionUpdate(cmd *cobra.Command, _ []string) error { } log.WithFields(log.Fields{"endpoint": conf.Source.Beacon.Endpoint}).Info("connecting to beacon API") - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) store.Connect() @@ -695,7 +695,7 @@ func generateInboundFixture(cmd *cobra.Command, _ []string) error { return err } - p := protocol.New(beaconConf.Source.Beacon.Spec) + p := protocol.New(beaconConf.Source.Beacon.Spec, beaconConf.Sink.Parachain.HeaderRedundancy) store := store.New(beaconConf.Source.Beacon.DataStore.Location, beaconConf.Source.Beacon.DataStore.MaxEntries, *p) store.Connect() diff --git a/relayer/cmd/import_beacon_state.go b/relayer/cmd/import_beacon_state.go index f683e48db0..0dac560832 100644 --- a/relayer/cmd/import_beacon_state.go +++ b/relayer/cmd/import_beacon_state.go @@ -71,7 +71,7 @@ func importBeaconState(cmd *cobra.Command, _ []string) error { return fmt.Errorf("open finalized state file: %w", err) } - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) beaconClient := api.NewBeaconClient(conf.Source.Beacon.Endpoint, conf.Source.Beacon.StateEndpoint) syncer := syncer.New(beaconClient, &store, p) diff --git a/relayer/cmd/import_execution_header.go b/relayer/cmd/import_execution_header.go index c003131e96..45126a63f7 100644 --- a/relayer/cmd/import_execution_header.go +++ b/relayer/cmd/import_execution_header.go @@ -109,7 +109,7 @@ func importExecutionHeaderFn(cmd *cobra.Command, _ []string) error { log.WithField("hash", beaconHeader).Info("will be syncing execution header for beacon hash") - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) store.Connect() defer store.Close() diff --git a/relayer/cmd/list_beacon_states.go b/relayer/cmd/list_beacon_states.go index f3113968f1..cbc48fd4c5 100644 --- a/relayer/cmd/list_beacon_states.go +++ b/relayer/cmd/list_beacon_states.go @@ -49,7 +49,7 @@ func listBeaconState(cmd *cobra.Command, _ []string) error { return err } - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) err = store.Connect() diff --git a/relayer/cmd/store_beacon_state.go b/relayer/cmd/store_beacon_state.go index 490a39a4d5..2158bf54bd 100644 --- a/relayer/cmd/store_beacon_state.go +++ b/relayer/cmd/store_beacon_state.go @@ -49,7 +49,7 @@ func storeBeaconState(cmd *cobra.Command, _ []string) error { return err } - p := protocol.New(conf.Source.Beacon.Spec) + p := protocol.New(conf.Source.Beacon.Spec, conf.Sink.Parachain.HeaderRedundancy) store := store.New(conf.Source.Beacon.DataStore.Location, conf.Source.Beacon.DataStore.MaxEntries, *p) beaconClient := api.NewBeaconClient(conf.Source.Beacon.Endpoint, conf.Source.Beacon.StateEndpoint) syncer := syncer.New(beaconClient, &store, p) diff --git a/relayer/relays/beacon/config/config.go b/relayer/relays/beacon/config/config.go index 1e3b465597..f0c9c86374 100644 --- a/relayer/relays/beacon/config/config.go +++ b/relayer/relays/beacon/config/config.go @@ -3,7 +3,6 @@ package config import ( "errors" "fmt" - "github.com/snowfork/snowbridge/relayer/config" ) type Config struct { @@ -35,8 +34,16 @@ type BeaconConfig struct { } type SinkConfig struct { - Parachain config.ParachainConfig `mapstructure:"parachain"` - UpdateSlotInterval uint64 `mapstructure:"updateSlotInterval"` + Parachain ParachainConfig `mapstructure:"parachain"` + UpdateSlotInterval uint64 `mapstructure:"updateSlotInterval"` +} + +type ParachainConfig struct { + Endpoint string `mapstructure:"endpoint"` + MaxWatchedExtrinsics int64 `mapstructure:"maxWatchedExtrinsics"` + // The max number of header in the FinalizedBeaconStateBuffer on-chain. + // https://github.com/paritytech/polkadot-sdk/blob/master/bridges/snowbridge/pallets/ethereum-client/src/types.rs#L23 + HeaderRedundancy uint64 `mapstructure:"headerRedundancy"` } func (c Config) Validate() error { @@ -81,3 +88,16 @@ func (b BeaconConfig) Validate() error { } return nil } + +func (p ParachainConfig) Validate() error { + if p.Endpoint == "" { + return errors.New("[endpoint] is not set") + } + if p.MaxWatchedExtrinsics == 0 { + return errors.New("[maxWatchedExtrinsics] is not set") + } + if p.HeaderRedundancy == 0 { + return errors.New("[HeaderRedundancy] is not set") + } + return nil +} diff --git a/relayer/relays/beacon/header/header.go b/relayer/relays/beacon/header/header.go index d65f75ebca..e7dca2c828 100644 --- a/relayer/relays/beacon/header/header.go +++ b/relayer/relays/beacon/header/header.go @@ -523,32 +523,32 @@ func (h *Header) findLatestCheckPoint(slot uint64) (state.FinalizedHeader, error return beaconState, fmt.Errorf("GetLastFinalizedStateIndex error: %w", err) } startIndex := uint64(lastIndex) - endIndex := uint64(0) + endIndex := startIndex + 1 syncCommitteePeriod := h.protocol.Settings.SlotsInEpoch * h.protocol.Settings.EpochsPerSyncCommitteePeriod - slotPeriodIndex := slot / syncCommitteePeriod - - for index := startIndex; index >= endIndex; index-- { + totalStates := syncCommitteePeriod * h.protocol.HeaderRedundancy // Total size of the circular buffer, + // https://github.com/paritytech/polkadot-sdk/blob/master/bridges/snowbridge/pallets/ethereum-client/src/lib.rs#L75 + for index := startIndex; index != endIndex; index = (index - 1 + totalStates) % totalStates { beaconRoot, err := h.writer.GetFinalizedBeaconRootByIndex(uint32(index)) if err != nil { return beaconState, fmt.Errorf("GetFinalizedBeaconRootByIndex %d, error: %w", index, err) } beaconState, err = h.writer.GetFinalizedHeaderStateByBlockRoot(beaconRoot) if err != nil { - return beaconState, fmt.Errorf("GetFinalizedHeaderStateByBlockRoot %s, error: %w", beaconRoot.Hex(), err) + // As soon as it can't find a block root, it means the circular wrap around array is empty. + log.WithFields(log.Fields{"index": index, "blockRoot": beaconRoot.Hex()}).WithError(err).Info("searching for checkpoint on-chain failed") + break } - statePeriodIndex := beaconState.BeaconSlot / syncCommitteePeriod + if beaconState.BeaconSlot < slot { + log.WithFields(log.Fields{"index": index, "blockRoot": beaconRoot.Hex()}).WithError(err).Debug("unable to find a relevant on-chain header") break } // Found the beaconState - if beaconState.BeaconSlot > slot && beaconState.BeaconSlot < slot+syncCommitteePeriod && slotPeriodIndex == statePeriodIndex { - break + if beaconState.BeaconSlot > slot && beaconState.BeaconSlot < slot+syncCommitteePeriod { + return beaconState, nil } } - if beaconState.BeaconSlot > slot && beaconState.BeaconSlot < slot+syncCommitteePeriod { - return beaconState, nil - } return beaconState, fmt.Errorf("no checkpoint on chain for slot %d", slot) } diff --git a/relayer/relays/beacon/header/header_test.go b/relayer/relays/beacon/header/header_test.go index 5ff7cee5ef..358cbe9d09 100644 --- a/relayer/relays/beacon/header/header_test.go +++ b/relayer/relays/beacon/header/header_test.go @@ -3,6 +3,7 @@ package header import ( "context" "github.com/ethereum/go-ethereum/common" + "github.com/snowfork/go-substrate-rpc-client/v4/types" "github.com/snowfork/snowbridge/relayer/relays/beacon/config" "github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer/api" "github.com/snowfork/snowbridge/relayer/relays/beacon/mock" @@ -15,6 +16,8 @@ import ( "testing" ) +const MaxRedundancy = 20 + // Verifies that the closest checkpoint is populated successfully if it is not populated in the first place. func TestSyncInterimFinalizedUpdate_WithDataFromAPI(t *testing.T) { settings := config.SpecSettings{ @@ -22,7 +25,7 @@ func TestSyncInterimFinalizedUpdate_WithDataFromAPI(t *testing.T) { EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, } - p := protocol.New(settings) + p := protocol.New(settings, MaxRedundancy) client := mock.API{} beaconStore := mock.Store{} @@ -80,7 +83,7 @@ func TestSyncInterimFinalizedUpdate_WithDataFromStore(t *testing.T) { EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, } - p := protocol.New(settings) + p := protocol.New(settings, MaxRedundancy) client := mock.API{} beaconStore := mock.Store{} @@ -146,7 +149,7 @@ func TestSyncInterimFinalizedUpdate_WithDataFromStoreWithDifferentBlocks(t *test EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, } - p := protocol.New(settings) + p := protocol.New(settings, MaxRedundancy) client := mock.API{} beaconStore := mock.Store{} @@ -212,7 +215,7 @@ func TestSyncInterimFinalizedUpdate_BeaconStateNotAvailableInAPIAndStore(t *test EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, } - p := protocol.New(settings) + p := protocol.New(settings, MaxRedundancy) client := mock.API{} beaconStore := mock.Store{} @@ -256,7 +259,7 @@ func TestSyncInterimFinalizedUpdate_NoValidBlocksFound(t *testing.T) { EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, } - p := protocol.New(settings) + p := protocol.New(settings, MaxRedundancy) client := mock.API{} beaconStore := mock.Store{} @@ -324,3 +327,102 @@ func TestShouldUpdate(t *testing.T) { assert.Equal(t, tt.result, result, "expected %t but found %t", tt.result, result) } } + +func TestFindLatestCheckPoint(t *testing.T) { + settings := config.SpecSettings{ + SlotsInEpoch: 4, + EpochsPerSyncCommitteePeriod: 2, + DenebForkEpoch: 0, + } + maxRedundancy := uint64(2) + p := protocol.New(settings, maxRedundancy) + // Total circular array would be 4 * 2 * 2 = 16 + client := mock.API{} + beaconStore := mock.Store{} + + headerIndex5 := common.HexToHash("0xd118e1464716db841f14ac1c3245f2b7900ee6f896ac85362deae3ff90c14c78") + headerIndex4 := common.HexToHash("0xe9d993e257b0d7ac775b8a03827209db2c7314a780c24a7fad64fd9fcee529f7") + headerIndex3 := common.HexToHash("0x7f2c1240dd714f3d74050638c642f14bf49f541d42f0808b7ae0c188c7edbb08") + headerIndex2 := common.HexToHash("0x01eaa6cbb00311f19c84965f3a9e8ddf56dd5443dfa8ea35c3e6d0b6306554b3") + headerIndex1 := common.HexToHash("0xa106b85508139ad0417cc521f41943a74908bfedbc6f548b3d1acddf60548493") + headerIndex0 := common.HexToHash("0xefef79bf51c3e02c19f9cbe718c6e226ad516153622a500bf783fce2aa8ec7c6") + headerIndex15 := common.HexToHash("0x416f890494e218d3cb32ce1ef3bd08e3acccf6e112b66db544cfcc6295bbdc2a") + headerIndex14 := common.HexToHash("0x74c4e67ca468722a7c3af52c5f96f4bbdd60b4d237ae7693863dca308e3c354c") + + h := New( + &mock.Writer{ + LastFinalizedState: state.FinalizedHeader{ + BeaconBlockRoot: common.Hash{}, + BeaconSlot: 50, + InitialCheckpointRoot: common.Hash{}, + InitialCheckpointSlot: 0, + }, + LastFinalizedStateIndex: 5, + FinalizedBeaconRootByIndex: map[uint32]types.H256{ + 5: types.H256(headerIndex5), + 4: types.H256(headerIndex4), + 3: types.H256(headerIndex3), + 2: types.H256(headerIndex2), + 1: types.H256(headerIndex1), + 0: types.H256(headerIndex0), + 15: types.H256(headerIndex15), + 14: types.H256(headerIndex14), + }, + FinalizedHeaderStateByBlockRoot: map[types.H256]state.FinalizedHeader{ + types.H256(headerIndex5): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex5, + BeaconSlot: 50, + }, + types.H256(headerIndex4): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex4, + BeaconSlot: 46, + }, + types.H256(headerIndex3): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex3, + BeaconSlot: 42, + }, + types.H256(headerIndex2): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex2, + BeaconSlot: 38, + }, + types.H256(headerIndex1): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex1, + BeaconSlot: 30, + }, + types.H256(headerIndex0): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex0, + BeaconSlot: 32, + }, + types.H256(headerIndex15): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex15, + BeaconSlot: 20, + }, + types.H256(headerIndex14): state.FinalizedHeader{ + BeaconBlockRoot: headerIndex14, + BeaconSlot: 18, + }, + }, + }, + &client, + settings, + &beaconStore, + p, + 316, + ) + + // Slot 20 would be usable to prove slot 19 + header, err := h.findLatestCheckPoint(19) + assert.NoError(t, err) + assert.Equal(t, headerIndex15, header.BeaconBlockRoot) + assert.Equal(t, uint64(20), header.BeaconSlot) + + // No header would be within range to prove slot 4 + _, err = h.findLatestCheckPoint(4) + assert.Error(t, err) + + // Slot 46 would be usable to prove slot 19 + header, err = h.findLatestCheckPoint(40) + assert.NoError(t, err) + assert.Equal(t, headerIndex4, header.BeaconBlockRoot) + assert.Equal(t, uint64(46), header.BeaconSlot) +} diff --git a/relayer/relays/beacon/header/syncer/syncer_test.go b/relayer/relays/beacon/header/syncer/syncer_test.go index 3e94469058..b74dfb15c2 100644 --- a/relayer/relays/beacon/header/syncer/syncer_test.go +++ b/relayer/relays/beacon/header/syncer/syncer_test.go @@ -19,13 +19,14 @@ import ( ) const TestUrl = "https://lodestar-sepolia.chainsafe.io" +const MaxRedundancy = 20 func newTestRunner() *Syncer { return New(api.NewBeaconClient(TestUrl, TestUrl), &mock.Store{}, protocol.New(config.SpecSettings{ SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) } // Verifies that the Lodestar provided finalized endpoint matches the manually constructed finalized endpoint @@ -110,7 +111,7 @@ func TestGetFinalizedUpdateWithSyncCommitteeUpdateAtSlot(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) // Manually construct a finalized update manualUpdate, err := syncer.GetFinalizedUpdateAtAttestedSlot(129, 0, true) @@ -165,7 +166,7 @@ func TestFindAttestedAndFinalizedHeadersAtBoundary(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) attested, err := syncer.FindValidAttestedHeader(8000, 8160) assert.NoError(t, err) @@ -195,7 +196,7 @@ func TestFindAttestedAndFinalizedHeadersAtBoundary(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) attested, err = syncer.FindValidAttestedHeader(32576, 32704) assert.NoError(t, err) @@ -225,7 +226,7 @@ func TestFindAttestedAndFinalizedHeadersAtBoundary(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) attested, err = syncer.FindValidAttestedHeader(25076, 32736) assert.NoError(t, err) @@ -249,7 +250,7 @@ func TestFindAttestedAndFinalizedHeadersAtBoundary(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) attested, err = syncer.FindValidAttestedHeader(32540, 32768) assert.Error(t, err) diff --git a/relayer/relays/beacon/main.go b/relayer/relays/beacon/main.go index 58b46d8688..1e6c1937c8 100644 --- a/relayer/relays/beacon/main.go +++ b/relayer/relays/beacon/main.go @@ -37,7 +37,7 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { paraconn := parachain.NewConnection(r.config.Sink.Parachain.Endpoint, r.keypair.AsKeyringPair()) - err := paraconn.ConnectWithHeartBeat(ctx, 60 * time.Second) + err := paraconn.ConnectWithHeartBeat(ctx, 60*time.Second) if err != nil { return err } @@ -47,7 +47,7 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { r.config.Sink.Parachain.MaxWatchedExtrinsics, ) - p := protocol.New(specSettings) + p := protocol.New(specSettings, r.config.Sink.Parachain.HeaderRedundancy) err = writer.Start(ctx, eg) if err != nil { diff --git a/relayer/relays/beacon/mock/mock_writer.go b/relayer/relays/beacon/mock/mock_writer.go index b0700e7cc5..054a99c7f2 100644 --- a/relayer/relays/beacon/mock/mock_writer.go +++ b/relayer/relays/beacon/mock/mock_writer.go @@ -12,7 +12,10 @@ import ( ) type Writer struct { - LastFinalizedState state.FinalizedHeader + LastFinalizedState state.FinalizedHeader + LastFinalizedStateIndex types.U32 + FinalizedBeaconRootByIndex map[uint32]types.H256 + FinalizedHeaderStateByBlockRoot map[types.H256]state.FinalizedHeader } func (m *Writer) GetLastExecutionHeaderState() (state.ExecutionHeader, error) { @@ -20,11 +23,11 @@ func (m *Writer) GetLastExecutionHeaderState() (state.ExecutionHeader, error) { } func (m *Writer) GetLastFinalizedStateIndex() (types.U32, error) { - return 0, nil + return m.LastFinalizedStateIndex, nil } func (m *Writer) GetFinalizedBeaconRootByIndex(index uint32) (types.H256, error) { - return types.H256{}, nil + return m.FinalizedBeaconRootByIndex[index], nil } func (m *Writer) BatchCall(ctx context.Context, extrinsic []string, calls []interface{}) error { @@ -66,7 +69,7 @@ func (m *Writer) GetLastBasicChannelNonceByAddress(address common.Address) (uint } func (m *Writer) GetFinalizedHeaderStateByBlockRoot(blockRoot types.H256) (state.FinalizedHeader, error) { - return state.FinalizedHeader{}, nil + return m.FinalizedHeaderStateByBlockRoot[blockRoot], nil } func (m *Writer) FindCheckPointBackward(slot uint64) (state.FinalizedHeader, error) { diff --git a/relayer/relays/beacon/protocol/protocol.go b/relayer/relays/beacon/protocol/protocol.go index a0935730ab..a81a6b30e2 100644 --- a/relayer/relays/beacon/protocol/protocol.go +++ b/relayer/relays/beacon/protocol/protocol.go @@ -10,12 +10,14 @@ import ( type Protocol struct { Settings config.SpecSettings SlotsPerHistoricalRoot uint64 + HeaderRedundancy uint64 } -func New(setting config.SpecSettings) *Protocol { +func New(setting config.SpecSettings, headerRedundancy uint64) *Protocol { return &Protocol{ Settings: setting, SlotsPerHistoricalRoot: setting.SlotsInEpoch * setting.EpochsPerSyncCommitteePeriod, + HeaderRedundancy: headerRedundancy, } } diff --git a/relayer/relays/beacon/store/datastore_test.go b/relayer/relays/beacon/store/datastore_test.go index efb10f75d7..75069c5883 100644 --- a/relayer/relays/beacon/store/datastore_test.go +++ b/relayer/relays/beacon/store/datastore_test.go @@ -13,6 +13,7 @@ import ( ) const TestDataStoreFile = "./" +const MaxRedundancy = 20 func TestGetBeaconState(t *testing.T) { _ = os.RemoveAll(TestDataStoreFile + BeaconStateDir) @@ -22,7 +23,7 @@ func TestGetBeaconState(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) err := store.Connect() require.NoError(t, err) defer func() { @@ -66,7 +67,7 @@ func TestPruneOldStates(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - })) + }, MaxRedundancy)) err := store.Connect() require.NoError(t, err) defer func() { @@ -137,7 +138,7 @@ func TestFindBeaconStateWithinRange(t *testing.T) { SlotsInEpoch: 32, EpochsPerSyncCommitteePeriod: 256, DenebForkEpoch: 0, - }) + }, MaxRedundancy) store := New(TestDataStoreFile, 2, *p) err := store.Connect() require.NoError(t, err) diff --git a/relayer/relays/execution/config.go b/relayer/relays/execution/config.go index 1390812e11..70dd1d5cae 100644 --- a/relayer/relays/execution/config.go +++ b/relayer/relays/execution/config.go @@ -46,7 +46,7 @@ type ContractsConfig struct { } type SinkConfig struct { - Parachain config.ParachainConfig `mapstructure:"parachain"` + Parachain beaconconf.ParachainConfig `mapstructure:"parachain"` } type ChannelID [32]byte diff --git a/relayer/relays/execution/main.go b/relayer/relays/execution/main.go index 2514a4e285..39618e6410 100644 --- a/relayer/relays/execution/main.go +++ b/relayer/relays/execution/main.go @@ -87,7 +87,7 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { } r.gatewayContract = contract - p := protocol.New(r.config.Source.Beacon.Spec) + p := protocol.New(r.config.Source.Beacon.Spec, r.config.Sink.Parachain.HeaderRedundancy) store := store.New(r.config.Source.Beacon.DataStore.Location, r.config.Source.Beacon.DataStore.MaxEntries, *p) store.Connect() diff --git a/smoketest/.envrc-westend b/smoketest/.envrc-westend new file mode 100644 index 0000000000..3edf6c5ca9 --- /dev/null +++ b/smoketest/.envrc-westend @@ -0,0 +1,26 @@ +source_up_if_exists + +export ETH_NETWORK=sepolia +export POLKADOT_NETWORK=westend + +# Endpoints +export BRIDGE_HUB_WS_URL=wss://westend-bridge-hub-rpc.polkadot.io +export ASSET_HUB_WS_URL=wss://westend-asset-hub-rpc.polkadot.io +export RELAY_CHAIN_WS_URL=wss://westend-rpc.polkadot.io +export ETHEREUM_HTTP_API=https://sepolia.infura.io/v3/*** +export ETHEREUM_API=wss://sepolia.infura.io/ws/v3/*** + +# Contract address +export GATEWAY_PROXY_CONTRACT=9ed8b47bc3417e3bd0507adc06e56e2fa360a4e9 +export WETH_CONTRACT=fff9976782d46cc05630d1f6ebab18b2324d6b14 + +# Receiver Accounts +export SUBSTRATE_RECEIVER=5827013ddc4082f8252f8729bd2f06e77e7863dea9202a6f0e7a2c34e356e85a +export ETHEREUM_RECEIVER=302F0B71B8aD3CF6dD90aDb668E49b2168d652fd + +# Sender Keys +export ETHEREUM_KEY=* +export SUBSTRATE_KEY=* + +# Wait period in blocks +WAIT_PERIOD=1000 \ No newline at end of file diff --git a/smoketest/.gitignore b/smoketest/.gitignore index e40652197b..16f7a97f76 100644 --- a/smoketest/.gitignore +++ b/smoketest/.gitignore @@ -16,3 +16,4 @@ src/parachains/bridgehub.rs src/parachains/penpal.rs src/parachains/relaychain.rs src/contracts +.envrc diff --git a/smoketest/Cargo.lock b/smoketest/Cargo.lock index b205e9171c..6223b9ff8a 100644 --- a/smoketest/Cargo.lock +++ b/smoketest/Cargo.lock @@ -36,6 +36,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.5.2" @@ -282,6 +288,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + [[package]] name = "async-channel" version = "2.1.1" @@ -475,7 +490,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.2", "object 0.32.2", "rustc-demangle", ] @@ -551,6 +566,21 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitcoin-internals" version = "0.2.0" @@ -710,6 +740,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "camino" version = "1.1.6" @@ -748,6 +799,7 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -974,6 +1026,25 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -1240,6 +1311,48 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "docify" version = "0.2.8" @@ -1400,6 +1513,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.33" @@ -1527,9 +1649,11 @@ dependencies = [ "ethers-addressbook", "ethers-contract", "ethers-core", + "ethers-etherscan", "ethers-middleware", "ethers-providers", "ethers-signers", + "ethers-solc", ] [[package]] @@ -1570,11 +1694,13 @@ dependencies = [ "const-hex", "dunce", "ethers-core", + "ethers-etherscan", "eyre", "prettyplease", "proc-macro2", "quote", "regex", + "reqwest", "serde", "serde_json", "syn 2.0.58", @@ -1626,6 +1752,21 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ethers-etherscan" +version = "2.0.13" +source = "git+https://github.com/gakonst/ethers-rs#79cdcf54e46ceebbcb661fad24ee6b4d98914f5c" +dependencies = [ + "chrono", + "ethers-core", + "reqwest", + "semver", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "ethers-middleware" version = "2.0.13" @@ -1706,6 +1847,37 @@ dependencies = [ "tracing", ] +[[package]] +name = "ethers-solc" +version = "2.0.13" +source = "git+https://github.com/gakonst/ethers-rs#79cdcf54e46ceebbcb661fad24ee6b4d98914f5c" +dependencies = [ + "cfg-if", + "const-hex", + "dirs", + "dunce", + "ethers-core", + "glob", + "home", + "md-5", + "num_cpus", + "once_cell", + "path-slash", + "rayon", + "regex", + "semver", + "serde", + "serde_json", + "solang-parser", + "svm-rs", + "thiserror", + "tiny-keccak", + "tokio", + "tracing", + "walkdir", + "yansi", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1818,6 +1990,22 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1865,6 +2053,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -2052,6 +2250,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gloo-timers" version = "0.2.6" @@ -2213,6 +2417,15 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.11" @@ -2461,6 +2674,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -2496,6 +2718,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.67" @@ -2714,6 +2945,36 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools 0.11.0", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax 0.8.2", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata 0.4.5", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2732,6 +2993,16 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "libsecp256k1" version = "0.7.1" @@ -2835,6 +3106,16 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.7.1" @@ -2901,6 +3182,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.0.1" @@ -2913,6 +3203,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "no-std-net" version = "0.6.0" @@ -3095,6 +3391,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parity-bip39" version = "2.0.1" @@ -3164,6 +3466,17 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "password-hash" version = "0.5.0" @@ -3181,6 +3494,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "path-slash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -3188,6 +3507,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", + "hmac 0.12.1", + "password-hash 0.4.2", + "sha2 0.10.8", ] [[package]] @@ -3198,7 +3520,7 @@ checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", "hmac 0.12.1", - "password-hash", + "password-hash 0.5.0", ] [[package]] @@ -3216,6 +3538,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.2", +] + [[package]] name = "pharos" version = "0.5.3" @@ -3226,6 +3558,57 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher 0.3.11", +] + [[package]] name = "pin-project" version = "1.1.4" @@ -3279,6 +3662,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "platforms" version = "3.3.0" @@ -3396,6 +3785,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettier-please" version = "0.2.0" @@ -3567,6 +3962,26 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "reconnecting-jsonrpsee-ws-client" version = "0.4.3" @@ -3592,6 +4007,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "ref-cast" version = "1.0.22" @@ -3671,6 +4097,7 @@ dependencies = [ "http 0.2.11", "http-body", "hyper", + "hyper-rustls", "ipnet", "js-sys", "log", @@ -3678,17 +4105,21 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls 0.21.10", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.25.4", "winreg", ] @@ -3924,7 +4355,7 @@ dependencies = [ "rustls-webpki 0.102.2", "security-framework", "security-framework-sys", - "webpki-roots", + "webpki-roots 0.26.3", "winapi", ] @@ -4462,6 +4893,12 @@ dependencies = [ "time", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "siphasher" version = "1.0.0" @@ -4545,7 +4982,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "sha3", - "siphasher", + "siphasher 1.0.0", "slab", "smallvec", "soketto 0.7.1", @@ -4584,7 +5021,7 @@ dependencies = [ "rand_chacha", "serde", "serde_json", - "siphasher", + "siphasher 1.0.0", "slab", "smol", "smoldot", @@ -4650,6 +5087,20 @@ dependencies = [ "sha1", ] +[[package]] +name = "solang-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" +dependencies = [ + "itertools 0.11.0", + "lalrpop", + "lalrpop-util", + "phf", + "thiserror", + "unicode-xid", +] + [[package]] name = "sp-application-crypto" version = "33.0.0" @@ -5031,6 +5482,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.10.0" @@ -5231,6 +5695,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "svm-rs" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" +dependencies = [ + "dirs", + "fs2", + "hex", + "once_cell", + "reqwest", + "semver", + "serde", + "serde_json", + "sha2 0.10.8", + "thiserror", + "url", + "zip", +] + [[package]] name = "syn" version = "1.0.109" @@ -5304,6 +5788,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -5476,8 +5971,11 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", + "rustls 0.21.10", "tokio", + "tokio-rustls 0.24.1", "tungstenite", + "webpki-roots 0.25.4", ] [[package]] @@ -5704,6 +6202,7 @@ dependencies = [ "httparse", "log", "rand", + "rustls 0.21.10", "sha1", "thiserror", "url", @@ -6141,6 +6640,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "0.26.3" @@ -6447,6 +6952,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "yap" version = "0.11.0" @@ -6492,3 +7003,52 @@ dependencies = [ "quote", "syn 2.0.58", ] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq 0.1.5", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/smoketest/Cargo.toml b/smoketest/Cargo.toml index 259e8faee4..965d732068 100644 --- a/smoketest/Cargo.toml +++ b/smoketest/Cargo.toml @@ -12,11 +12,11 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = hex = "0.4.3" hex-literal = "0.4.1" serde = { version = "1.0.197", features = ["derive"] } -subxt = { version = "0.37.0", features = ["substrate-compat", "unstable-light-client"]} +subxt = { version = "0.37.0", features = ["substrate-compat", "unstable-light-client"] } subxt-macro = { version = "0.37.0" } subxt-metadata = { version = "0.37.0" } subxt-codegen = { version = "0.37.0" } subxt-signer = { version = "0.37.0" } -ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["abigen", "ws"] } +ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["abigen", "ws", "rustls"] } lazy_static = "1.4.0" diff --git a/smoketest/make-bindings.sh b/smoketest/make-bindings.sh index 02b96f77da..26157f7983 100755 --- a/smoketest/make-bindings.sh +++ b/smoketest/make-bindings.sh @@ -15,13 +15,24 @@ command -v subxt || cargo install subxt-cli \ --git https://github.com/paritytech/subxt.git \ --tag v0.37.0 -if ! lsof -Pi :11144 -sTCP:LISTEN -t >/dev/null; then - echo "substrate nodes not running, please start with the e2e setup and rerun this script" - exit 1 +eth_network="${ETH_NETWORK:-localhost}" +polkadot_network="${POLKADOT_NETWORK:-localhost}" + +if [ "$polkadot_network" == "westend" ]; then + # Todo: There is no penpal nodes on westend yet + subxt codegen --url wss://westend-bridge-hub-rpc.polkadot.io >src/parachains/bridgehub.rs + subxt codegen --url wss://westend-asset-hub-rpc.polkadot.io >src/parachains/assethub.rs + subxt codegen --url wss://westend-rpc.polkadot.io >src/parachains/relaychain.rs +else + if ! lsof -Pi :11144 -sTCP:LISTEN -t >/dev/null; then + echo "substrate nodes not running, please start with the e2e setup and rerun this script" + exit 1 + fi + # Fetch metadata from BridgeHub and generate client + subxt codegen --url ws://localhost:11144 >src/parachains/bridgehub.rs + subxt codegen --url ws://localhost:12144 >src/parachains/assethub.rs + subxt codegen --url ws://localhost:13144 >src/parachains/penpal.rs + subxt codegen --url ws://localhost:9944 >src/parachains/relaychain.rs fi -# Fetch metadata from BridgeHub and generate client -subxt codegen --url ws://localhost:11144 >src/parachains/bridgehub.rs -subxt codegen --url ws://localhost:12144 >src/parachains/assethub.rs -subxt codegen --url ws://localhost:13144 >src/parachains/penpal.rs -subxt codegen --url ws://localhost:9944 >src/parachains/relaychain.rs + diff --git a/smoketest/src/constants.rs b/smoketest/src/constants.rs index 5394d848e8..c65b7228f5 100644 --- a/smoketest/src/constants.rs +++ b/smoketest/src/constants.rs @@ -1,3 +1,4 @@ +use hex::FromHex; use hex_literal::hex; use lazy_static::lazy_static; use std::{env, string::ToString}; @@ -8,23 +9,25 @@ pub const ASSET_HUB_PARA_ID: u32 = 1000; pub const BRIDGE_HUB_PARA_ID: u32 = 1002; pub const PENPAL_PARA_ID: u32 = 2000; -pub const ETHEREUM_API: &str = "ws://localhost:8546"; -pub const ETHEREUM_HTTP_API: &str = "http://localhost:8545"; +pub const DEFAULT_ETHEREUM_API: &str = "ws://localhost:8546"; +pub const DEFAULT_ETHEREUM_HTTP_API: &str = "http://localhost:8545"; -pub const ASSET_HUB_WS_URL: &str = "ws://127.0.0.1:12144"; -pub const BRIDGE_HUB_WS_URL: &str = "ws://127.0.0.1:11144"; +pub const DEFAULT_BRIDGE_HUB_WS_URL: &str = "ws://127.0.0.1:11144"; +pub const DEFAULT_ASSET_HUB_WS_URL: &str = "ws://127.0.0.1:12144"; pub const PENPAL_WS_URL: &str = "ws://127.0.0.1:13144"; -pub const RELAY_CHAIN_WS_URL: &str = "ws://127.0.0.1:9944"; +pub const DEFAULT_RELAY_CHAIN_WS_URL: &str = "ws://127.0.0.1:9944"; pub const TEMPLATE_NODE_WS_URL: &str = "ws://127.0.0.1:13144"; pub const ETHEREUM_CHAIN_ID: u64 = 11155111; -pub const ETHEREUM_KEY: &str = "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342"; +pub const DEFAULT_ETHEREUM_KEY: &str = + "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342"; pub const ETHEREUM_ADDRESS: [u8; 20] = hex!("90A987B944Cb1dCcE5564e5FDeCD7a54D3de27Fe"); // 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 GATEWAY_PROXY_CONTRACT: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); -pub const WETH_CONTRACT: [u8; 20] = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23"); +pub const DEFAULT_GATEWAY_PROXY_CONTRACT: [u8; 20] = + hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +pub const DEFAULT_WETH_CONTRACT: [u8; 20] = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23"); pub const AGENT_EXECUTOR_CONTRACT: [u8; 20] = hex!("Fc97A6197dc90bef6bbEFD672742Ed75E9768553"); pub const ERC20_DOT_CONTRACT: [u8; 20] = hex!("B8C39CbCe8106c8415472e3AAe88Eb694Cc70B57"); @@ -86,4 +89,96 @@ lazy_static! { .unwrap_or("1000000000000000".to_string()) .parse() .unwrap(); + pub static ref BRIDGE_HUB_WS_URL: String = { + if let Ok(val) = env::var("BRIDGE_HUB_WS_URL") { + val + } + else { + DEFAULT_BRIDGE_HUB_WS_URL.to_string() + } + }; + pub static ref ASSET_HUB_WS_URL: String = { + if let Ok(val) = env::var("ASSET_HUB_WS_URL") { + val + } + else { + DEFAULT_ASSET_HUB_WS_URL.to_string() + } + }; + pub static ref RELAY_CHAIN_WS_URL: String = { + if let Ok(val) = env::var("RELAY_CHAIN_WS_URL") { + val + } + else { + DEFAULT_RELAY_CHAIN_WS_URL.to_string() + } + }; + pub static ref ETHEREUM_API: String = { + if let Ok(val) = env::var("ETHEREUM_API") { + val + } + else { + DEFAULT_ETHEREUM_API.to_string() + } + }; + pub static ref ETHEREUM_HTTP_API: String = { + if let Ok(val) = env::var("ETHEREUM_HTTP_API") { + val + } + else { + DEFAULT_ETHEREUM_HTTP_API.to_string() + } + }; + pub static ref ETHEREUM_KEY: String = { + if let Ok(val) = env::var("ETHEREUM_KEY") { + val + } + else { + DEFAULT_ETHEREUM_KEY.to_string() + } + }; + pub static ref GATEWAY_PROXY_CONTRACT: [u8; 20] = { + if let Ok(val) = env::var("GATEWAY_PROXY_CONTRACT") { + <[u8; 20]>::from_hex(val).unwrap() + } + else { + DEFAULT_GATEWAY_PROXY_CONTRACT + } + }; + pub static ref WETH_CONTRACT: [u8; 20] = { + if let Ok(val) = env::var("WETH_CONTRACT") { + <[u8; 20]>::from_hex(val).unwrap() + } + else { + DEFAULT_WETH_CONTRACT + } + }; + + pub static ref SUBSTRATE_RECEIVER: [u8; 32] = { + if let Ok(val) = env::var("SUBSTRATE_RECEIVER") { + <[u8; 32]>::from_hex(val).unwrap() + } + else { + BOB_PUBLIC.clone() + } + }; + + pub static ref ETHEREUM_RECEIVER: [u8; 20] = { + if let Ok(val) = env::var("ETHEREUM_RECEIVER") { + <[u8; 20]>::from_hex(val).unwrap() + } + else { + <[u8; 20]>::from_hex("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e").unwrap() + } + }; + pub static ref SUBSTRATE_KEY: String = { + if let Ok(val) = env::var("SUBSTRATE_KEY") { + "0x".to_owned() + &val + } + else { + "//Bob".to_string() + } + }; + pub static ref WAIT_PERIOD: u64 = + env::var("WAIT_PERIOD").unwrap_or("100".to_string()).parse().unwrap(); } diff --git a/smoketest/src/helper.rs b/smoketest/src/helper.rs index e0b668e11d..136067d959 100644 --- a/smoketest/src/helper.rs +++ b/smoketest/src/helper.rs @@ -3,7 +3,6 @@ use crate::{ contracts::i_gateway, parachains::{ bridgehub::{self, api::runtime_types::snowbridge_core::outbound::v1::OperatingMode}, - penpal::{self, api::runtime_types as penpalTypes}, relaychain, relaychain::api::runtime_types::{ pallet_xcm::pallet::Call as RelaychainPalletXcmCall, @@ -33,14 +32,6 @@ use ethers::{ types::Log, }; use futures::StreamExt; -use penpalTypes::{ - penpal_runtime::RuntimeCall as PenpalRuntimeCall, - staging_xcm::v4::{ - junction::Junction as PenpalJunction, junctions::Junctions as PenpalJunctions, - location::Location as PenpalLocation, - }, - xcm::{VersionedLocation as PenpalVersionedLocation, VersionedXcm as PenpalVersionedXcm}, -}; use std::{ops::Deref, sync::Arc, time::Duration}; use subxt::{ config::DefaultExtrinsicParams, @@ -51,20 +42,6 @@ use subxt::{ Config, OnlineClient, PolkadotConfig, }; -/// Custom config that works with Penpal -pub enum PenpalConfig {} - -impl Config for PenpalConfig { - type Hash = ::Hash; - type AccountId = ::AccountId; - type Address = ::Address; - type AssetId = ::AssetId; - type Signature = ::Signature; - type Hasher = ::Hasher; - type Header = ::Header; - type ExtrinsicParams = DefaultExtrinsicParams; -} - /// Custom config that works with Statemint pub enum AssetHubConfig {} @@ -82,31 +59,28 @@ impl Config for AssetHubConfig { pub struct TestClients { pub asset_hub_client: Box>, pub bridge_hub_client: Box>, - pub penpal_client: Box>, pub relaychain_client: Box>, pub ethereum_client: Box>>, pub ethereum_signed_client: Box, LocalWallet>>>, } pub async fn initial_clients() -> Result> { - let bridge_hub_client: OnlineClient = OnlineClient::from_url(BRIDGE_HUB_WS_URL) - .await - .expect("can not connect to bridgehub"); - - let asset_hub_client: OnlineClient = OnlineClient::from_url(ASSET_HUB_WS_URL) - .await - .expect("can not connect to bridgehub"); + let bridge_hub_client: OnlineClient = + OnlineClient::from_url((*BRIDGE_HUB_WS_URL).to_string()) + .await + .expect("can not connect to bridgehub"); - let penpal_client: OnlineClient = OnlineClient::from_url(PENPAL_WS_URL) - .await - .expect("can not connect to penpal parachain"); + let asset_hub_client: OnlineClient = + OnlineClient::from_url((*ASSET_HUB_WS_URL).to_string()) + .await + .expect("can not connect to assethub"); let relaychain_client: OnlineClient = - OnlineClient::from_url(RELAY_CHAIN_WS_URL) + OnlineClient::from_url((*RELAY_CHAIN_WS_URL).to_string()) .await .expect("can not connect to relaychain"); - let ethereum_provider = Provider::::connect(ETHEREUM_API) + let ethereum_provider = Provider::::connect((*ETHEREUM_API).to_string()) .await .unwrap() .interval(Duration::from_millis(10u64)); @@ -118,7 +92,6 @@ pub async fn initial_clients() -> Result Ok(TestClients { asset_hub_client: Box::new(asset_hub_client), bridge_hub_client: Box::new(bridge_hub_client), - penpal_client: Box::new(penpal_client), relaychain_client: Box::new(relaychain_client), ethereum_client: Box::new(ethereum_client), ethereum_signed_client: Box::new(Arc::new(ethereum_signed_client)), @@ -153,7 +126,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_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let gateway = i_gateway::IGateway::new(gateway_addr, (*ethereum_client).deref().clone()); let wait_for_blocks = 500; @@ -181,56 +154,17 @@ pub struct SudoResult { pub extrinsic_hash: H256, } -pub async fn send_sudo_xcm_transact( - penpal_client: &Box>, - message: Box, -) -> Result> { - let dest = Box::new(PenpalVersionedLocation::V4(PenpalLocation { - parents: 1, - interior: PenpalJunctions::X1([PenpalJunction::Parachain(BRIDGE_HUB_PARA_ID)]), - })); - - let sudo_call = penpal::api::sudo::calls::TransactionApi::sudo( - &penpal::api::sudo::calls::TransactionApi, - PenpalRuntimeCall::PolkadotXcm(penpalTypes::pallet_xcm::pallet::Call::send { - dest, - message, - }), - ); - - let owner = Pair::from_string("//Alice", None).expect("cannot create keypair"); - - let signer: PairSigner = PairSigner::new(owner); - - let result = penpal_client - .tx() - .sign_and_submit_then_watch_default(&sudo_call, &signer) - .await - .expect("send through xcm call.") - .wait_for_finalized() - .await - .expect("xcm call failed"); - - let block_hash = result.block_hash(); - let extrinsic_hash = result.extrinsic_hash(); - - let sudo_result = SudoResult { block_hash, extrinsic_hash }; - - if let Err(err) = result.wait_for_success().await { - Err(Box::new(err)) - } else { - Ok(sudo_result) - } -} - pub async fn initialize_wallet( ) -> Result, LocalWallet>, Box> { - let provider = Provider::::try_from(ETHEREUM_HTTP_API) + let provider = Provider::::try_from((*ETHEREUM_HTTP_API).to_string()) .unwrap() .interval(Duration::from_millis(10u64)); - let wallet: LocalWallet = - ETHEREUM_KEY.parse::().unwrap().with_chain_id(ETHEREUM_CHAIN_ID); + let wallet: LocalWallet = (*ETHEREUM_KEY) + .to_string() + .parse::() + .unwrap() + .with_chain_id(ETHEREUM_CHAIN_ID); Ok(SignerMiddleware::new(provider.clone(), wallet.clone())) } @@ -342,7 +276,7 @@ pub async fn governance_bridgehub_call_from_relay_chain( pub async fn fund_agent(agent_id: [u8; 32]) -> Result<(), Box> { let test_clients = initial_clients().await.expect("initialize clients"); - let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into(); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let ethereum_client = *(test_clients.ethereum_client.clone()); let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone()); let agent_address = gateway.agent_of(agent_id).await.expect("find agent"); diff --git a/smoketest/src/lib.rs b/smoketest/src/lib.rs index 68fd128d80..5ce34eed3f 100644 --- a/smoketest/src/lib.rs +++ b/smoketest/src/lib.rs @@ -3,3 +3,5 @@ pub mod contracts; pub mod helper; pub mod parachains; pub mod xcm; + +pub mod penpal_helper; diff --git a/smoketest/src/penpal_helper.rs b/smoketest/src/penpal_helper.rs new file mode 100644 index 0000000000..46503d97a5 --- /dev/null +++ b/smoketest/src/penpal_helper.rs @@ -0,0 +1,83 @@ +use crate::{ + constants::*, + parachains::penpal::{self, api::runtime_types as penpalTypes}, +}; +use penpalTypes::{ + penpal_runtime::RuntimeCall as PenpalRuntimeCall, + staging_xcm::v4::{ + junction::Junction as PenpalJunction, junctions::Junctions as PenpalJunctions, + location::Location as PenpalLocation, + }, + xcm::{VersionedLocation as PenpalVersionedLocation, VersionedXcm as PenpalVersionedXcm}, +}; +use subxt::{ + config::DefaultExtrinsicParams, + ext::sp_core::{sr25519::Pair, Pair as PairT}, + tx::PairSigner, + utils::H256, + Config, OnlineClient, PolkadotConfig, +}; + +/// Custom config that works with Penpal +pub enum PenpalConfig {} + +impl Config for PenpalConfig { + type Hash = ::Hash; + type AccountId = ::AccountId; + type Address = ::Address; + type AssetId = ::AssetId; + type Signature = ::Signature; + type Hasher = ::Hasher; + type Header = ::Header; + type ExtrinsicParams = DefaultExtrinsicParams; +} + +pub struct SudoResult { + pub block_hash: H256, + pub extrinsic_hash: H256, +} + +pub async fn send_sudo_xcm_transact( + message: Box, +) -> Result> { + let penpal_client: OnlineClient = OnlineClient::from_url(PENPAL_WS_URL) + .await + .expect("can not connect to penpal parachain"); + + let dest = Box::new(PenpalVersionedLocation::V4(PenpalLocation { + parents: 1, + interior: PenpalJunctions::X1([PenpalJunction::Parachain(BRIDGE_HUB_PARA_ID)]), + })); + + let sudo_call = penpal::api::sudo::calls::TransactionApi::sudo( + &penpal::api::sudo::calls::TransactionApi, + PenpalRuntimeCall::PolkadotXcm(penpalTypes::pallet_xcm::pallet::Call::send { + dest, + message, + }), + ); + + let owner = Pair::from_string("//Alice", None).expect("cannot create keypair"); + + let signer: PairSigner = PairSigner::new(owner); + + let result = penpal_client + .tx() + .sign_and_submit_then_watch_default(&sudo_call, &signer) + .await + .expect("send through xcm call.") + .wait_for_finalized() + .await + .expect("xcm call failed"); + + let block_hash = result.block_hash(); + let extrinsic_hash = result.extrinsic_hash(); + + let sudo_result = SudoResult { block_hash, extrinsic_hash }; + + if let Err(err) = result.wait_for_success().await { + Err(Box::new(err)) + } else { + Ok(sudo_result) + } +} diff --git a/smoketest/tests/create_agent.rs b/smoketest/tests/create_agent.rs index 19159e2cc1..3074af3aa3 100644 --- a/smoketest/tests/create_agent.rs +++ b/smoketest/tests/create_agent.rs @@ -1,7 +1,7 @@ use snowbridge_smoketest::{ contracts::i_gateway::AgentCreatedFilter, helper::*, parachains::bridgehub::api::ethereum_system::events::CreateAgent, - xcm::construct_xcm_message_with_fee, + penpal_helper::send_sudo_xcm_transact, xcm::construct_xcm_message_with_fee, }; #[tokio::test] @@ -14,9 +14,7 @@ async fn create_agent() { let message = construct_xcm_message_with_fee(encoded_call); - let result = send_sudo_xcm_transact(&test_clients.penpal_client, message) - .await - .expect("failed to send xcm transact."); + let result = send_sudo_xcm_transact(message).await.expect("failed to send xcm transact."); println!( "xcm call issued at block hash {:?}, transaction hash {:?}", diff --git a/smoketest/tests/create_channel.rs b/smoketest/tests/create_channel.rs index 795847be43..43640ee181 100644 --- a/smoketest/tests/create_channel.rs +++ b/smoketest/tests/create_channel.rs @@ -1,7 +1,7 @@ use snowbridge_smoketest::{ contracts::i_gateway::ChannelCreatedFilter, helper::*, parachains::bridgehub::api::ethereum_system::events::CreateChannel, - xcm::construct_xcm_message_with_fee, + penpal_helper::send_sudo_xcm_transact, xcm::construct_xcm_message_with_fee, }; #[tokio::test] @@ -14,9 +14,7 @@ async fn create_channel() { let message = construct_xcm_message_with_fee(encoded_call); - let result = send_sudo_xcm_transact(&test_clients.penpal_client, message) - .await - .expect("failed to send xcm transact."); + let result = send_sudo_xcm_transact(message).await.expect("failed to send xcm transact."); println!( "xcm call issued at block hash {:?}, transaction hash {:?}", diff --git a/smoketest/tests/register_token.rs b/smoketest/tests/register_token.rs index a804452f9b..ede7f6991e 100644 --- a/smoketest/tests/register_token.rs +++ b/smoketest/tests/register_token.rs @@ -27,10 +27,10 @@ async fn register_token() { let ethereum_client = *(test_clients.ethereum_signed_client.clone()); let assethub = *(test_clients.asset_hub_client.clone()); - let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into(); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone()); - let weth_addr: Address = WETH_CONTRACT.into(); + let weth_addr: Address = (*WETH_CONTRACT).into(); let weth = weth9::WETH9::new(weth_addr, ethereum_client.clone()); let fee = gateway.quote_register_token_fee().call().await.unwrap(); @@ -59,7 +59,7 @@ async fn register_token() { assert_eq!(receipt.status.unwrap().as_u64(), 1u64); - let wait_for_blocks = 100; + let wait_for_blocks = (*WAIT_PERIOD) as usize; let mut blocks = assethub .blocks() .subscribe_finalized() @@ -71,7 +71,7 @@ async fn register_token() { parents: 2, interior: X2( GlobalConsensus(NetworkId::Ethereum { chain_id: ETHEREUM_CHAIN_ID }), - AccountKey20 { network: None, key: WETH_CONTRACT.into() }, + AccountKey20 { network: None, key: (*WETH_CONTRACT).into() }, ), }; let expected_creator: AccountId32 = SNOWBRIDGE_SOVEREIGN.into(); diff --git a/smoketest/tests/send_polkadot_token.rs b/smoketest/tests/send_polkadot_token.rs index a81e662e05..ccb60853a6 100644 --- a/smoketest/tests/send_polkadot_token.rs +++ b/smoketest/tests/send_polkadot_token.rs @@ -14,7 +14,7 @@ async fn send_polkadot_token() { let ethereum_client = *(test_clients.ethereum_signed_client.clone()); let assethub = *(test_clients.asset_hub_client.clone()); - let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into(); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone()); let token: Address = ERC20_DOT_CONTRACT.into(); diff --git a/smoketest/tests/send_token.rs b/smoketest/tests/send_token.rs index e0063d9391..0acf38e207 100644 --- a/smoketest/tests/send_token.rs +++ b/smoketest/tests/send_token.rs @@ -29,14 +29,14 @@ async fn send_token() { let ethereum_client = *(test_clients.ethereum_signed_client.clone()); let assethub = *(test_clients.asset_hub_client.clone()); - let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into(); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone()); - let weth_addr: Address = WETH_CONTRACT.into(); + let weth_addr: Address = (*WETH_CONTRACT).into(); let weth = weth9::WETH9::new(weth_addr, ethereum_client.clone()); // Mint WETH tokens - let value = parse_units("1", "ether").unwrap(); + let value = parse_units("0.01", "ether").unwrap(); let receipt = weth.deposit().value(value).send().await.unwrap().await.unwrap().unwrap(); assert_eq!(receipt.status.unwrap().as_u64(), 1u64); @@ -63,7 +63,7 @@ async fn send_token() { .send_token( weth.address(), ASSET_HUB_PARA_ID, - i_gateway::MultiAddress { kind: 1, data: (*BOB_PUBLIC).into() }, + i_gateway::MultiAddress { kind: 1, data: (*SUBSTRATE_RECEIVER).into() }, destination_fee, amount, ) @@ -89,7 +89,7 @@ async fn send_token() { assert_eq!(receipt.status.unwrap().as_u64(), 1u64); - let wait_for_blocks = 100; + let wait_for_blocks = (*WAIT_PERIOD) as usize; let mut blocks = assethub .blocks() .subscribe_finalized() @@ -101,10 +101,10 @@ async fn send_token() { parents: 2, interior: X2( GlobalConsensus(NetworkId::Ethereum { chain_id: ETHEREUM_CHAIN_ID }), - AccountKey20 { network: None, key: WETH_CONTRACT.into() }, + AccountKey20 { network: None, key: (*WETH_CONTRACT).into() }, ), }; - let expected_owner: AccountId32 = (*BOB_PUBLIC).into(); + let expected_owner: AccountId32 = (*SUBSTRATE_RECEIVER).into(); let mut issued_event_found = false; while let Some(Ok(block)) = blocks.next().await { diff --git a/smoketest/tests/send_token_to_penpal.rs b/smoketest/tests/send_token_to_penpal.rs index 3a24d5c9fc..d278cb56df 100644 --- a/smoketest/tests/send_token_to_penpal.rs +++ b/smoketest/tests/send_token_to_penpal.rs @@ -6,7 +6,7 @@ use futures::StreamExt; use snowbridge_smoketest::{ constants::*, contracts::{i_gateway, weth9}, - helper::{initial_clients, PenpalConfig}, + helper::initial_clients, parachains::{ assethub::api::{ foreign_assets::events::Issued as AssetHubIssued, @@ -21,6 +21,7 @@ use snowbridge_smoketest::{ }, penpal::{self, api::foreign_assets::events::Issued as PenpalIssued}, }, + penpal_helper::PenpalConfig, }; use subxt::{ ext::codec::Encode, @@ -34,12 +35,14 @@ async fn send_token_to_penpal() { let test_clients = initial_clients().await.expect("initialize clients"); let ethereum_client = *(test_clients.ethereum_signed_client.clone()); let assethub_client = *(test_clients.asset_hub_client.clone()); - let penpal_client = *(test_clients.penpal_client.clone()); + let penpal_client: OnlineClient = OnlineClient::from_url(PENPAL_WS_URL) + .await + .expect("can not connect to penpal parachain"); - let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into(); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone()); - let weth_addr: Address = WETH_CONTRACT.into(); + let weth_addr: Address = (*WETH_CONTRACT).into(); let weth = weth9::WETH9::new(weth_addr, ethereum_client.clone()); // Mint WETH tokens @@ -47,7 +50,7 @@ async fn send_token_to_penpal() { let receipt = weth.deposit().value(value).send().await.unwrap().await.unwrap().unwrap(); assert_eq!(receipt.status.unwrap().as_u64(), 1u64); - ensure_penpal_asset_exists(&mut test_clients.penpal_client.clone()).await; + ensure_penpal_asset_exists(&mut penpal_client.clone()).await; // Approve token spend weth.approve(gateway_addr, value.into()) @@ -100,7 +103,7 @@ async fn send_token_to_penpal() { parents: 2, interior: X2([ GlobalConsensus(NetworkId::Ethereum { chain_id: ETHEREUM_CHAIN_ID }), - AccountKey20 { network: None, key: WETH_CONTRACT.into() }, + AccountKey20 { network: None, key: (*WETH_CONTRACT).into() }, ]), }; let assethub_expected_owner: AccountId32 = PENPAL_SOVEREIGN.into(); @@ -166,7 +169,7 @@ async fn ensure_penpal_asset_exists(penpal_client: &mut OnlineClient::connect(ETHEREUM_API) + let ethereum_provider = Provider::::connect((*ETHEREUM_API).to_string()) .await .unwrap() .interval(Duration::from_millis(10u64)); @@ -38,7 +38,7 @@ async fn transfer_polkadot_token() { let ethereum_client = Arc::new(ethereum_provider); let assethub: OnlineClient = - OnlineClient::from_url(ASSET_HUB_WS_URL).await.unwrap(); + OnlineClient::from_url((*ASSET_HUB_WS_URL).to_string()).await.unwrap(); let amount: u128 = 1_000_000_000; let assets = VersionedAssets::V3(MultiAssets(vec![MultiAsset { diff --git a/smoketest/tests/transfer_token.rs b/smoketest/tests/transfer_token.rs index a356d2855e..7953011e53 100644 --- a/smoketest/tests/transfer_token.rs +++ b/smoketest/tests/transfer_token.rs @@ -5,7 +5,6 @@ use ethers::{ types::Address, }; use futures::StreamExt; -use hex_literal::hex; use snowbridge_smoketest::{ constants::*, contracts::{ @@ -28,30 +27,30 @@ use snowbridge_smoketest::{ {self}, }, }; -use std::{sync::Arc, time::Duration}; +use std::{str::FromStr, sync::Arc, time::Duration}; use subxt::OnlineClient; -use subxt_signer::sr25519::dev; - -const DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); +use subxt_signer::{sr25519, SecretUri}; #[tokio::test] async fn transfer_token() { - let ethereum_provider = Provider::::connect(ETHEREUM_API) + let ethereum_provider = Provider::::connect((*ETHEREUM_API).to_string()) .await .unwrap() .interval(Duration::from_millis(10u64)); let ethereum_client = Arc::new(ethereum_provider); - let weth_addr: Address = WETH_CONTRACT.into(); + let weth_addr: Address = (*WETH_CONTRACT).into(); let weth = WETH9::new(weth_addr, ethereum_client.clone()); - let gateway = IGateway::new(GATEWAY_PROXY_CONTRACT, ethereum_client.clone()); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); + let gateway = IGateway::new(gateway_addr, ethereum_client.clone()); + let agent_src = gateway.agent_of(ASSET_HUB_AGENT_ID).await.expect("could not get agent address"); let assethub: OnlineClient = - OnlineClient::from_url(ASSET_HUB_WS_URL).await.unwrap(); + OnlineClient::from_url((*ASSET_HUB_WS_URL).to_string()).await.unwrap(); let amount: u128 = 1_000_000_000; let assets = VersionedAssets::V3(MultiAssets(vec![MultiAsset { @@ -59,7 +58,7 @@ async fn transfer_token() { parents: 2, interior: Junctions::X2( Junction::GlobalConsensus(NetworkId::Ethereum { chain_id: ETHEREUM_CHAIN_ID }), - Junction::AccountKey20 { network: None, key: WETH_CONTRACT.into() }, + Junction::AccountKey20 { network: None, key: (*WETH_CONTRACT).into() }, ), }), fun: Fungibility::Fungible(amount), @@ -76,11 +75,13 @@ async fn transfer_token() { parents: 0, interior: Junctions::X1(Junction::AccountKey20 { network: None, - key: DESTINATION_ADDRESS.into(), + key: (*ETHEREUM_RECEIVER).into(), }), }); - let signer = dev::bob(); + let suri = SecretUri::from_str(&SUBSTRATE_KEY).expect("Parse SURI"); + + let signer = sr25519::Keypair::from_uri(&suri).expect("valid keypair"); let token_transfer_call = TransactionApi.reserve_transfer_assets(destination, beneficiary, assets, 0); @@ -101,11 +102,13 @@ async fn transfer_token() { weth.event::().at_block_hash(block.hash.unwrap()).query().await { for transfer in transfers { - println!("Transfer event found at ethereum block {:?}", block.number.unwrap()); - assert_eq!(transfer.src, agent_src.into()); - assert_eq!(transfer.dst, DESTINATION_ADDRESS.into()); - assert_eq!(transfer.wad, amount.into()); - transfer_event_found = true; + if transfer.src.eq(&agent_src) { + println!("Transfer event found at ethereum block {:?}", block.number.unwrap()); + assert_eq!(transfer.src, agent_src.into()); + assert_eq!(transfer.dst, (*ETHEREUM_RECEIVER).into()); + assert_eq!(transfer.wad, amount.into()); + transfer_event_found = true; + } } } if transfer_event_found { diff --git a/smoketest/tests/upgrade_gateway.rs b/smoketest/tests/upgrade_gateway.rs index 2ddd5ff91d..e896b2c29c 100644 --- a/smoketest/tests/upgrade_gateway.rs +++ b/smoketest/tests/upgrade_gateway.rs @@ -46,13 +46,13 @@ const GATEWAY_V2_ADDRESS: [u8; 20] = hex!("f8f7758fbcefd546eaeff7de24aff666b6228 #[tokio::test] async fn upgrade_gateway() { - let ethereum_provider = Provider::::connect(ETHEREUM_API) + let ethereum_provider = Provider::::connect((*ETHEREUM_API).to_string()) .await .unwrap() .interval(Duration::from_millis(10u64)); let ethereum_client = Arc::new(ethereum_provider); - let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into(); + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); let gateway = i_upgradable::IUpgradable::new(gateway_addr, ethereum_client.clone()); let new_impl = mock_gateway_v2::MockGatewayV2::new( @@ -64,9 +64,9 @@ async fn upgrade_gateway() { let new_impl_initializer_params = ethers::abi::encode(&[Token::Uint(42.into())]); let relaychain: OnlineClient = - OnlineClient::from_url(RELAY_CHAIN_WS_URL).await.unwrap(); + OnlineClient::from_url((*RELAY_CHAIN_WS_URL).to_string()).await.unwrap(); let bridgehub: OnlineClient = - OnlineClient::from_url(BRIDGE_HUB_WS_URL).await.unwrap(); + OnlineClient::from_url((*BRIDGE_HUB_WS_URL).to_string()).await.unwrap(); let sudo: Pair = Pair::from_string("//Alice", None).expect("cannot create sudo keypair"); diff --git a/web/packages/api/package.json b/web/packages/api/package.json index 9b772d4884..84f83107cd 100644 --- a/web/packages/api/package.json +++ b/web/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@snowbridge/api", - "version": "0.1.19", + "version": "0.1.21", "description": "Snowbridge API client", "license": "Apache-2.0", "repository": { diff --git a/web/packages/api/src/environment.ts b/web/packages/api/src/environment.ts index 3ccf1ef787..bbfb610607 100644 --- a/web/packages/api/src/environment.ts +++ b/web/packages/api/src/environment.ts @@ -306,6 +306,99 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = { }, }, }, + paseo_sepolia: { + name: "paseo_sepolia", + ethChainId: 11155111, + locations: [ + { + id: "ethereum", + name: "Ethereum", + type: "ethereum", + destinationIds: ["assethub"], + erc20tokensReceivable: [ + { + id: "WETH", + address: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14", + minimumTransferAmount: 15_000_000_000_000n, + } + ], + }, + { + id: "assethub", + name: "Asset Hub", + type: "substrate", + destinationIds: ["ethereum"], + paraInfo: { + paraId: 1000, + destinationFeeDOT: 0n, + skipExistentialDepositCheck: false, + addressType: "32byte", + decimals: 10, + maxConsumers: 16, + }, + erc20tokensReceivable: [ + { + id: "WETH", + address: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14", + minimumTransferAmount: 15_000_000_000_000n, + } + ], + } + ], + config: { + BEACON_HTTP_API: "https://lodestar-sepolia.chainsafe.io", + ETHEREUM_API: (key) => `https://eth-sepolia.g.alchemy.com/v2/${key}`, + RELAY_CHAIN_URL: "wss://paseo-rpc.dwellir.com", + ASSET_HUB_URL: "wss://asset-hub-paseo-rpc.dwellir.com", + BRIDGE_HUB_URL: "wss://bridge-hub-paseo.dotters.network", + PARACHAINS: [], + GATEWAY_CONTRACT: "0x5a84b15B618beEE6F6285F6bd2bA20a08673e473", + BEEFY_CONTRACT: "0xE7388f953f50d377D131350490156dB649E5DC10", + ASSET_HUB_PARAID: 1000, + BRIDGE_HUB_PARAID: 1002, + PRIMARY_GOVERNANCE_CHANNEL_ID: + "0x0000000000000000000000000000000000000000000000000000000000000001", + SECONDARY_GOVERNANCE_CHANNEL_ID: + "0x0000000000000000000000000000000000000000000000000000000000000002", + RELAYERS: [ + { + name: "beacon", + account: "5E4Hf7LzHE4W3jabjLWSP8p8RzEa9ednwRivFEwYAprzpgwc", + type: "substrate", + }, + { + name: "beefy", + account: "0xc189De708158e75E5C88C0ABfA5F9a26C71F54D1", + type: "ethereum", + }, + { + name: "parachain-primary-gov", + account: "0x4BBa8c0e87242897521Ba598d327bE8280032609", + type: "ethereum", + }, + { + name: "parachain-secondary-gov", + account: "0x4BBa8c0e87242897521Ba598d327bE8280032609", + type: "ethereum", + }, + { + name: "execution-assethub", + account: "5HT2ysqEg6SXghQ3NGXp1VWT22hhj48Um8UAwk6Udg8ZCEv8", + type: "substrate", + }, + { + name: "parachain-assethub", + account: "0x4BBa8c0e87242897521Ba598d327bE8280032609", + type: "ethereum", + }, + ], + SUBSCAN_API: { + RELAY_CHAIN_URL: "https://paseo.api.subscan.io/", + ASSET_HUB_URL: "https://assethub-paseo.api.subscan.io", + BRIDGE_HUB_URL: "https://bridgehub-paseo.api.subscan.io", + }, + }, + }, polkadot_mainnet: { name: "polkadot_mainnet", ethChainId: 1, @@ -532,4 +625,97 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = { GRAPHQL_API_URL: "https://data.snowbridge.network/graphql", }, }, + westend_sepolia: { + name: "westend_sepolia", + ethChainId: 11155111, + locations: [ + { + id: "ethereum", + name: "Ethereum", + type: "ethereum", + destinationIds: ["assethub", "muse"], + erc20tokensReceivable: [ + { + id: "WETH", + address: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14", + minimumTransferAmount: 1_000_000_000_000n, + } + ], + }, + { + id: "assethub", + name: "Asset Hub", + type: "substrate", + destinationIds: ["ethereum"], + paraInfo: { + paraId: 1000, + destinationFeeDOT: 0n, + skipExistentialDepositCheck: false, + addressType: "32byte", + decimals: 12, + maxConsumers: 16, + }, + erc20tokensReceivable: [ + { + id: "WETH", + address: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14", + minimumTransferAmount: 15_000_000_000_000n, + } + ], + } + ], + config: { + BEACON_HTTP_API: "https://lodestar-sepolia.chainsafe.io", + ETHEREUM_API: (key) => `https://eth-sepolia.g.alchemy.com/v2/${key}`, + RELAY_CHAIN_URL: "https://westend-rpc.polkadot.io", + ASSET_HUB_URL: "wss://westend-asset-hub-rpc.polkadot.io", + BRIDGE_HUB_URL: "https://westend-bridge-hub-rpc.polkadot.io", + PARACHAINS: [], + GATEWAY_CONTRACT: "0x9ed8b47bc3417e3bd0507adc06e56e2fa360a4e9", + BEEFY_CONTRACT: "0x6DFaD3D73A28c48E4F4c616ECda80885b415283a", + ASSET_HUB_PARAID: 1000, + BRIDGE_HUB_PARAID: 1002, + PRIMARY_GOVERNANCE_CHANNEL_ID: + "0x0000000000000000000000000000000000000000000000000000000000000001", + SECONDARY_GOVERNANCE_CHANNEL_ID: + "0x0000000000000000000000000000000000000000000000000000000000000002", + RELAYERS: [ + { + name: "beacon", + account: "5E4Hf7LzHE4W3jabjLWSP8p8RzEa9ednwRivFEwYAprzpgwc", + type: "substrate", + }, + { + name: "beefy", + account: "0x302f0b71b8ad3cf6dd90adb668e49b2168d652fd", + type: "ethereum", + }, + { + name: "parachain-primary-gov", + account: "0x302f0b71b8ad3cf6dd90adb668e49b2168d652fd", + type: "ethereum", + }, + { + name: "parachain-secondary-gov", + account: "0x302f0b71b8ad3cf6dd90adb668e49b2168d652fd", + type: "ethereum", + }, + { + name: "execution-assethub", + account: "5E4Hf7LzHE4W3jabjLWSP8p8RzEa9ednwRivFEwYAprzpgwc", + type: "substrate", + }, + { + name: "parachain-assethub", + account: "0x302f0b71b8ad3cf6dd90adb668e49b2168d652fd", + type: "ethereum", + }, + ], + SUBSCAN_API: { + RELAY_CHAIN_URL: "https://westend.api.subscan.io", + ASSET_HUB_URL: "https://assethub-westend.api.subscan.io", + BRIDGE_HUB_URL: "https://bridgehub-westend.api.subscan.io", + }, + }, + }, } diff --git a/web/packages/api/src/status.ts b/web/packages/api/src/status.ts index 6bc4572c1b..fecda19f4c 100644 --- a/web/packages/api/src/status.ts +++ b/web/packages/api/src/status.ts @@ -63,6 +63,8 @@ export enum AlarmReason { ToEthereumChannelStale = "ToEthereumChannelStale", ToPolkadotChannelStale = "ToPolkadotChannelStale", AccountBalanceInsufficient = "AccountBalanceInsufficient", + ToEthereumNoTransfer = "ToEthereumNoTransfer", + ToPolkadotNoTransfer = "ToPolkadotNoTransfer", } export type Sovereign = { name: string; account: string; balance: bigint; type: SourceType } @@ -94,8 +96,12 @@ export const bridgeStatusInfo = async ( options = { polkadotBlockTimeInSeconds: 6, ethereumBlockTimeInSeconds: 12, - toPolkadotCheckIntervalInBlock: BlockLatencyThreshold.ToPolkadot, - toEthereumCheckIntervalInBlock: BlockLatencyThreshold.ToEthereum, + toPolkadotCheckIntervalInBlock: process.env["CheckIntervalToPolkadot"] + ? parseInt(process.env["CheckIntervalToPolkadot"]) + : BlockLatencyThreshold.ToPolkadot, + toEthereumCheckIntervalInBlock: process.env["CheckIntervalToEthereum"] + ? parseInt(process.env["CheckIntervalToEthereum"]) + : BlockLatencyThreshold.ToEthereum, } ): Promise => { // Beefy status @@ -194,8 +200,12 @@ export const channelStatusInfo = async ( context: Context, channelId: string, options = { - toPolkadotCheckIntervalInBlock: BlockLatencyThreshold.ToEthereum, - toEthereumCheckIntervalInBlock: BlockLatencyThreshold.ToPolkadot, + toPolkadotCheckIntervalInBlock: process.env["CheckIntervalToPolkadot"] + ? parseInt(process.env["CheckIntervalToPolkadot"]) + : BlockLatencyThreshold.ToPolkadot, + toEthereumCheckIntervalInBlock: process.env["CheckIntervalToEthereum"] + ? parseInt(process.env["CheckIntervalToEthereum"]) + : BlockLatencyThreshold.ToEthereum, } ): Promise => { const [inbound_nonce_eth, outbound_nonce_eth] = diff --git a/web/packages/contract-types/package.json b/web/packages/contract-types/package.json index 174ced3685..b9f8fcc83f 100644 --- a/web/packages/contract-types/package.json +++ b/web/packages/contract-types/package.json @@ -1,6 +1,6 @@ { "name": "@snowbridge/contract-types", - "version": "0.1.19", + "version": "0.1.21", "description": "Snowbridge contract type bindings", "license": "Apache-2.0", "repository": { diff --git a/web/packages/contracts/package.json b/web/packages/contracts/package.json index e639c095aa..b1219a9db8 100644 --- a/web/packages/contracts/package.json +++ b/web/packages/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@snowbridge/contracts", - "version": "0.1.19", + "version": "0.1.21", "description": "Snowbridge contract source and abi.", "license": "Apache-2.0", "repository": { diff --git a/web/packages/operations/.env.example b/web/packages/operations/.env.example index 378a348e98..a679a1de38 100644 --- a/web/packages/operations/.env.example +++ b/web/packages/operations/.env.example @@ -16,3 +16,10 @@ GRAPHQL_API_URL=https://data.snowbridge.network/graphql # Scan interval(in minutes) SCAN_INTERVAL=30 + +# Keys +ETHEREUM_KEY= +SUBSTRATE_KEY= + +PENPAL_TRANSFER=false +CRON_EXPRESSION=0 0 * * * diff --git a/web/packages/operations/ecosystem.config.js b/web/packages/operations/ecosystem.config.js index 64a1825368..a7807f8673 100644 --- a/web/packages/operations/ecosystem.config.js +++ b/web/packages/operations/ecosystem.config.js @@ -3,7 +3,20 @@ module.exports = { { name: "monitor", node_args: "--require=dotenv/config", - script: "./dist/src/cron.js", + script: "./dist/src/main.js", + args: "cron" + }, + { + name: "transferToPolkadot", + node_args: "--require=dotenv/config", + script: "./dist/src/transfer_to_polkadot.js", + args: "cron" + }, + { + name: "transferToEthereum", + node_args: "--require=dotenv/config", + script: "./dist/src/transfer_to_ethereum.js", + args: "cron" }, ], }; diff --git a/web/packages/operations/package.json b/web/packages/operations/package.json index 2727ad395e..124b588eb6 100644 --- a/web/packages/operations/package.json +++ b/web/packages/operations/package.json @@ -16,6 +16,9 @@ "start": "npx ts-node src/main.ts start", "cron": "npx ts-node src/main.ts cron", "server": "npx ts-node src/server.ts", + "smokeTest": "npx ts-node src/transfer_token.ts start", + "transferToPolkadot": "npx ts-node src/transfer_to_polkadot.ts start", + "transferToEthereum": "npx ts-node src/transfer_to_ethereum.ts start", "format": "prettier src --write" }, "devDependencies": { diff --git a/web/packages/operations/src/alarm.ts b/web/packages/operations/src/alarm.ts index dd77a3e1d6..6f3d68b564 100644 --- a/web/packages/operations/src/alarm.ts +++ b/web/packages/operations/src/alarm.ts @@ -64,6 +64,10 @@ export const sendMetrics = async (metrics: status.AllMetrics) => { }) // Channel metrics for (let channel of metrics.channels) { + // Only monitor AH channel + if (channel.name != status.ChannelKind.AssetHub) { + continue + } // To Ethereum metricData.push({ MetricName: "ToEthereumOutboundNonce", @@ -113,6 +117,12 @@ export const sendMetrics = async (metrics: status.AllMetrics) => { channel.toEthereum.inbound <= channel.toEthereum.previousInbound) ), }) + metricData.push({ + MetricName: AlarmReason.ToEthereumNoTransfer.toString(), + Value: Number( + channel.toEthereum.inbound == channel.toEthereum.previousInbound + ), + }) // To Polkadot metricData.push({ MetricName: "ToPolkadotOutboundNonce", diff --git a/web/packages/operations/src/global_transfer_history.ts b/web/packages/operations/src/global_transfer_history.ts index 54c687fe0e..e936b1f21f 100644 --- a/web/packages/operations/src/global_transfer_history.ts +++ b/web/packages/operations/src/global_transfer_history.ts @@ -9,12 +9,12 @@ const monitor = async () => { if (process.env.NODE_ENV !== undefined) { env = process.env.NODE_ENV } - const snwobridgeEnv = environment.SNOWBRIDGE_ENV[env] - if (snwobridgeEnv === undefined) { + const snowbridgeEnv = environment.SNOWBRIDGE_ENV[env] + if (snowbridgeEnv === undefined) { throw Error(`Unknown environment '${env}'`) } - const { config, ethChainId } = snwobridgeEnv + const { config, ethChainId } = snowbridgeEnv if (!config.SUBSCAN_API) throw Error(`Environment ${env} does not support subscan.`) const ethereumProvider = new AlchemyProvider(ethChainId, process.env.REACT_APP_ALCHEMY_KEY) diff --git a/web/packages/operations/src/transfer_to_ethereum.ts b/web/packages/operations/src/transfer_to_ethereum.ts new file mode 100644 index 0000000000..d6d692fbe2 --- /dev/null +++ b/web/packages/operations/src/transfer_to_ethereum.ts @@ -0,0 +1,89 @@ +import "dotenv/config" +import { Keyring } from "@polkadot/keyring" +import { + contextFactory, + destroyContext, + environment, + toEthereum, +} from "@snowbridge/api" +import { Wallet } from "ethers" +import cron from "node-cron" + +const transfer = async () => { + let env = "local_e2e" + if (process.env.NODE_ENV !== undefined) { + env = process.env.NODE_ENV + } + const snowbridgeEnv = environment.SNOWBRIDGE_ENV[env] + if (snowbridgeEnv === undefined) { + throw Error(`Unknown environment '${env}'`) + } + + const { config } = snowbridgeEnv + + const context = await contextFactory({ + ethereum: { + execution_url: process.env["EXECUTION_NODE_URL"] || config.ETHEREUM_API(process.env.REACT_APP_INFURA_KEY || ""), + beacon_url: config.BEACON_HTTP_API, + }, + polkadot: { + url: { + bridgeHub: config.BRIDGE_HUB_URL, + assetHub: config.ASSET_HUB_URL, + relaychain: config.RELAY_CHAIN_URL, + parachains: config.PARACHAINS, + }, + }, + appContracts: { + gateway: config.GATEWAY_CONTRACT, + beefy: config.BEEFY_CONTRACT, + }, + }) + const polkadot_keyring = new Keyring({ type: "sr25519" }) + + const ETHEREUM_ACCOUNT = new Wallet( + process.env["ETHEREUM_KEY"] || "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342", + context.ethereum.api + ) + const ETHEREUM_ACCOUNT_PUBLIC = await ETHEREUM_ACCOUNT.getAddress() + const POLKADOT_ACCOUNT = process.env["SUBSTRATE_KEY"]?polkadot_keyring.addFromUri(process.env["SUBSTRATE_KEY"]):polkadot_keyring.addFromUri("//Ferdie") + + const amount = 2_000_000_000_000n + + const WETH_CONTRACT = snowbridgeEnv.locations[0].erc20tokensReceivable.find( + (t) => t.id === "WETH" + )!.address + + console.log("# Asset Hub to Ethereum") + { + const plan = await toEthereum.validateSend( + context, + POLKADOT_ACCOUNT, + 1000, + ETHEREUM_ACCOUNT_PUBLIC, + WETH_CONTRACT, + amount + ) + console.log("Plan:", plan, plan.failure?.errors) + const result = await toEthereum.send(context, POLKADOT_ACCOUNT, plan) + console.log("Execute:", result) + } + await destroyContext(context) +} + +if (process.argv.length != 3) { + console.error("Expected one argument with Enum from `start|cron`") + process.exit(1) +} + +if (process.argv[2] == "start") { + transfer() + .then(() => process.exit(0)) + .catch((error) => { + console.error("Error:", error) + process.exit(1) + }) +} else if (process.argv[2] == "cron") { + console.log("running cronjob") + cron.schedule(process.env["CRON_EXPRESSION"] || "0 0 * * *", transfer) +} diff --git a/web/packages/operations/src/transfer_to_polkadot.ts b/web/packages/operations/src/transfer_to_polkadot.ts new file mode 100644 index 0000000000..a2ff782e5e --- /dev/null +++ b/web/packages/operations/src/transfer_to_polkadot.ts @@ -0,0 +1,103 @@ +import "dotenv/config" +import { Keyring } from "@polkadot/keyring" +import { + contextFactory, + destroyContext, + environment, + toPolkadot, +} from "@snowbridge/api" +import { WETH9__factory } from "@snowbridge/contract-types" +import { Wallet } from "ethers" +import cron from "node-cron" + +const transfer = async () => { + let env = "local_e2e" + if (process.env.NODE_ENV !== undefined) { + env = process.env.NODE_ENV + } + const snowbridgeEnv = environment.SNOWBRIDGE_ENV[env] + if (snowbridgeEnv === undefined) { + throw Error(`Unknown environment '${env}'`) + } + + const { config } = snowbridgeEnv + + const context = await contextFactory({ + ethereum: { + execution_url: process.env["EXECUTION_NODE_URL"] || config.ETHEREUM_API(process.env.REACT_APP_INFURA_KEY || ""), + beacon_url: config.BEACON_HTTP_API, + }, + polkadot: { + url: { + bridgeHub: config.BRIDGE_HUB_URL, + assetHub: config.ASSET_HUB_URL, + relaychain: config.RELAY_CHAIN_URL, + parachains: config.PARACHAINS, + }, + }, + appContracts: { + gateway: config.GATEWAY_CONTRACT, + beefy: config.BEEFY_CONTRACT, + }, + }) + const polkadot_keyring = new Keyring({ type: "sr25519" }) + + const ETHEREUM_ACCOUNT = new Wallet( + process.env["ETHEREUM_KEY"] || "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342", + context.ethereum.api + ) + const POLKADOT_ACCOUNT = process.env["SUBSTRATE_KEY"]?polkadot_keyring.addFromUri(process.env["SUBSTRATE_KEY"]):polkadot_keyring.addFromUri("//Ferdie") + const POLKADOT_ACCOUNT_PUBLIC = POLKADOT_ACCOUNT.address + + const amount = 2_000_000_000_000n + + const WETH_CONTRACT = snowbridgeEnv.locations[0].erc20tokensReceivable.find( + (t) => t.id === "WETH" + )!.address + + console.log("# Deposit and Approve WETH") + { + const weth9 = WETH9__factory.connect(WETH_CONTRACT, ETHEREUM_ACCOUNT) + const depositResult = await weth9.deposit({ value: amount }) + const depositReceipt = await depositResult.wait() + + const approveResult = await weth9.approve(config.GATEWAY_CONTRACT, amount) + const approveReceipt = await approveResult.wait() + + console.log('deposit tx', depositReceipt?.hash, 'approve tx', approveReceipt?.hash) + } + + console.log("# Ethereum to Asset Hub") + { + const plan = await toPolkadot.validateSend( + context, + ETHEREUM_ACCOUNT, + POLKADOT_ACCOUNT_PUBLIC, + WETH_CONTRACT, + 1000, + amount, + BigInt(0) + ) + console.log("Plan:", plan, plan.failure?.errors) + let result = await toPolkadot.send(context, ETHEREUM_ACCOUNT, plan) + console.log("Execute:", result) + } + await destroyContext(context) +} + +if (process.argv.length != 3) { + console.error("Expected one argument with Enum from `start|cron`") + process.exit(1) +} + +if (process.argv[2] == "start") { + transfer() + .then(() => process.exit(0)) + .catch((error) => { + console.error("Error:", error) + process.exit(1) + }) +} else if (process.argv[2] == "cron") { + console.log("running cronjob") + cron.schedule(process.env["CRON_EXPRESSION"] || "0 0 * * *", transfer) +} diff --git a/web/packages/test/config/beacon-relay.json b/web/packages/test/config/beacon-relay.json index b7ac5fc9d6..73534b66f6 100644 --- a/web/packages/test/config/beacon-relay.json +++ b/web/packages/test/config/beacon-relay.json @@ -18,7 +18,8 @@ "sink": { "parachain": { "endpoint": "ws://127.0.0.1:11144", - "maxWatchedExtrinsics": 8 + "maxWatchedExtrinsics": 8, + "headerRedundancy": 20 }, "updateSlotInterval": 30 } diff --git a/web/packages/test/config/execution-relay.json b/web/packages/test/config/execution-relay.json index 9bd122aeb7..f94caa75c3 100644 --- a/web/packages/test/config/execution-relay.json +++ b/web/packages/test/config/execution-relay.json @@ -25,7 +25,8 @@ "sink": { "parachain": { "endpoint": "ws://127.0.0.1:11144", - "maxWatchedExtrinsics": 8 + "maxWatchedExtrinsics": 8, + "headerRedundancy": 20 } }, "instantVerification": false, diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index d43ffb9035..83347d9973 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -2528,7 +2528,7 @@ packages: resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} engines: {node: '>= 4.0'} os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + deprecated: Upgrade to fsevents v2 to mitigate potential security issues fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}