Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions daemon/algod/api/server/v1/handlers/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
errFailedRetrievingNodeStatus = "failed retrieving node status"
errFailedRetrievingAsset = "failed to retrieve asset information"
errFailedParsingRoundNumber = "failed to parse the round number"
errFailedParsingRawOption = "failed to parse the raw option"
errFailedParsingMaxAssetsToList = "failed to parse max assets, must be between %d and %d"
errFailedParsingAssetIdx = "failed to parse asset index"
errFailedToGetAssetCreator = "failed to retrieve asset creator from the ledger"
Expand Down
34 changes: 34 additions & 0 deletions daemon/algod/api/server/v1/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/algorand/go-algorand/ledger"
"github.com/algorand/go-algorand/node"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/rpcs"
)

func nodeStatus(node *node.AlgorandFullNode) (res v1.NodeStatus, err error) {
Expand Down Expand Up @@ -1179,6 +1180,12 @@ func GetBlock(ctx lib.ReqContext, w http.ResponseWriter, r *http.Request) {
// minimum: 0
// required: true
// description: The round from which to fetch block information.
// - name: raw
// in: query
// type: integer
// format: int64
// required: false
// description: Return raw msgpack block bytes
// Responses:
// 200:
// "$ref": '#/responses/BlockResponse'
Expand All @@ -1196,6 +1203,33 @@ func GetBlock(ctx lib.ReqContext, w http.ResponseWriter, r *http.Request) {
return
}

// raw msgpack option:
rawstr := r.FormValue("raw")
if rawstr != "" {
rawint, err := strconv.ParseUint(rawstr, 10, 64)
if err != nil {
lib.ErrorResponse(w, http.StatusBadRequest, err, errFailedParsingRawOption, ctx.Log)
return
}
if rawint != 0 {
blockbytes, err := rpcs.RawBlockBytes(ctx.Node.Ledger(), basics.Round(queryRound))
if err != nil {
lib.ErrorResponse(w, http.StatusInternalServerError, err, errFailedLookingUpLedger, ctx.Log)
return
}
w.Header().Set("Content-Type", rpcs.LedgerResponseContentType)
w.Header().Set("Content-Length", strconv.Itoa(len(blockbytes)))
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
w.WriteHeader(http.StatusOK)
_, err = w.Write(blockbytes)
if err != nil {
ctx.Log.Warnf("algod failed to write an object to the response stream: %v", err)
}
return
}
}

// decoded json-reencoded default:
ledger := ctx.Node.Ledger()
b, c, err := ledger.BlockCert(basics.Round(queryRound))
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion rpcs/httpFetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (hf *HTTPFetcher) GetBlockBytes(ctx context.Context, r basics.Round) (data
// TODO: Temporarily allow old and new content types so we have time for lazy upgrades
// Remove this 'old' string after next release.
const ledgerResponseContentTypeOld = "application/algorand-block-v1"
if contentTypes[0] != ledgerResponseContentType && contentTypes[0] != ledgerResponseContentTypeOld {
if contentTypes[0] != LedgerResponseContentType && contentTypes[0] != ledgerResponseContentTypeOld {
hf.log.Warnf("http block fetcher response has an invalid content type : %s", contentTypes[0])
response.Body.Close()
return nil, fmt.Errorf("http block fetcher invalid content type '%s'", contentTypes[0])
Expand Down
15 changes: 9 additions & 6 deletions rpcs/ledgerService.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ import (
"github.com/algorand/go-algorand/protocol"
)

const ledgerResponseContentType = "application/x-algorand-block-v1"
// LedgerResponseContentType is the HTTP Content-Type header for a raw binary block
const LedgerResponseContentType = "application/x-algorand-block-v1"
const ledgerResponseHasBlockCacheControl = "public, max-age=31536000, immutable" // 31536000 seconds are one year.
const ledgerResponseMissingBlockCacheControl = "public, max-age=1, must-revalidate" // cache for 1 second, and force revalidation afterward
const ledgerServerMaxBodyLength = 512 // we don't really pass meaningful content here, so 512 bytes should be a safe limit
Expand Down Expand Up @@ -160,7 +161,7 @@ func (ls *LedgerService) ServeHTTP(response http.ResponseWriter, request *http.R
response.WriteHeader(http.StatusBadRequest)
return
}
encodedBlockCert, err := ls.encodedBlockCert(round)
encodedBlockCert, err := RawBlockBytes(ls.ledger, basics.Round(round))
if err != nil {
switch err.(type) {
case ledger.ErrNoEntry:
Expand All @@ -176,7 +177,7 @@ func (ls *LedgerService) ServeHTTP(response http.ResponseWriter, request *http.R
}
}

response.Header().Set("Content-Type", ledgerResponseContentType)
response.Header().Set("Content-Type", LedgerResponseContentType)
response.Header().Set("Content-Length", strconv.Itoa(len(encodedBlockCert)))
response.Header().Set("Cache-Control", ledgerResponseHasBlockCacheControl)
response.WriteHeader(http.StatusOK)
Expand Down Expand Up @@ -236,7 +237,8 @@ func (ls *LedgerService) handleCatchupReq(ctx context.Context, reqMsg network.In
return
}
res.Round = req.Round
encodedBlob, err := ls.encodedBlockCert(req.Round)
encodedBlob, err := RawBlockBytes(ls.ledger, basics.Round(req.Round))

if err != nil {
res.Error = err.Error()
return
Expand All @@ -254,8 +256,9 @@ func (ls *LedgerService) sendCatchupRes(ctx context.Context, target network.Unic
}
}

func (ls *LedgerService) encodedBlockCert(round uint64) ([]byte, error) {
blk, cert, err := ls.ledger.EncodedBlockCert(basics.Round(round))
// RawBlockBytes return the msgpack bytes for a block
func RawBlockBytes(ledger *data.Ledger, round basics.Round) ([]byte, error) {
blk, cert, err := ledger.EncodedBlockCert(round)
if err != nil {
return nil, err
}
Expand Down