Skip to content

Commit

Permalink
Merge pull request #237 from ethpandaops/pk910/download-blocks
Browse files Browse the repository at this point in the history
allow downloading block headers & bodies as json or ssz
  • Loading branch information
pk910 authored Feb 10, 2025
2 parents 25a7559 + 9d6c005 commit 455fe5d
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 14 deletions.
89 changes: 83 additions & 6 deletions handlers/slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,21 @@ func Slot(w http.ResponseWriter, r *http.Request) {
}
}

urlArgs := r.URL.Query()
if pageError := services.GlobalCallRateLimiter.CheckCallLimit(r, 1); pageError != nil {
handlePageError(w, r, pageError)
return
}

var pageData *models.SlotPageData
var pageError error
pageError = services.GlobalCallRateLimiter.CheckCallLimit(r, 1)
if pageError == nil {
pageData, pageError = getSlotPageData(blockSlot, blockRootHash)
urlArgs := r.URL.Query()
if urlArgs.Has("download") {
if err := handleSlotDownload(r.Context(), w, blockSlot, blockRootHash, urlArgs.Get("download")); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
return
}

pageData, pageError := getSlotPageData(blockSlot, blockRootHash)
if pageError != nil {
handlePageError(w, r, pageError)
return
Expand Down Expand Up @@ -885,3 +892,73 @@ func getSlotPageConsolidationRequests(pageData *models.SlotPageBlockData, consol

pageData.ConsolidationRequestsCount = uint64(len(pageData.ConsolidationRequests))
}

func handleSlotDownload(ctx context.Context, w http.ResponseWriter, blockSlot int64, blockRoot []byte, downloadType string) error {
chainState := services.GlobalBeaconService.GetChainState()
currentSlot := chainState.CurrentSlot()
var blockData *services.CombinedBlockResponse
var err error
if blockSlot > -1 {
if phase0.Slot(blockSlot) <= currentSlot {
blockData, err = services.GlobalBeaconService.GetSlotDetailsBySlot(ctx, phase0.Slot(blockSlot))
}
} else {
blockData, err = services.GlobalBeaconService.GetSlotDetailsByBlockroot(ctx, phase0.Root(blockRoot))
}

if err != nil {
return fmt.Errorf("error getting block data: %v", err)
}

if blockData == nil || blockData.Block == nil {
return fmt.Errorf("block not found")
}

switch downloadType {
case "block-ssz":
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=block-%d-%x.ssz", blockData.Header.Message.Slot, blockData.Root[:]))

dynSsz := services.GlobalBeaconService.GetBeaconIndexer().GetDynSSZ()
_, blockSSZ, err := beacon.MarshalVersionedSignedBeaconBlockSSZ(dynSsz, blockData.Block, false, true)
if err != nil {
return fmt.Errorf("error serializing block: %v", err)
}
w.Write(blockSSZ)
return nil

case "block-json":
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=block-%d-%x.json", blockData.Header.Message.Slot, blockData.Root[:]))

_, jsonRes, err := beacon.MarshalVersionedSignedBeaconBlockJson(blockData.Block)
if err != nil {
return fmt.Errorf("error serializing block: %v", err)
}
w.Write(jsonRes)
return nil

case "header-ssz":
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=header-%d-%x.ssz", blockData.Header.Message.Slot, blockData.Root[:]))
headerSSZ, err := blockData.Header.MarshalSSZ()
if err != nil {
return fmt.Errorf("error serializing header: %v", err)
}
w.Write(headerSSZ)
return nil

case "header-json":
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=header-%d-%x.json", blockData.Header.Message.Slot, blockData.Root[:]))
jsonRes, err := blockData.Header.MarshalJSON()
if err != nil {
return fmt.Errorf("error serializing header: %v", err)
}
w.Write(jsonRes)
return nil

default:
return fmt.Errorf("unknown download type: %s", downloadType)
}
}
4 changes: 2 additions & 2 deletions indexer/beacon/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func (block *Block) buildUnfinalizedBlock(compress bool) (*dbtypes.UnfinalizedBl
return nil, fmt.Errorf("marshal header ssz failed: %v", err)
}

blockVer, blockSSZ, err := marshalVersionedSignedBeaconBlockSSZ(block.dynSsz, block.GetBlock(), compress)
blockVer, blockSSZ, err := MarshalVersionedSignedBeaconBlockSSZ(block.dynSsz, block.GetBlock(), compress, false)
if err != nil {
return nil, fmt.Errorf("marshal block ssz failed: %v", err)
}
Expand All @@ -290,7 +290,7 @@ func (block *Block) buildOrphanedBlock(compress bool) (*dbtypes.OrphanedBlock, e
return nil, fmt.Errorf("marshal header ssz failed: %v", err)
}

blockVer, blockSSZ, err := marshalVersionedSignedBeaconBlockSSZ(block.dynSsz, block.GetBlock(), compress)
blockVer, blockSSZ, err := MarshalVersionedSignedBeaconBlockSSZ(block.dynSsz, block.GetBlock(), compress, false)
if err != nil {
return nil, fmt.Errorf("marshal block ssz failed: %v", err)
}
Expand Down
12 changes: 6 additions & 6 deletions indexer/beacon/block_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import (
var jsonVersionFlag uint64 = 0x40000000
var compressionFlag uint64 = 0x20000000

// marshalVersionedSignedBeaconBlockSSZ marshals a versioned signed beacon block using SSZ encoding.
func marshalVersionedSignedBeaconBlockSSZ(dynSsz *dynssz.DynSsz, block *spec.VersionedSignedBeaconBlock, compress bool) (version uint64, ssz []byte, err error) {
if utils.Config.KillSwitch.DisableSSZEncoding {
// MarshalVersionedSignedBeaconBlockSSZ marshals a versioned signed beacon block using SSZ encoding.
func MarshalVersionedSignedBeaconBlockSSZ(dynSsz *dynssz.DynSsz, block *spec.VersionedSignedBeaconBlock, compress bool, forceSSZ bool) (version uint64, ssz []byte, err error) {
if utils.Config.KillSwitch.DisableSSZEncoding && !forceSSZ {
// SSZ encoding disabled, use json instead
version, ssz, err = marshalVersionedSignedBeaconBlockJson(block)
version, ssz, err = MarshalVersionedSignedBeaconBlockJson(block)
} else {
// SSZ encoding
switch block.Version {
Expand Down Expand Up @@ -116,8 +116,8 @@ func unmarshalVersionedSignedBeaconBlockSSZ(dynSsz *dynssz.DynSsz, version uint6
return block, nil
}

// marshalVersionedSignedBeaconBlockJson marshals a versioned signed beacon block using JSON encoding.
func marshalVersionedSignedBeaconBlockJson(block *spec.VersionedSignedBeaconBlock) (version uint64, jsonRes []byte, err error) {
// MarshalVersionedSignedBeaconBlockJson marshals a versioned signed beacon block using JSON encoding.
func MarshalVersionedSignedBeaconBlockJson(block *spec.VersionedSignedBeaconBlock) (version uint64, jsonRes []byte, err error) {
switch block.Version {
case spec.DataVersionPhase0:
version = uint64(block.Version)
Expand Down
6 changes: 6 additions & 0 deletions indexer/beacon/indexer_getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ import (
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/ethpandaops/dora/clients/consensus"
"github.com/ethpandaops/dora/db"
dynssz "github.com/pk910/dynamic-ssz"
)

// GetDynSSZ returns the dynSsz instance used by the indexer.
func (indexer *Indexer) GetDynSSZ() *dynssz.DynSsz {
return indexer.dynSsz
}

// GetAllClients returns a slice of all clients in the indexer.
func (indexer *Indexer) GetAllClients() []*Client {
clients := make([]*Client, len(indexer.clients))
Expand Down
54 changes: 54 additions & 0 deletions templates/slot/slot.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ <h1 class="h4 my-2 mb-md-0 h1-pager">
<a class="nav-link" id="consolidationRequests-tab" data-bs-toggle="tab" href="#consolidationRequests" role="tab" aria-controls="consolidationRequests" aria-selected="false">Consolidation Requests <span class="badge bg-secondary text-white">{{ .Block.ConsolidationRequestsCount }}</span></a>
</li>
{{ end }}
{{ if .Block }}
<li class="nav-item ms-auto">
<a class="nav-link" id="download-tab" data-bs-toggle="tab" href="#download" role="tab" aria-controls="download" aria-selected="false">
<i class="fas fa-download"></i>
</a>
</li>
{{ end }}
{{ end }}
</ul>

Expand Down Expand Up @@ -251,7 +258,54 @@ <h3 class="h5 col-md-12 text-center"><b>Showing {{ .Block.ConsolidationRequestsC
{{ template "block_consolidation_requests" . }}
</div>
{{ end }}
{{ if .Block }}
<div class="tab-pane fade" id="download" role="tabpanel" aria-labelledby="download-tab">
<div class="card block-card">
<div class="card-body">
<h3 class="h5 mb-4">Download Block Data</h3>

<div class="row g-4">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">
<h4 class="h6 mb-0">Signed Beacon Block Header</h4>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="?download=header-ssz" class="btn btn-outline-primary">
<i class="fas fa-file-download me-2"></i>Download as SSZ
</a>
<a href="?download=header-json" class="btn btn-outline-primary">
<i class="fas fa-file-download me-2"></i>Download as JSON
</a>
</div>
</div>
</div>
</div>

<div class="col-md-6">
<div class="card h-100">
<div class="card-header">
<h4 class="h6 mb-0">Signed Beacon Block</h4>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="?download=block-ssz" class="btn btn-outline-primary">
<i class="fas fa-file-download me-2"></i>Download as SSZ
</a>
<a href="?download=block-json" class="btn btn-outline-primary">
<i class="fas fa-file-download me-2"></i>Download as JSON
</a>
</div>
</div>
</div>
</div>

</div>
</div>
</div>
</div>
{{ end }}
{{ end }}
</div>
<script type="text/javascript">
Expand Down

0 comments on commit 455fe5d

Please sign in to comment.