Skip to content
This repository was 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
petabytestorage wants to merge 6 commits intoNebulousLabs:masterfrom
petabytestorage:explorer-patch
Open

extend explorer api with block.ID() and optional hexblock field#2418
petabytestorage wants to merge 6 commits intoNebulousLabs:masterfrom
petabytestorage:explorer-patch

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
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

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

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

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
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
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

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

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