Skip to content
This repository has been archived by the owner on Nov 2, 2018. It is now read-only.

extend explorer api with block.ID() and optional hexblock field #2418

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

petabytestorage
Copy link
Contributor

block.ID() fixes "missing block" explorer issue with blocks with no transaction. "hexblock" optional return field (with "hexblock"="true" optional form parameter) provides verbatim hex encoded binary block to enable external applications and developer scaffolding.

api/explorer.go Outdated
@@ -188,10 +193,17 @@ func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block)
panic("incorrect request to buildExplorerBlock - block does not exist")
}

hex_block := ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more idiomatic to do var hex_block string

Project consistently and strictly uses camel case throughout for variable names, need to do the same here.

@petabytestorage
Copy link
Contributor Author

hex_block_enabled -> hexBlockEnabled and hex_block -> hexBlock ty @DavidVorick

@@ -214,7 +226,8 @@ func (api *API) explorerBlocksHandler(w http.ResponseWriter, req *http.Request,
return
}
WriteJSON(w, ExplorerBlockGET{
Block: api.buildExplorerBlock(height, block),
Block: api.buildExplorerBlock(height, block,
req.FormValue("hexblock") == "true"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't comment on this in the first pass. Wasn't sure what we wanted here, but after discussing with Luke, we agree that the call should return an error if hexblock is set to anything besides "true" or "false" - strict api reduces the liklihood of dev mistakes, and coherent errors around incorrect input make it easier to find the source of the mistake.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me and I will do it. I copied this pattern from api/wallet.go where it appears twice and does only look for "true". I also guessed that the wallet.go would be more strict/secure than an explorer module. So I'm going to suggest the wallet.go "force" field should be considered as well if you want to use this pattern for example to avoid misleading other devs into copying around or learning a bad pattern. @DavidVorick

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me an example of the new pattern you and luke want in the pre-existing source code please? I'm looking through all the boolean flags in the system and not finding one "good" one according to your description.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per our discord discussion, we'll punt this to a follow-up PR

@@ -271,7 +284,8 @@ func (api *API) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps
if exists {
WriteJSON(w, ExplorerHashGET{
HashType: "blockid",
Block: api.buildExplorerBlock(height, block),
Block: api.buildExplorerBlock(height, block,
req.FormValue("hexblock") == "true"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

@DavidVorick
Copy link
Member

LGTM except for the final comments. Looking for review from @lukechampine

api/explorer.go Outdated
@@ -188,10 +193,17 @@ func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block)
panic("incorrect request to buildExplorerBlock - block does not exist")
}

var hexBlock string
if hexBlockEnable {
hexBlock = fmt.Sprintf("%x", encoding.Marshal(block)[:])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

encoding.Marshal returns a []byte so the [:] slicing is unnecessary

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, I think we slightly prefer:

import "encoding/hex"

hex.EncodeToString(encoding.Marshal(block))

api/explorer.go Outdated
return ExplorerBlock{
MinerPayoutIDs: mpoids,
Transactions: etxns,
RawBlock: block,
HexBlock: hexBlock,
BlockId: fmt.Sprintf("%x", encoding.Marshal(block.ID())[:]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

api/explorer.go Outdated
@@ -20,6 +22,8 @@ type (
MinerPayoutIDs []types.SiacoinOutputID `json:"minerpayoutids"`
Transactions []ExplorerTransaction `json:"transactions"`
RawBlock types.Block `json:"rawblock"`
HexBlock string `json:"hexblock"`
BlockId string `json:"blockid"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be BlockID

api/explorer.go Outdated
var hexBlock string
if hexBlockEnable {
hexBlock = hex.EncodeToString(encoding.Marshal(block))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for structs with optional fields, I generally prefer:

b := ExplorerBlock{
    // required fields here...
}
if hexBlockEnable {
    b.HexBlock = hex.EncodeToString(encoding.Marshal(block))
}
return b

@petabytestorage
Copy link
Contributor Author

all done @DavidVorick and @lukechampine thanks for the idiomatic go

api/explorer.go Outdated
@@ -20,6 +22,8 @@ type (
MinerPayoutIDs []types.SiacoinOutputID `json:"minerpayoutids"`
Transactions []ExplorerTransaction `json:"transactions"`
RawBlock types.Block `json:"rawblock"`
HexBlock string `json:"hexblock"`
BlockID string `json:"blockid"`

modules.BlockFacts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, changing to BlockID actually caused tests to start failing, because modules.BlockFacts has an embedded BlockID field.

Is it necessary to have a BlockID string field if we already have it as a [32]byte? If so, maybe call it HexBlockID?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lukechampine when i remove the field i get:

➜  Sia git:(explorer-patch) ✗ make
go install -tags 'netgo' -ldflags='-s -w' ./api ./build ./compatibility ./crypto ./encoding ./modules ./modules/consensus ./modules/explorer ./modules/gateway ./modules/host ./modules/host/contractmanager ./modules/renter ./modules/renter/contractor ./modules/renter/hostdb ./modules/renter/hostdb/hosttree ./modules/renter/proto ./modules/miner ./modules/wallet ./modules/transactionpool ./persist ./siac ./siad ./sync ./types
# github.com/NebulousLabs/Sia/api
api/explorer.go:201: unknown field 'BlockID' in struct literal of type ExplorerBlock
Makefile:75: recipe for target 'release-std' failed
make: *** [release-std] Error 2

therefore i renamed it to HexBlockID however i left the json named the standard way because to me it seems cleaner. please feel free to adjust again if you think there is a cleaner way to handle this.

Copy link
Contributor

@ChrisSchinnerl ChrisSchinnerl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good to me. I'd only like to see a few minor changes that centralize the encoding of the types a bit more.

@@ -188,13 +194,18 @@ func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block)
panic("incorrect request to buildExplorerBlock - block does not exist")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion we shouldn't crash if a user supplies an invalid height but return an error we write to the http response. What do you think @lukechampine @DavidVorick

@@ -20,6 +22,8 @@ type (
MinerPayoutIDs []types.SiacoinOutputID `json:"minerpayoutids"`
Transactions []ExplorerTransaction `json:"transactions"`
RawBlock types.Block `json:"rawblock"`
HexBlock string `json:"hexblock"`
HexBlockID string `json:"blockid"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HexBlockID can probably be BlockID and have types.BlockID as a type.
types.BlockID implements String and MarshalJSON which should implicitly convert it to a hex string in WriteJSON.

MinerPayoutIDs: mpoids,
Transactions: etxns,
RawBlock: block,
HexBlockID: hex.EncodeToString(encoding.Marshal(block.ID())),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above this can probably just be a type.BlockID instead of having to encode yourself. That means it would be BlockID: block.ID(),


BlockFacts: facts,
}
if hexBlockEnable {
b.HexBlock = hex.EncodeToString(encoding.Marshal(block))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you implement something like the following in types/block.go?

// String returns a hex encoded marshalled Block.
func (b Block) String() string {                                                                                                                                                                                  
    return hex.EncodeToString(encoding.Marshal(b))                                                                                                                                                                                     
} 

That way you can simply do b.HexBlock = block.String() here.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants