diff --git a/cmd/api/docs/docs.go b/cmd/api/docs/docs.go index 450c739..e9a713a 100644 --- a/cmd/api/docs/docs.go +++ b/cmd/api/docs/docs.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 PK Lab AG +// SPDX-License-Identifier: MIT + // Package docs Code generated by swaggo/swag. DO NOT EDIT package docs @@ -2102,6 +2105,9 @@ const docTemplate = `{ "balance": { "$ref": "#/definitions/responses.Balance" }, + "bridged_rollup": { + "type": "string" + }, "first_height": { "type": "integer", "example": 100 @@ -2337,6 +2343,10 @@ const docTemplate = `{ "type": "integer", "example": 100 }, + "bridge_address": { + "type": "string", + "example": "115F94D8C98FFD73FE65182611140F0EDC7C3C94" + }, "first_height": { "type": "integer", "example": 100 diff --git a/cmd/api/docs/swagger.json b/cmd/api/docs/swagger.json index 5fd1ec6..a9537cf 100644 --- a/cmd/api/docs/swagger.json +++ b/cmd/api/docs/swagger.json @@ -2095,6 +2095,9 @@ "balance": { "$ref": "#/definitions/responses.Balance" }, + "bridged_rollup": { + "type": "string" + }, "first_height": { "type": "integer", "example": 100 @@ -2330,6 +2333,10 @@ "type": "integer", "example": 100 }, + "bridge_address": { + "type": "string", + "example": "115F94D8C98FFD73FE65182611140F0EDC7C3C94" + }, "first_height": { "type": "integer", "example": 100 diff --git a/cmd/api/docs/swagger.yaml b/cmd/api/docs/swagger.yaml index 7722908..c523212 100644 --- a/cmd/api/docs/swagger.yaml +++ b/cmd/api/docs/swagger.yaml @@ -42,6 +42,8 @@ definitions: type: integer balance: $ref: '#/definitions/responses.Balance' + bridged_rollup: + type: string first_height: example: 100 type: integer @@ -213,6 +215,9 @@ definitions: actions_count: example: 100 type: integer + bridge_address: + example: 115F94D8C98FFD73FE65182611140F0EDC7C3C94 + type: string first_height: example: 100 type: integer diff --git a/cmd/api/handler/address.go b/cmd/api/handler/address.go index 4655ebe..188891a 100644 --- a/cmd/api/handler/address.go +++ b/cmd/api/handler/address.go @@ -74,7 +74,12 @@ func (handler *AddressHandler) Get(c echo.Context) error { return handleError(c, err, handler.address) } - return c.JSON(http.StatusOK, responses.NewAddress(address)) + rollup, err := handler.rollups.ByBridgeAddress(c.Request().Context(), address.Id) + if err != nil { + return handleError(c, err, handler.address) + } + + return c.JSON(http.StatusOK, responses.NewAddress(address, &rollup)) } // List godoc @@ -111,7 +116,7 @@ func (handler *AddressHandler) List(c echo.Context) error { response := make([]responses.Address, len(address)) for i := range address { - response[i] = responses.NewAddress(address[i]) + response[i] = responses.NewAddress(address[i], nil) } return returnArray(c, response) diff --git a/cmd/api/handler/address_test.go b/cmd/api/handler/address_test.go index b651c2e..822fc32 100644 --- a/cmd/api/handler/address_test.go +++ b/cmd/api/handler/address_test.go @@ -70,6 +70,11 @@ func (s *AddressTestSuite) TestGet() { Return(testAddress, nil). Times(1) + s.rollups.EXPECT(). + ByBridgeAddress(gomock.Any(), testAddress.Id). + Return(testRollup, nil). + Times(1) + s.Require().NoError(s.handler.Get(c)) s.Require().Equal(http.StatusOK, rec.Code) @@ -82,6 +87,7 @@ func (s *AddressTestSuite) TestGet() { s.Require().EqualValues(0, address.Height) s.Require().EqualValues(10, address.Nonce) s.Require().Equal(testAddressHash, address.Hash) + s.Require().Equal(testRollup.String(), address.BridgedRollup) } func (s *AddressTestSuite) TestGetInvalidAddress() { diff --git a/cmd/api/handler/responses/address.go b/cmd/api/handler/responses/address.go index c0e8944..b5755c0 100644 --- a/cmd/api/handler/responses/address.go +++ b/cmd/api/handler/responses/address.go @@ -4,8 +4,6 @@ package responses import ( - "encoding/hex" - "github.com/celenium-io/astria-indexer/internal/storage" pkgTypes "github.com/celenium-io/astria-indexer/pkg/types" ) @@ -21,16 +19,17 @@ type Address struct { Nonce uint32 `example:"10" json:"nonce" swaggertype:"integer"` Hash string `example:"115F94D8C98FFD73FE65182611140F0EDC7C3C94" json:"hash" swaggertype:"string"` Balance *Balance `json:"balance,omitempty"` + BridgedRollup string `json:"bridged_rollup,omitempty"` } -func NewAddress(addr storage.Address) Address { +func NewAddress(addr storage.Address, bridgedRollup *storage.Rollup) Address { result := Address{ Id: addr.Id, Height: addr.Height, ActionsCount: addr.ActionsCount, SignedTxCount: addr.SignedTxCount, Nonce: addr.Nonce, - Hash: hex.EncodeToString(addr.Hash), + Hash: addr.String(), } if addr.Balance != nil { @@ -39,6 +38,9 @@ func NewAddress(addr storage.Address) Address { Value: addr.Balance.Total.String(), } } + if bridgedRollup != nil { + result.BridgedRollup = bridgedRollup.String() + } return result } diff --git a/cmd/api/handler/responses/rollup.go b/cmd/api/handler/responses/rollup.go index a1ff292..a84e9e4 100644 --- a/cmd/api/handler/responses/rollup.go +++ b/cmd/api/handler/responses/rollup.go @@ -11,19 +11,26 @@ import ( ) type Rollup struct { - Id uint64 `example:"321" json:"id" swaggertype:"integer"` - FirstHeight types.Level `example:"100" json:"first_height" swaggertype:"integer"` - AstriaId string `example:"19ba8abb3e4b56a309df6756c47b97e298e3a72d88449d36a0fadb1ca7366539" json:"hash" swaggertype:"string"` - ActionsCount int64 `example:"100" json:"actions_count" swaggertype:"integer"` - Size int64 `example:"100" json:"size" swaggertype:"integer"` + Id uint64 `example:"321" json:"id" swaggertype:"integer"` + FirstHeight types.Level `example:"100" json:"first_height" swaggertype:"integer"` + AstriaId string `example:"19ba8abb3e4b56a309df6756c47b97e298e3a72d88449d36a0fadb1ca7366539" json:"hash" swaggertype:"string"` + ActionsCount int64 `example:"100" json:"actions_count" swaggertype:"integer"` + Size int64 `example:"100" json:"size" swaggertype:"integer"` + BridgeAddress string `example:"115F94D8C98FFD73FE65182611140F0EDC7C3C94" json:"bridge_address,omitempty" swaggertype:"string"` } func NewRollup(rollup *storage.Rollup) Rollup { - return Rollup{ + r := Rollup{ Id: rollup.Id, AstriaId: hex.EncodeToString(rollup.AstriaId), FirstHeight: rollup.FirstHeight, ActionsCount: rollup.ActionsCount, Size: rollup.Size, } + + if rollup.BridgeAddress != nil { + r.BridgeAddress = rollup.BridgeAddress.String() + } + + return r } diff --git a/cmd/api/handler/rollup.go b/cmd/api/handler/rollup.go index bd15b50..e6fe94e 100644 --- a/cmd/api/handler/rollup.go +++ b/cmd/api/handler/rollup.go @@ -244,7 +244,7 @@ func (handler *RollupHandler) Addresses(c echo.Context) error { response := make([]responses.Address, len(addresses)) for i := range addresses { if addresses[i].Address != nil { - response[i] = responses.NewAddress(*addresses[i].Address) + response[i] = responses.NewAddress(*addresses[i].Address, nil) } } diff --git a/cmd/api/handler/search.go b/cmd/api/handler/search.go index e4d163b..8a69bbd 100644 --- a/cmd/api/handler/search.go +++ b/cmd/api/handler/search.go @@ -69,7 +69,7 @@ func (s *SearchHandler) Search(c echo.Context) error { return internalServerError(c, err) } results := []responses.SearchResult{ - responses.NewSearchResult(address.String(), "address", responses.NewAddress(address)), + responses.NewSearchResult(address.String(), "address", responses.NewAddress(address, nil)), } return returnArray(c, results) } diff --git a/internal/storage/mock/rollup.go b/internal/storage/mock/rollup.go index 8fb88ae..6a54681 100644 --- a/internal/storage/mock/rollup.go +++ b/internal/storage/mock/rollup.go @@ -161,6 +161,45 @@ func (c *IRollupAddressesCall) DoAndReturn(f func(context.Context, uint64, int, return c } +// ByBridgeAddress mocks base method. +func (m *MockIRollup) ByBridgeAddress(ctx context.Context, id uint64) (storage.Rollup, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ByBridgeAddress", ctx, id) + ret0, _ := ret[0].(storage.Rollup) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ByBridgeAddress indicates an expected call of ByBridgeAddress. +func (mr *MockIRollupMockRecorder) ByBridgeAddress(ctx, id any) *IRollupByBridgeAddressCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByBridgeAddress", reflect.TypeOf((*MockIRollup)(nil).ByBridgeAddress), ctx, id) + return &IRollupByBridgeAddressCall{Call: call} +} + +// IRollupByBridgeAddressCall wrap *gomock.Call +type IRollupByBridgeAddressCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *IRollupByBridgeAddressCall) Return(arg0 storage.Rollup, arg1 error) *IRollupByBridgeAddressCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *IRollupByBridgeAddressCall) Do(f func(context.Context, uint64) (storage.Rollup, error)) *IRollupByBridgeAddressCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *IRollupByBridgeAddressCall) DoAndReturn(f func(context.Context, uint64) (storage.Rollup, error)) *IRollupByBridgeAddressCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // ByHash mocks base method. func (m *MockIRollup) ByHash(ctx context.Context, hash []byte) (storage.Rollup, error) { m.ctrl.T.Helper() diff --git a/internal/storage/postgres/index.go b/internal/storage/postgres/index.go index dc7504d..d5922cc 100644 --- a/internal/storage/postgres/index.go +++ b/internal/storage/postgres/index.go @@ -149,6 +149,15 @@ func createIndices(ctx context.Context, conn *database.Bun) error { Exec(ctx); err != nil { return err } + if _, err := tx.NewCreateIndex(). + IfNotExists(). + Model((*storage.Rollup)(nil)). + Index("rollup_bridge_address_id_idx"). + Column("bridge_address_id"). + Where("bridge_address_id is not null"). + Exec(ctx); err != nil { + return err + } // Rollup actions if _, err := tx.NewCreateIndex(). diff --git a/internal/storage/postgres/rollup.go b/internal/storage/postgres/rollup.go index b7669c3..a0be0a5 100644 --- a/internal/storage/postgres/rollup.go +++ b/internal/storage/postgres/rollup.go @@ -66,9 +66,16 @@ func (r *Rollup) CountActionsByTxId(ctx context.Context, txId uint64) (int64, er } func (r *Rollup) ByHash(ctx context.Context, hash []byte) (rollup storage.Rollup, err error) { - err = r.DB().NewSelect().Model(&rollup). + query := r.DB().NewSelect().Model((*storage.Rollup)(nil)). Where("astria_id = ?", hash). - Scan(ctx) + Limit(1) + + err = r.DB().NewSelect(). + TableExpr("(?) as rollup", query). + ColumnExpr("rollup.*"). + ColumnExpr("address.hash as bridge_address__hash"). + Join("left join address on address.id = rollup.bridge_address_id"). + Scan(ctx, &rollup) return } @@ -113,3 +120,12 @@ func (r *Rollup) ListExt(ctx context.Context, fltrs storage.RollupListFilter) (r err = query.Scan(ctx) return } + +func (r *Rollup) ByBridgeAddress(ctx context.Context, id uint64) (rollup storage.Rollup, err error) { + err = r.DB().NewSelect(). + Model(&rollup). + Where("bridge_address_id = ?", id). + Limit(1). + Scan(ctx) + return +} diff --git a/internal/storage/postgres/rollup_test.go b/internal/storage/postgres/rollup_test.go index b150fbf..d40eda2 100644 --- a/internal/storage/postgres/rollup_test.go +++ b/internal/storage/postgres/rollup_test.go @@ -76,6 +76,8 @@ func (s *StorageTestSuite) TestRollupByHash() { s.Require().EqualValues(hash, rollup.AstriaId) s.Require().EqualValues(112, rollup.Size) s.Require().EqualValues(1, rollup.ActionsCount) + + s.Require().Nil(rollup.BridgeAddress) } func (s *StorageTestSuite) TestRollupAddresses() { @@ -122,25 +124,23 @@ func (s *StorageTestSuite) TestListExt() { "", } { rollups, err := s.storage.Rollup.ListExt(ctx, models.RollupListFilter{ - Limit: 1, + Limit: 2, Offset: 0, SortField: field, SortOrder: order, }) s.Require().NoError(err) - s.Require().Len(rollups, 1) - - rollup := rollups[0] - - hash, err := hex.DecodeString("19ba8abb3e4b56a309df6756c47b97e298e3a72d88449d36a0fadb1ca7366539") - s.Require().NoError(err) - - s.Require().EqualValues(1, rollup.Id) - s.Require().EqualValues(hash, rollup.AstriaId) - s.Require().EqualValues(112, rollup.Size) - s.Require().EqualValues(1, rollup.ActionsCount) - s.Require().EqualValues(7316, rollup.FirstHeight) + s.Require().Len(rollups, 2) } } } + +func (s *StorageTestSuite) TestByBridgedAddress() { + ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer ctxCancel() + + rollup, err := s.storage.Rollup.ByBridgeAddress(ctx, 2) + s.Require().NoError(err) + s.Require().EqualValues(2, rollup.Id) +} diff --git a/internal/storage/postgres/transaction.go b/internal/storage/postgres/transaction.go index 0ac38bf..cf03774 100644 --- a/internal/storage/postgres/transaction.go +++ b/internal/storage/postgres/transaction.go @@ -131,14 +131,14 @@ func (tx Transaction) SaveRollups(ctx context.Context, rollups ...*models.Rollup rs[i].Rollup = rollups[i] } - _, err := tx.Tx().NewInsert().Model(&rs). - Column("first_height", "astria_id", "actions_count", "size"). + query := tx.Tx().NewInsert().Model(&rs). + Column("first_height", "astria_id", "actions_count", "size", "bridge_address_id"). On("CONFLICT ON CONSTRAINT rollup_id DO UPDATE"). Set("actions_count = added_rollup.actions_count + EXCLUDED.actions_count"). Set("size = added_rollup.size + EXCLUDED.size"). - Returning("xmax, id"). - Exec(ctx) - if err != nil { + Set("bridge_address_id = case when EXCLUDED.bridge_address_id is not null then EXCLUDED.bridge_address_id else added_rollup.bridge_address_id end") + + if _, err := query.Returning("xmax, id").Exec(ctx); err != nil { return 0, err } @@ -149,7 +149,7 @@ func (tx Transaction) SaveRollups(ctx context.Context, rollups ...*models.Rollup } } - return count, err + return count, nil } func (tx Transaction) SaveRollupActions(ctx context.Context, actions ...*models.RollupAction) error { diff --git a/internal/storage/postgres/transaction_test.go b/internal/storage/postgres/transaction_test.go index 804bc17..a608a7f 100644 --- a/internal/storage/postgres/transaction_test.go +++ b/internal/storage/postgres/transaction_test.go @@ -248,6 +248,9 @@ func (s *TransactionTestSuite) TestSaveRollups() { ActionsCount: 1, Size: 10, } + if i%2 == 1 { + rollups[i].BridgeAddressId = uint64(i) + } } count, err := tx.SaveRollups(ctx, rollups...) @@ -256,6 +259,10 @@ func (s *TransactionTestSuite) TestSaveRollups() { s.Require().NoError(tx.Flush(ctx)) s.Require().NoError(tx.Close(ctx)) + + ret, err := s.storage.Rollup.List(ctx, 10, 0, sdk.SortOrderAsc) + s.Require().NoError(err) + s.Require().Len(ret, 7) } func (s *TransactionTestSuite) TestSaveRollupActions() { diff --git a/internal/storage/rollup.go b/internal/storage/rollup.go index aeaffbf..e83b3d0 100644 --- a/internal/storage/rollup.go +++ b/internal/storage/rollup.go @@ -25,16 +25,20 @@ type IRollup interface { Addresses(ctx context.Context, rollupId uint64, limit, offset int, sort sdk.SortOrder) ([]RollupAddress, error) ListRollupsByAddress(ctx context.Context, addressId uint64, limit, offset int, sort sdk.SortOrder) ([]RollupAddress, error) ListExt(ctx context.Context, fltrs RollupListFilter) ([]Rollup, error) + ByBridgeAddress(ctx context.Context, id uint64) (Rollup, error) } type Rollup struct { bun.BaseModel `bun:"rollup" comment:"Table with rollups"` - Id uint64 `bun:"id,pk,notnull,autoincrement" comment:"Unique internal identity"` - AstriaId []byte `bun:"astria_id,unique:rollup_id" comment:"Astria rollup identity"` - FirstHeight types.Level `bun:"first_height" comment:"Block number of the first rollup occurance"` - ActionsCount int64 `bun:"actions_count" comment:"Count of actions in which the rollup was involved"` - Size int64 `bun:"size" comment:"Count bytes which was saved in the rollup"` + Id uint64 `bun:"id,pk,notnull,autoincrement" comment:"Unique internal identity"` + AstriaId []byte `bun:"astria_id,unique:rollup_id" comment:"Astria rollup identity"` + FirstHeight types.Level `bun:"first_height" comment:"Block number of the first rollup occurance"` + ActionsCount int64 `bun:"actions_count" comment:"Count of actions in which the rollup was involved"` + Size int64 `bun:"size" comment:"Count bytes which was saved in the rollup"` + BridgeAddressId uint64 `bun:"bridge_address_id" comment:"Address id associated with rollup"` + + BridgeAddress *Address `bun:"rel:has-one,join:bridge_address_id=id"` } // TableName - diff --git a/pkg/indexer/decode/actions.go b/pkg/indexer/decode/actions.go index d7be7df..cfd7b4c 100644 --- a/pkg/indexer/decode/actions.go +++ b/pkg/indexer/decode/actions.go @@ -65,7 +65,7 @@ func parseActions(height types.Level, blockTime time.Time, from bytes.HexBytes, err = parseIbcRelayerChange(val, height, ctx, &actions[i]) case *astria.Action_InitBridgeAccountAction: tx.ActionTypes.Set(storageTypes.ActionTypeInitBridgeAccountBits) - err = parseInitBridgeAccount(val, height, ctx, &actions[i]) + err = parseInitBridgeAccount(val, from, height, ctx, &actions[i]) default: return nil, errors.Errorf( "unknown action type | position = %d | block = %d: %##v", @@ -341,7 +341,7 @@ func parseIbcRelayerChange(body *astria.Action_IbcRelayerChangeAction, height ty return nil } -func parseInitBridgeAccount(body *astria.Action_InitBridgeAccountAction, height types.Level, ctx *Context, action *storage.Action) error { +func parseInitBridgeAccount(body *astria.Action_InitBridgeAccountAction, from bytes.HexBytes, height types.Level, ctx *Context, action *storage.Action) error { action.Type = storageTypes.ActionTypeInitBridgeAccount action.Data = make(map[string]any) if body.InitBridgeAccountAction != nil { @@ -356,6 +356,12 @@ func parseInitBridgeAccount(body *astria.Action_InitBridgeAccountAction, height Action: action, Rollup: rollup, } + + fromAddress, ok := ctx.Addresses.Get(from) + if !ok { + return errors.Errorf("unknown from address: %s", from.String()) + } + rollup.BridgeAddress = fromAddress } return nil } diff --git a/pkg/indexer/decode/actions_test.go b/pkg/indexer/decode/actions_test.go index d75b7b9..4a5fb16 100644 --- a/pkg/indexer/decode/actions_test.go +++ b/pkg/indexer/decode/actions_test.go @@ -586,6 +586,8 @@ func TestDecodeActions(t *testing.T) { assetId := testsuite.RandomHash(32) rollupId := testsuite.RandomHash(10) feAssetId := testsuite.RandomHash(32) + from := testsuite.RandomHash(20) + fromAddr := decodeContext.Addresses.Set(from, 1000, decimal.Zero, 0, 1) message := &astria.Action_InitBridgeAccountAction{ InitBridgeAccountAction: &astria.InitBridgeAccountAction{ @@ -606,9 +608,10 @@ func TestDecodeActions(t *testing.T) { RollupAction: &storage.RollupAction{ Height: 1000, Rollup: &storage.Rollup{ - AstriaId: message.InitBridgeAccountAction.RollupId, - FirstHeight: 1000, - ActionsCount: 1, + AstriaId: message.InitBridgeAccountAction.RollupId, + FirstHeight: 1000, + ActionsCount: 1, + BridgeAddress: fromAddr, }, }, } @@ -617,7 +620,7 @@ func TestDecodeActions(t *testing.T) { action := storage.Action{ Height: 1000, } - err := parseInitBridgeAccount(message, 1000, &decodeContext, &action) + err := parseInitBridgeAccount(message, from, 1000, &decodeContext, &action) require.NoError(t, err) require.Equal(t, wantAction, action) }) diff --git a/pkg/indexer/storage/action.go b/pkg/indexer/storage/action.go index 4f71b1e..d4c22e5 100644 --- a/pkg/indexer/storage/action.go +++ b/pkg/indexer/storage/action.go @@ -13,7 +13,6 @@ func saveAction( ctx context.Context, tx storage.Transaction, actions []*storage.Action, - addrToId map[string]uint64, ) error { if len(actions) == 0 { return nil diff --git a/pkg/indexer/storage/rollup.go b/pkg/indexer/storage/rollup.go index 7f419b9..99a2f6d 100644 --- a/pkg/indexer/storage/rollup.go +++ b/pkg/indexer/storage/rollup.go @@ -7,11 +7,13 @@ import ( "context" "github.com/celenium-io/astria-indexer/internal/storage" + "github.com/pkg/errors" ) func (module *Module) saveRollup( ctx context.Context, tx storage.Transaction, + addrToId map[string]uint64, rollups map[string]*storage.Rollup, rollupAddress map[string]*storage.RollupAddress, ) (int64, error) { @@ -21,6 +23,11 @@ func (module *Module) saveRollup( data := make([]*storage.Rollup, 0) for _, value := range rollups { + if id, ok := addrToId[value.BridgeAddress.String()]; ok { + value.BridgeAddressId = id + } else { + return 0, errors.Errorf("unknown bridge address id: %s", value.BridgeAddress.String()) + } data = append(data, value) } diff --git a/pkg/indexer/storage/storage.go b/pkg/indexer/storage/storage.go index e74530b..c57d4b5 100644 --- a/pkg/indexer/storage/storage.go +++ b/pkg/indexer/storage/storage.go @@ -172,7 +172,7 @@ func (module *Module) processBlockInTransaction(ctx context.Context, tx storage. return state, err } - totalRollups, err := module.saveRollup(ctx, tx, block.Rollups, block.RollupAddress) + totalRollups, err := module.saveRollup(ctx, tx, addrToId, block.Rollups, block.RollupAddress) if err != nil { return state, err } @@ -186,7 +186,7 @@ func (module *Module) processBlockInTransaction(ctx context.Context, tx storage. } } - if err := saveAction(ctx, tx, actions, addrToId); err != nil { + if err := saveAction(ctx, tx, actions); err != nil { return state, err } diff --git a/test/data/rollup.yml b/test/data/rollup.yml index b3ab4da..d66860f 100644 --- a/test/data/rollup.yml +++ b/test/data/rollup.yml @@ -3,3 +3,10 @@ first_height: 7316 actions_count: 1 size: 112 + bridge_address_id: null +- id: 2 + astria_id: 0xf69ac0156da05bc30d82e516641be86c8fbee5ad8f38ca2b1c4c145249dde6a3 + first_height: 23456 + actions_count: 10 + size: 1120 + bridge_address_id: 2