From 86be5e680ed81351d0792529db75b9b6a1878a54 Mon Sep 17 00:00:00 2001 From: Brian Olson Date: Tue, 26 Nov 2019 09:38:26 -0500 Subject: [PATCH] add ?raw=1 to local block api to return msgpack bytes with full data --- daemon/algod/api/server/v1/handlers/errors.go | 1 + .../algod/api/server/v1/handlers/handlers.go | 34 +++++++++++++++++++ rpcs/httpFetcher.go | 2 +- rpcs/ledgerService.go | 15 ++++---- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/daemon/algod/api/server/v1/handlers/errors.go b/daemon/algod/api/server/v1/handlers/errors.go index 98dede9ae7..17eeca5305 100644 --- a/daemon/algod/api/server/v1/handlers/errors.go +++ b/daemon/algod/api/server/v1/handlers/errors.go @@ -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" diff --git a/daemon/algod/api/server/v1/handlers/handlers.go b/daemon/algod/api/server/v1/handlers/handlers.go index dfbb9cb866..d00058241d 100644 --- a/daemon/algod/api/server/v1/handlers/handlers.go +++ b/daemon/algod/api/server/v1/handlers/handlers.go @@ -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) { @@ -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' @@ -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 { diff --git a/rpcs/httpFetcher.go b/rpcs/httpFetcher.go index 4f32a7db34..b5c339cd3b 100644 --- a/rpcs/httpFetcher.go +++ b/rpcs/httpFetcher.go @@ -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]) diff --git a/rpcs/ledgerService.go b/rpcs/ledgerService.go index a67690b1bc..5d0a3298ad 100644 --- a/rpcs/ledgerService.go +++ b/rpcs/ledgerService.go @@ -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 @@ -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: @@ -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) @@ -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 @@ -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 }