diff --git a/backend/pkg/api/data_access/block.go b/backend/pkg/api/data_access/block.go new file mode 100644 index 000000000..65e63b408 --- /dev/null +++ b/backend/pkg/api/data_access/block.go @@ -0,0 +1,146 @@ +package dataaccess + +import ( + "context" + + t "github.com/gobitfly/beaconchain/pkg/api/types" +) + +type BlockRepository interface { + GetBlock(ctx context.Context, chainId, block uint64) (*t.BlockSummary, error) + GetBlockOverview(ctx context.Context, chainId, block uint64) (*t.BlockOverview, error) + GetBlockTransactions(ctx context.Context, chainId, block uint64) ([]t.BlockTransactionTableRow, error) + GetBlockVotes(ctx context.Context, chainId, block uint64) ([]t.BlockVoteTableRow, error) + GetBlockAttestations(ctx context.Context, chainId, block uint64) ([]t.BlockAttestationTableRow, error) + GetBlockWithdrawals(ctx context.Context, chainId, block uint64) ([]t.BlockWithdrawalTableRow, error) + GetBlockBlsChanges(ctx context.Context, chainId, block uint64) ([]t.BlockBlsChangeTableRow, error) + GetBlockVoluntaryExits(ctx context.Context, chainId, block uint64) ([]t.BlockVoluntaryExitTableRow, error) + GetBlockBlobs(ctx context.Context, chainId, block uint64) ([]t.BlockBlobTableRow, error) + + GetSlot(ctx context.Context, chainId, block uint64) (*t.BlockSummary, error) + GetSlotOverview(ctx context.Context, chainId, block uint64) (*t.BlockOverview, error) + GetSlotTransactions(ctx context.Context, chainId, block uint64) ([]t.BlockTransactionTableRow, error) + GetSlotVotes(ctx context.Context, chainId, block uint64) ([]t.BlockVoteTableRow, error) + GetSlotAttestations(ctx context.Context, chainId, block uint64) ([]t.BlockAttestationTableRow, error) + GetSlotWithdrawals(ctx context.Context, chainId, block uint64) ([]t.BlockWithdrawalTableRow, error) + GetSlotBlsChanges(ctx context.Context, chainId, block uint64) ([]t.BlockBlsChangeTableRow, error) + GetSlotVoluntaryExits(ctx context.Context, chainId, block uint64) ([]t.BlockVoluntaryExitTableRow, error) + GetSlotBlobs(ctx context.Context, chainId, block uint64) ([]t.BlockBlobTableRow, error) +} + +func (d *DataAccessService) GetBlock(ctx context.Context, chainId, block uint64) (*t.BlockSummary, error) { + // @DATA-ACCESS + return d.dummy.GetBlock(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockOverview(ctx context.Context, chainId, block uint64) (*t.BlockOverview, error) { + // @DATA-ACCESS + return d.dummy.GetBlockOverview(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockTransactions(ctx context.Context, chainId, block uint64) ([]t.BlockTransactionTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockTransactions(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockVotes(ctx context.Context, chainId, block uint64) ([]t.BlockVoteTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockVotes(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockAttestations(ctx context.Context, chainId, block uint64) ([]t.BlockAttestationTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockAttestations(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockWithdrawals(ctx context.Context, chainId, block uint64) ([]t.BlockWithdrawalTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockWithdrawals(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockBlsChanges(ctx context.Context, chainId, block uint64) ([]t.BlockBlsChangeTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockBlsChanges(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockVoluntaryExits(ctx context.Context, chainId, block uint64) ([]t.BlockVoluntaryExitTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockVoluntaryExits(ctx, chainId, block) +} + +func (d *DataAccessService) GetBlockBlobs(ctx context.Context, chainId, block uint64) ([]t.BlockBlobTableRow, error) { + // @DATA-ACCESS + return d.dummy.GetBlockBlobs(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlot(ctx context.Context, chainId, slot uint64) (*t.BlockSummary, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlock(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotOverview(ctx context.Context, chainId, slot uint64) (*t.BlockOverview, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockOverview(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotTransactions(ctx context.Context, chainId, slot uint64) ([]t.BlockTransactionTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockTransactions(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotVotes(ctx context.Context, chainId, slot uint64) ([]t.BlockVoteTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockVotes(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotAttestations(ctx context.Context, chainId, slot uint64) ([]t.BlockAttestationTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockAttestations(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotWithdrawals(ctx context.Context, chainId, slot uint64) ([]t.BlockWithdrawalTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockWithdrawals(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotBlsChanges(ctx context.Context, chainId, slot uint64) ([]t.BlockBlsChangeTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockBlsChanges(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotVoluntaryExits(ctx context.Context, chainId, slot uint64) ([]t.BlockVoluntaryExitTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockVoluntaryExits(ctx, chainId, block) +} + +func (d *DataAccessService) GetSlotBlobs(ctx context.Context, chainId, slot uint64) ([]t.BlockBlobTableRow, error) { + block, err := d.GetBlockHeightAt(slot) + if err != nil { + return nil, err + } + return d.GetBlockBlobs(ctx, chainId, block) +} diff --git a/backend/pkg/api/data_access/data_access.go b/backend/pkg/api/data_access/data_access.go index e04198870..5dbb915c3 100644 --- a/backend/pkg/api/data_access/data_access.go +++ b/backend/pkg/api/data_access/data_access.go @@ -25,10 +25,13 @@ type DataAccessor interface { UserRepository NotificationsRepository AdminRepository + BlockRepository Close() GetLatestSlot() (uint64, error) + GetLatestBlock() (uint64, error) + GetBlockHeightAt(slot uint64) (uint64, error) GetLatestExchangeRates() ([]t.EthConversionRate, error) GetProductSummary(ctx context.Context) (*t.ProductSummary, error) diff --git a/backend/pkg/api/data_access/dummy.go b/backend/pkg/api/data_access/dummy.go index f4683ae00..cae2669eb 100644 --- a/backend/pkg/api/data_access/dummy.go +++ b/backend/pkg/api/data_access/dummy.go @@ -63,6 +63,18 @@ func (d *DummyService) GetLatestSlot() (uint64, error) { return r, err } +func (d *DummyService) GetLatestBlock() (uint64, error) { + r := uint64(0) + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockHeightAt(slot uint64) (uint64, error) { + r := uint64(0) + err := commonFakeData(&r) + return r, err +} + func (d *DummyService) GetLatestExchangeRates() ([]t.EthConversionRate, error) { r := []t.EthConversionRate{} err := commonFakeData(&r) @@ -646,3 +658,111 @@ func (d *DummyService) UpdateAdConfiguration(ctx context.Context, key, jquerySel func (d *DummyService) RemoveAdConfiguration(ctx context.Context, key string) error { return nil } + +func (d *DummyService) GetBlock(ctx context.Context, chainId, block uint64) (*t.BlockSummary, error) { + r := t.BlockSummary{} + err := commonFakeData(&r) + return &r, err +} + +func (d *DummyService) GetBlockOverview(ctx context.Context, chainId, block uint64) (*t.BlockOverview, error) { + r := t.BlockOverview{} + err := commonFakeData(&r) + return &r, err +} + +func (d *DummyService) GetBlockTransactions(ctx context.Context, chainId, block uint64) ([]t.BlockTransactionTableRow, error) { + r := []t.BlockTransactionTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockVotes(ctx context.Context, chainId, block uint64) ([]t.BlockVoteTableRow, error) { + r := []t.BlockVoteTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockAttestations(ctx context.Context, chainId, block uint64) ([]t.BlockAttestationTableRow, error) { + r := []t.BlockAttestationTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockWithdrawals(ctx context.Context, chainId, block uint64) ([]t.BlockWithdrawalTableRow, error) { + r := []t.BlockWithdrawalTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockBlsChanges(ctx context.Context, chainId, block uint64) ([]t.BlockBlsChangeTableRow, error) { + r := []t.BlockBlsChangeTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockVoluntaryExits(ctx context.Context, chainId, block uint64) ([]t.BlockVoluntaryExitTableRow, error) { + r := []t.BlockVoluntaryExitTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetBlockBlobs(ctx context.Context, chainId, block uint64) ([]t.BlockBlobTableRow, error) { + r := []t.BlockBlobTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlot(ctx context.Context, chainId, block uint64) (*t.BlockSummary, error) { + r := t.BlockSummary{} + err := commonFakeData(&r) + return &r, err +} + +func (d *DummyService) GetSlotOverview(ctx context.Context, chainId, block uint64) (*t.BlockOverview, error) { + r := t.BlockOverview{} + err := commonFakeData(&r) + return &r, err +} + +func (d *DummyService) GetSlotTransactions(ctx context.Context, chainId, block uint64) ([]t.BlockTransactionTableRow, error) { + r := []t.BlockTransactionTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlotVotes(ctx context.Context, chainId, block uint64) ([]t.BlockVoteTableRow, error) { + r := []t.BlockVoteTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlotAttestations(ctx context.Context, chainId, block uint64) ([]t.BlockAttestationTableRow, error) { + r := []t.BlockAttestationTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlotWithdrawals(ctx context.Context, chainId, block uint64) ([]t.BlockWithdrawalTableRow, error) { + r := []t.BlockWithdrawalTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlotBlsChanges(ctx context.Context, chainId, block uint64) ([]t.BlockBlsChangeTableRow, error) { + r := []t.BlockBlsChangeTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlotVoluntaryExits(ctx context.Context, chainId, block uint64) ([]t.BlockVoluntaryExitTableRow, error) { + r := []t.BlockVoluntaryExitTableRow{} + err := commonFakeData(&r) + return r, err +} + +func (d *DummyService) GetSlotBlobs(ctx context.Context, chainId, block uint64) ([]t.BlockBlobTableRow, error) { + r := []t.BlockBlobTableRow{} + err := commonFakeData(&r) + return r, err +} diff --git a/backend/pkg/api/data_access/header.go b/backend/pkg/api/data_access/header.go index 928e96467..9363483bb 100644 --- a/backend/pkg/api/data_access/header.go +++ b/backend/pkg/api/data_access/header.go @@ -11,6 +11,16 @@ func (d *DataAccessService) GetLatestSlot() (uint64, error) { return latestSlot, nil } +func (d *DataAccessService) GetLatestBlock() (uint64, error) { + // @DATA-ACCESS implement + return d.dummy.GetLatestBlock() +} + +func (d *DataAccessService) GetBlockHeightAt(slot uint64) (uint64, error) { + // @DATA-ACCESS implement; return error if no block at slot + return d.dummy.GetBlockHeightAt(slot) +} + func (d *DataAccessService) GetLatestExchangeRates() ([]t.EthConversionRate, error) { result := []t.EthConversionRate{} diff --git a/backend/pkg/api/handlers/common.go b/backend/pkg/api/handlers/common.go index 58ae9ecf6..647d48c89 100644 --- a/backend/pkg/api/handlers/common.go +++ b/backend/pkg/api/handlers/common.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/gobitfly/beaconchain/pkg/commons/log" + "github.com/gorilla/mux" "github.com/invopop/jsonschema" "github.com/xeipuuv/gojsonschema" @@ -359,6 +360,32 @@ func (h *HandlerService) getDashboardPremiumPerks(ctx context.Context, id types. return &userInfo.PremiumPerks, nil } +// helper function to unify handling of block detail request validation +func (h *HandlerService) validateBlockRequest(r *http.Request, paramName string) (uint64, uint64, error) { + var v validationError + var err error + chainId := v.checkNetworkParameter(mux.Vars(r)["network"]) + var value uint64 + switch paramValue := mux.Vars(r)[paramName]; paramValue { + // possibly add other values like "genesis", "finalized", hardforks etc. later + case "latest": + if paramName == "block" { + value, err = h.dai.GetLatestBlock() + } else if paramName == "slot" { + value, err = h.dai.GetLatestSlot() + } + if err != nil { + return 0, 0, err + } + default: + value = v.checkUint(paramValue, paramName) + } + if v.hasErrors() { + return 0, 0, v + } + return chainId, value, nil +} + // checkGroupId validates the given group id and returns it as an int64. // If the given group id is empty and allowEmpty is true, it returns -1 (all groups). func (v *validationError) checkGroupId(param string, allowEmpty bool) int64 { @@ -420,6 +447,10 @@ func (v *validationError) checkAddress(publicId string) string { return v.checkRegex(reEthereumAddress, publicId, "address") } +func (v *validationError) checkUintMinMax(param string, min uint64, max uint64, paramName string) uint64 { + return checkMinMax(v, v.checkUint(param, paramName), min, max, paramName) +} + func (v *validationError) checkPagingParams(q url.Values) Paging { paging := Paging{ cursor: q.Get("cursor"), @@ -428,13 +459,7 @@ func (v *validationError) checkPagingParams(q url.Values) Paging { } if limitStr := q.Get("limit"); limitStr != "" { - limit, err := strconv.ParseUint(limitStr, 10, 64) - if err != nil { - v.add("limit", fmt.Sprintf("given value '%s' is not a valid positive integer", limitStr)) - return paging - } - checkMinMax(v, limit, 1, maxQueryLimit, "limit") - paging.limit = limit + paging.limit = v.checkUintMinMax(limitStr, 1, maxQueryLimit, "limit") } if paging.cursor != "" { diff --git a/backend/pkg/api/handlers/internal.go b/backend/pkg/api/handlers/internal.go index c86a56bac..65a2a966c 100644 --- a/backend/pkg/api/handlers/internal.go +++ b/backend/pkg/api/handlers/internal.go @@ -2006,3 +2006,349 @@ func (h *HandlerService) InternalPostUserNotificationsTestWebhook(w http.Respons // TODO returnOk(w, nil) } + +// -------------------------------------- +// Blocks + +func (h *HandlerService) InternalGetBlock(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlock(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockResponse{ + Data: *data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockOverview(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockOverview(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + response := types.InternalGetBlockOverviewResponse{ + Data: *data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockTransactions(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockTransactions(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockTransactionsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockVotes(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockVotes(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockVotesResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockAttestations(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockAttestations(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockAttestationsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockWithdrawals(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockWithdrawals(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockWtihdrawalsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockBlsChanges(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockBlsChanges(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockBlsChangesResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockVoluntaryExits(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockVoluntaryExits(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockVoluntaryExitsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetBlockBlobs(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "block") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetBlockBlobs(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockBlobsResponse{ + Data: data, + } + returnOk(w, response) +} + +// -------------------------------------- +// Slots + +func (h *HandlerService) InternalGetSlot(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlot(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockResponse{ + Data: *data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotOverview(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotOverview(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + response := types.InternalGetBlockOverviewResponse{ + Data: *data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotTransactions(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotTransactions(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockTransactionsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotVotes(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotVotes(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockVotesResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotAttestations(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotAttestations(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockAttestationsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotWithdrawals(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotWithdrawals(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockWtihdrawalsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotBlsChanges(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotBlsChanges(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockBlsChangesResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotVoluntaryExits(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotVoluntaryExits(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockVoluntaryExitsResponse{ + Data: data, + } + returnOk(w, response) +} + +func (h *HandlerService) InternalGetSlotBlobs(w http.ResponseWriter, r *http.Request) { + chainId, block, err := h.validateBlockRequest(r, "slot") + if err != nil { + handleErr(w, err) + return + } + + data, err := h.dai.GetSlotBlobs(r.Context(), chainId, block) + if err != nil { + handleErr(w, err) + return + } + + response := types.InternalGetBlockBlobsResponse{ + Data: data, + } + returnOk(w, response) +} diff --git a/backend/pkg/api/handlers/public.go b/backend/pkg/api/handlers/public.go index 6f8ba6c43..12adf32dc 100644 --- a/backend/pkg/api/handlers/public.go +++ b/backend/pkg/api/handlers/public.go @@ -463,10 +463,18 @@ func (h *HandlerService) PublicGetNetworkSlotAttestations(w http.ResponseWriter, returnOk(w, nil) } +func (h *HandlerService) PublicGetNetworkSlotVotes(w http.ResponseWriter, r *http.Request) { + returnOk(w, nil) +} + func (h *HandlerService) PublicGetNetworkBlockAttestations(w http.ResponseWriter, r *http.Request) { returnOk(w, nil) } +func (h *HandlerService) PublicGetNetworkBlockVotes(w http.ResponseWriter, r *http.Request) { + returnOk(w, nil) +} + func (h *HandlerService) PublicGetNetworkAggregatedAttestations(w http.ResponseWriter, r *http.Request) { returnOk(w, nil) } diff --git a/backend/pkg/api/router.go b/backend/pkg/api/router.go index 4705698a0..80994142b 100644 --- a/backend/pkg/api/router.go +++ b/backend/pkg/api/router.go @@ -138,9 +138,13 @@ func addRoutes(hs *handlers.HandlerService, publicRouter, internalRouter *mux.Ro {http.MethodGet, "/networks/{network}/epochs/{epoch}", hs.PublicGetNetworkEpoch, nil}, {http.MethodGet, "/networks/{network}/blocks", hs.PublicGetNetworkBlocks, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}", hs.PublicGetNetworkBlock, nil}, + {http.MethodGet, "/networks/{network}/blocks/{block}", nil, hs.InternalGetBlock}, + {http.MethodGet, "/networks/{network}/blocks/{block}/overview", hs.PublicGetNetworkBlock, hs.InternalGetBlockOverview}, + {http.MethodGet, "/networks/{network}/blocks/{block}/votes", hs.PublicGetNetworkBlockVotes, hs.InternalGetBlockVotes}, {http.MethodGet, "/networks/{network}/slots", hs.PublicGetNetworkSlots, nil}, - {http.MethodGet, "/networks/{network}/slots/{slot}", hs.PublicGetNetworkSlot, nil}, + {http.MethodGet, "/networks/{network}/slots/{slot}", nil, hs.InternalGetSlot}, + {http.MethodGet, "/networks/{network}/slots/{slot}/overview", hs.PublicGetNetworkSlot, hs.InternalGetSlotOverview}, + {http.MethodGet, "/networks/{network}/slots/{slot}/votes", hs.PublicGetNetworkSlotVotes, hs.InternalGetSlotVotes}, {http.MethodGet, "/networks/{network}/validators/{validator}/blocks", hs.PublicGetNetworkValidatorBlocks, nil}, {http.MethodGet, "/networks/{network}/addresses/{address}/priority-fee-blocks", hs.PublicGetNetworkAddressPriorityFeeBlocks, nil}, {http.MethodGet, "/networks/{network}/addresses/{address}/proposer-reward-blocks", hs.PublicGetNetworkAddressProposerRewardBlocks, nil}, @@ -151,8 +155,8 @@ func addRoutes(hs *handlers.HandlerService, publicRouter, internalRouter *mux.Ro {http.MethodGet, "/networks/{network}/validators/{validator}/attestations", hs.PublicGetNetworkValidatorAttestations, nil}, {http.MethodGet, "/networks/{network}/epochs/{epoch}/attestations", hs.PublicGetNetworkEpochAttestations, nil}, - {http.MethodGet, "/networks/{network}/slots/{slot}/attestations", hs.PublicGetNetworkSlotAttestations, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}/attestations", hs.PublicGetNetworkBlockAttestations, nil}, + {http.MethodGet, "/networks/{network}/slots/{slot}/attestations", hs.PublicGetNetworkSlotAttestations, hs.InternalGetSlotAttestations}, + {http.MethodGet, "/networks/{network}/blocks/{block}/attestations", hs.PublicGetNetworkBlockAttestations, hs.InternalGetBlockAttestations}, {http.MethodGet, "/networks/{network}/aggregated-attestations", hs.PublicGetNetworkAggregatedAttestations, nil}, {http.MethodGet, "/networks/{network}/ethstore/{day}", hs.PublicGetNetworkEthStore, nil}, @@ -168,15 +172,15 @@ func addRoutes(hs *handlers.HandlerService, publicRouter, internalRouter *mux.Ro {http.MethodGet, "/networks/{network}/transactions/{hash}/deposits", hs.PublicGetNetworkTransactionDeposits, nil}, {http.MethodGet, "/networks/{network}/withdrawals", hs.PublicGetNetworkWithdrawals, nil}, - {http.MethodGet, "/networks/{network}/slots/{slot}/withdrawals", hs.PublicGetNetworkSlotWithdrawals, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}/withdrawals", hs.PublicGetNetworkBlockWithdrawals, nil}, + {http.MethodGet, "/networks/{network}/slots/{slot}/withdrawals", hs.PublicGetNetworkSlotWithdrawals, hs.InternalGetSlotWithdrawals}, + {http.MethodGet, "/networks/{network}/blocks/{block}/withdrawals", hs.PublicGetNetworkBlockWithdrawals, hs.InternalGetBlockWithdrawals}, {http.MethodGet, "/networks/{network}/validators/{validator}/withdrawals", hs.PublicGetNetworkValidatorWithdrawals, nil}, {http.MethodGet, "/networks/{network}/withdrawal-credentials/{credential}/withdrawals", hs.PublicGetNetworkWithdrawalCredentialWithdrawals, nil}, {http.MethodGet, "/networks/{network}/voluntary-exits", hs.PublicGetNetworkVoluntaryExits, nil}, {http.MethodGet, "/networks/{network}/epochs/{epoch}/voluntary-exits", hs.PublicGetNetworkEpochVoluntaryExits, nil}, - {http.MethodGet, "/networks/{network}/slots/{slot}/voluntary-exits", hs.PublicGetNetworkSlotVoluntaryExits, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}/voluntary-exits", hs.PublicGetNetworkBlockVoluntaryExits, nil}, + {http.MethodGet, "/networks/{network}/slots/{slot}/voluntary-exits", hs.PublicGetNetworkSlotVoluntaryExits, hs.InternalGetSlotVoluntaryExits}, + {http.MethodGet, "/networks/{network}/blocks/{block}/voluntary-exits", hs.PublicGetNetworkBlockVoluntaryExits, hs.InternalGetBlockVoluntaryExits}, {http.MethodGet, "/networks/{network}/addresses/{address}/balance-history", hs.PublicGetNetworkAddressBalanceHistory, nil}, {http.MethodGet, "/networks/{network}/addresses/{address}/token-supply-history", hs.PublicGetNetworkAddressTokenSupplyHistory, nil}, @@ -185,14 +189,14 @@ func addRoutes(hs *handlers.HandlerService, publicRouter, internalRouter *mux.Ro {http.MethodGet, "/networks/{network}/transactions", hs.PublicGetNetworkTransactions, nil}, {http.MethodGet, "/networks/{network}/transactions/{hash}", hs.PublicGetNetworkTransaction, nil}, {http.MethodGet, "/networks/{network}/addresses/{address}/transactions", hs.PublicGetNetworkAddressTransactions, nil}, - {http.MethodGet, "/networks/{network}/slots/{slot}/transactions", hs.PublicGetNetworkSlotTransactions, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}/transactions", hs.PublicGetNetworkBlockTransactions, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}/blobs", hs.PublicGetNetworkBlockBlobs, nil}, + {http.MethodGet, "/networks/{network}/slots/{slot}/transactions", hs.PublicGetNetworkSlotTransactions, hs.InternalGetSlotTransactions}, + {http.MethodGet, "/networks/{network}/blocks/{block}/transactions", hs.PublicGetNetworkBlockTransactions, hs.InternalGetBlockTransactions}, + {http.MethodGet, "/networks/{network}/blocks/{block}/blobs", hs.PublicGetNetworkBlockBlobs, hs.InternalGetBlockBlobs}, {http.MethodGet, "/networks/{network}/handlerService-changes", hs.PublicGetNetworkBlsChanges, nil}, {http.MethodGet, "/networks/{network}/epochs/{epoch}/handlerService-changes", hs.PublicGetNetworkEpochBlsChanges, nil}, - {http.MethodGet, "/networks/{network}/slots/{slot}/handlerService-changes", hs.PublicGetNetworkSlotBlsChanges, nil}, - {http.MethodGet, "/networks/{network}/blocks/{block}/handlerService-changes", hs.PublicGetNetworkBlockBlsChanges, nil}, + {http.MethodGet, "/networks/{network}/slots/{slot}/handlerService-changes", hs.PublicGetNetworkSlotBlsChanges, hs.InternalGetSlotBlsChanges}, + {http.MethodGet, "/networks/{network}/blocks/{block}/handlerService-changes", hs.PublicGetNetworkBlockBlsChanges, hs.InternalGetBlockBlsChanges}, {http.MethodGet, "/networks/{network}/validators/{validator}/handlerService-changes", hs.PublicGetNetworkValidatorBlsChanges, nil}, {http.MethodGet, "/networks/ethereum/addresses/{address}/ens", hs.PublicGetNetworkAddressEns, nil}, diff --git a/backend/pkg/api/types/block.go b/backend/pkg/api/types/block.go new file mode 100644 index 000000000..3f3bae56c --- /dev/null +++ b/backend/pkg/api/types/block.go @@ -0,0 +1,201 @@ +package types + +import ( + "github.com/shopspring/decimal" +) + +// count indicator per block details tab; each tab is only present if count > 0 +type BlockSummary struct { + Transactions uint64 `json:"transactions"` + Votes uint64 `json:"votes"` + Attestations uint64 `json:"attestations"` + Withdrawals uint64 `json:"withdrawals"` + BlsChanges uint64 `json:"bls_changes"` + VoluntaryExits uint64 `json:"voluntary_exits"` + Blobs uint64 `json:"blobs"` +} + +type InternalGetBlockResponse ApiDataResponse[BlockSummary] + +type BlockExecutionPayload struct { + BlockHash Hash `json:"block_hash"` + ParentHash Hash `json:"parent_hash"` + PriorityFeesRecipient Address `json:"priority_fees_recipient"` + GasUsed uint64 `json:"gas_used"` + GasLimit uint64 `json:"gas_limit"` + BaseFeePerGas decimal.Decimal `json:"base_fee_per_gas"` + BaseFees decimal.Decimal `json:"base_fees"` +} + +type BlockConsensusLayer struct { + StateRoot Hash `json:"state_root"` + Signature Hash `json:"signature"` + RandaoReveal Hash `json:"randao_reveal"` + Attestations uint64 `json:"attestations"` + Votes uint64 `json:"votes"` + VotingValidators uint64 `json:"voting_validators"` + VoluntaryExits uint64 `json:"voluntary_exits"` + AttesterSlashings uint64 `json:"attester_slashings"` + ProposerSlashings uint64 `json:"proposer_slashings"` + Deposits uint64 `json:"deposit"` + SyncCommittee BlockSyncCommittee `json:"sync_committee"` + Eth1Data BlockEth1Data `json:"eth1_data"` + Graffiti string `json:"graffiti"` +} + +type BlockSyncCommittee struct { + Participation float64 `json:"participation"` + Bits []bool `json:"bits"` + SyncCommittee []uint64 `json:"sync_committee"` + Signature Hash `json:"signature"` +} + +type BlockEth1Data struct { + BlockHash Hash `json:"block_hash"` + DepositCount uint64 `json:"deposit_count"` + DepositRoot Hash `json:"deposit_root"` +} + +type BlobInfo struct { + Count uint64 `json:"name"` + TxCount uint64 `json:"tx_count"` + GasUsed uint64 `json:"gas_used"` + GasPrice *decimal.Decimal `json:"gas_price"` + ExcessGas uint64 `json:"excess_gas"` + BurnedFees *decimal.Decimal `json:"burned_fees"` +} + +type BlockOverview struct { + // General + Block uint64 `json:"block"` + Time int64 `json:"time"` + + // Old blocks only + Miner *Address `json:"miner,omitempty"` + Rewards *decimal.Decimal `json:"rewards,omitempty"` + TxFees *decimal.Decimal `json:"tx_fees,omitempty"` + GasUsage *decimal.Decimal `json:"gas_usage,omitempty"` + GasLimit *struct { + Value uint64 `json:"value"` + Percent float64 `json:"percent"` + } `json:"gas_limit,omitempty"` + LowestGasPrice *decimal.Decimal `json:"lowest_gas_price,omitempty"` + Difficulty *decimal.Decimal `json:"difficulty,omitempty"` + // base + burned fee only present post EIP-1559 + BaseFee *decimal.Decimal `json:"base_fee,omitempty"` + BurnedFees *decimal.Decimal `json:"burned_fees,omitempty"` + Extra string `json:"extra,omitempty"` + Hash Hash `json:"hash,omitempty"` + ParentHash Hash `json:"parent_hash,omitempty"` + + // New blocks only + MevTags []struct { + Name string `json:"name"` + Color string `json:"color"` + } `json:"mev_tags,omitempty"` + Epoch uint64 `json:"epoch,omitempty"` + Slot uint64 `json:"slot,omitempty"` + Proposer uint64 `json:"proposer,omitempty"` + ProposerReward *ClElValue[decimal.Decimal] `json:"proposer_reward,omitempty"` + ProposerRewardRecipient *Address `json:"proposer_reward_recipient,omitempty"` + Status *struct { + Proposal string `json:"proposal" tstype:"'proposed' | 'orphaned' | 'missed' | 'scheduled'" faker:"oneof: proposed, orphaned, missed, scheduled"` + Finalized string `json:"finalized" tstype:"'finalized' | 'justified' | 'not_finalized'" faker:"oneof: finalized, justified, not_finalized"` + } `json:"status,omitempty"` + PriorityFees *decimal.Decimal `json:"priority_fees,omitempty"` + Transactions *struct { + General uint64 `json:"general"` + Internal uint64 `json:"internal"` + Blob uint64 `json:"blob,omitempty"` + } `json:"transactions,omitempty"` + BlockRoot Hash `json:"block_root,omitempty"` + ParentRoot Hash `json:"parent_root,omitempty"` + + ExecutionPayload *BlockExecutionPayload `json:"execution_payload,omitempty"` + ConsensusLayer *BlockConsensusLayer `json:"consensus_layer,omitempty"` +} + +type InternalGetBlockOverviewResponse ApiDataResponse[BlockOverview] + +type BlockTransactionTableRow struct { + Success bool `json:"success"` + TxHash Hash `json:"tx_hash"` + Method string `json:"method"` + Block uint64 `json:"block"` + Age uint64 `json:"age"` + From Address `json:"from"` + Type string `json:"type" tstype:"'out' | 'in' | 'out_in' | 'self' | 'contract'" faker:"oneof: out, int, out_in, self, contract"` + To Address `json:"to"` + Value decimal.Decimal `json:"value"` + GasPrice decimal.Decimal `json:"gas_price"` + TxFee decimal.Decimal `json:"tx_fee"` +} + +type InternalGetBlockTransactionsResponse ApiDataResponse[[]BlockTransactionTableRow] + +type BlockVoteTableRow struct { + AllocatedSlot uint64 `json:"allocated_slot"` + Committee uint64 `json:"committee"` + IncludedInBlock uint64 `json:"included_in_block"` + Validators []uint64 `json:"validators"` +} + +type InternalGetBlockVotesResponse ApiDataResponse[[]BlockVoteTableRow] + +type EpochInfo struct { + Epoch uint64 `json:"epoch"` + BlockRoot Hash `json:"block_root"` +} + +type BlockAttestationTableRow struct { + Slot uint64 `json:"slot"` + CommitteeIndex uint64 `json:"committee_index"` + AggregationBits []bool `json:"aggregation_bits"` + Validators []uint64 `json:"validators"` + BeaconBlockRoot Hash `json:"beacon_block_root"` + Source EpochInfo `json:"source"` + Target EpochInfo `json:"target"` + Signature Hash `json:"signature"` +} + +type InternalGetBlockAttestationsResponse ApiDataResponse[[]BlockAttestationTableRow] + +type BlockWithdrawalTableRow struct { + // no design present yet, TODO confirm this + Index uint64 `json:"index"` + Epoch uint64 `json:"epoch"` + Slot uint64 `json:"slot"` + Age uint64 `json:"age"` + Recipient Address `json:"recipient"` + Amount decimal.Decimal `json:"amount"` +} + +type InternalGetBlockWtihdrawalsResponse ApiDataResponse[[]BlockWithdrawalTableRow] + +type BlockBlsChangeTableRow struct { + Index uint64 `json:"index"` + Signature Hash `json:"signature"` + BlsPubkey Hash `json:"bls_pubkey"` + NewWithdrawalAddress Address `json:"new_withdrawal_address"` +} + +type InternalGetBlockBlsChangesResponse ApiDataResponse[[]BlockBlsChangeTableRow] + +type BlockVoluntaryExitTableRow struct { + Validator uint64 `json:"validator"` + Signature Hash `json:"signature"` +} + +type InternalGetBlockVoluntaryExitsResponse ApiDataResponse[[]BlockVoluntaryExitTableRow] + +type BlockBlobTableRow struct { + VersionedHash Hash `json:"versioned_hash"` + Commitment Hash `json:"commitment"` + Proof Hash `json:"proof"` + Size uint64 `json:"size"` + TransactionHash Hash `json:"transaction_hash"` + Block uint64 `json:"block"` + Data string `json:"data"` +} + +type InternalGetBlockBlobsResponse ApiDataResponse[[]BlockBlobTableRow] diff --git a/backend/pkg/api/types/common.go b/backend/pkg/api/types/common.go index bef879a03..e658afa80 100644 --- a/backend/pkg/api/types/common.go +++ b/backend/pkg/api/types/common.go @@ -37,6 +37,7 @@ type Address struct { Hash Hash `json:"hash"` Ens string `json:"ens,omitempty"` } + type LuckItem struct { Percent float64 `json:"percent"` Expected time.Time `json:"expected"` diff --git a/frontend/types/api/block.ts b/frontend/types/api/block.ts new file mode 100644 index 000000000..56b0ff31b --- /dev/null +++ b/frontend/types/api/block.ts @@ -0,0 +1,188 @@ +// Code generated by tygo. DO NOT EDIT. +/* eslint-disable */ +import type { ApiDataResponse, Hash, Address, ClElValue } from './common' + +////////// +// source: block.go + +/** + * count indicator per block details tab; each tab is only present if count > 0 + */ +export interface BlockSummary { + transactions: number /* uint64 */; + votes: number /* uint64 */; + attestations: number /* uint64 */; + withdrawals: number /* uint64 */; + bls_changes: number /* uint64 */; + voluntary_exits: number /* uint64 */; + blobs: number /* uint64 */; +} +export type InternalGetBlockResponse = ApiDataResponse; +export interface BlockExecutionPayload { + block_hash: Hash; + parent_hash: Hash; + priority_fees_recipient: Address; + gas_used: number /* uint64 */; + gas_limit: number /* uint64 */; + base_fee_per_gas: string /* decimal.Decimal */; + base_fees: string /* decimal.Decimal */; +} +export interface BlockConsensusLayer { + state_root: Hash; + signature: Hash; + randao_reveal: Hash; + attestations: number /* uint64 */; + votes: number /* uint64 */; + voting_validators: number /* uint64 */; + voluntary_exits: number /* uint64 */; + attester_slashings: number /* uint64 */; + proposer_slashings: number /* uint64 */; + deposit: number /* uint64 */; + sync_committee: BlockSyncCommittee; + eth1_data: BlockEth1Data; + graffiti: string; +} +export interface BlockSyncCommittee { + participation: number /* float64 */; + bits: boolean[]; + sync_committee: number /* uint64 */[]; + signature: Hash; +} +export interface BlockEth1Data { + block_hash: Hash; + deposit_count: number /* uint64 */; + deposit_root: Hash; +} +export interface BlobInfo { + name: number /* uint64 */; + tx_count: number /* uint64 */; + gas_used: number /* uint64 */; + gas_price?: string /* decimal.Decimal */; + excess_gas: number /* uint64 */; + burned_fees?: string /* decimal.Decimal */; +} +export interface BlockOverview { + /** + * General + */ + block: number /* uint64 */; + time: number /* int64 */; + /** + * Old blocks only + */ + miner?: Address; + rewards?: string /* decimal.Decimal */; + tx_fees?: string /* decimal.Decimal */; + gas_usage?: string /* decimal.Decimal */; + gas_limit?: { + value: number /* uint64 */; + percent: number /* float64 */; + }; + lowest_gas_price?: string /* decimal.Decimal */; + difficulty?: string /* decimal.Decimal */; + /** + * base + burned fee only present post EIP-1559 + */ + base_fee?: string /* decimal.Decimal */; + burned_fees?: string /* decimal.Decimal */; + extra?: string; + hash?: Hash; + parent_hash?: Hash; + /** + * New blocks only + */ + mev_tags?: { + name: string; + color: string; + }[]; + epoch?: number /* uint64 */; + slot?: number /* uint64 */; + proposer?: number /* uint64 */; + proposer_reward?: ClElValue; + proposer_reward_recipient?: Address; + status?: { + proposal: 'proposed' | 'orphaned' | 'missed' | 'scheduled'; + finalized: 'finalized' | 'justified' | 'not_finalized'; + }; + priority_fees?: string /* decimal.Decimal */; + transactions?: { + general: number /* uint64 */; + internal: number /* uint64 */; + blob?: number /* uint64 */; + }; + block_root?: Hash; + parent_root?: Hash; + execution_payload?: BlockExecutionPayload; + consensus_layer?: BlockConsensusLayer; +} +export type InternalGetBlockOverviewResponse = ApiDataResponse; +export interface BlockTransactionTableRow { + success: boolean; + tx_hash: Hash; + method: string; + block: number /* uint64 */; + age: number /* uint64 */; + from: Address; + type: 'out' | 'in' | 'out_in' | 'self' | 'contract'; + to: Address; + value: string /* decimal.Decimal */; + gas_price: string /* decimal.Decimal */; + tx_fee: string /* decimal.Decimal */; +} +export type InternalGetBlockTransactionsResponse = ApiDataResponse; +export interface BlockVoteTableRow { + allocated_slot: number /* uint64 */; + committee: number /* uint64 */; + included_in_block: number /* uint64 */; + validators: number /* uint64 */[]; +} +export type InternalGetBlockVotesResponse = ApiDataResponse; +export interface EpochInfo { + epoch: number /* uint64 */; + block_root: Hash; +} +export interface BlockAttestationTableRow { + slot: number /* uint64 */; + committee_index: number /* uint64 */; + aggregation_bits: boolean[]; + validators: number /* uint64 */[]; + beacon_block_root: Hash; + source: EpochInfo; + target: EpochInfo; + signature: Hash; +} +export type InternalGetBlockAttestationsResponse = ApiDataResponse; +export interface BlockWithdrawalTableRow { + /** + * no design present yet, TODO confirm this + */ + index: number /* uint64 */; + epoch: number /* uint64 */; + slot: number /* uint64 */; + age: number /* uint64 */; + recipient: Address; + amount: string /* decimal.Decimal */; +} +export type InternalGetBlockWtihdrawalsResponse = ApiDataResponse; +export interface BlockBlsChangeTableRow { + index: number /* uint64 */; + signature: Hash; + bls_pubkey: Hash; + new_withdrawal_address: Address; +} +export type InternalGetBlockBlsChangesResponse = ApiDataResponse; +export interface BlockVoluntaryExitTableRow { + validator: number /* uint64 */; + signature: Hash; +} +export type InternalGetBlockVoluntaryExitsResponse = ApiDataResponse; +export interface BlockBlobTableRow { + versioned_hash: Hash; + commitment: Hash; + proof: Hash; + size: number /* uint64 */; + transaction_hash: Hash; + block: number /* uint64 */; + data: string; +} +export type InternalGetBlockBlobsResponse = ApiDataResponse;