Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Tendermint Block Response #4922

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9c8bcba
Added the conversion of the block in order to convert HEX validator a…
RiccardoM Aug 19, 2019
37c46f2
Merge branch 'master' of github.com:cosmos/cosmos-sdk into riccardo/b…
RiccardoM Aug 19, 2019
604f494
Removed mutex
RiccardoM Aug 19, 2019
40604b4
Undo go mod change
RiccardoM Aug 19, 2019
4439a72
Removed Cosmos* type prefix
RiccardoM Aug 19, 2019
499818d
Sorted imports using goimports instead of gofmt
RiccardoM Aug 20, 2019
e29aba1
Applied suggestion to conversion call
RiccardoM Aug 21, 2019
6934f17
Undo changed in go.sum
RiccardoM Aug 21, 2019
93a8526
Used type wrappers instead of re defining the whole type while conver…
RiccardoM Aug 21, 2019
faa3f2c
Added // nolint: structtag
RiccardoM Aug 21, 2019
69fd89c
Removed useless comments and variables while converting a block
RiccardoM Aug 26, 2019
909faa0
Unified how different types are treated (encapsulation) and added mis…
RiccardoM Aug 26, 2019
70d5cb7
Reverted back encapsulation
RiccardoM Aug 26, 2019
c667b53
Merge remote-tracking branch 'origin/riccardo/block-response-conversi…
RiccardoM Aug 26, 2019
8910ec2
Update client/rpc/block_converter.go
RiccardoM Aug 26, 2019
80ffba3
Merge branch 'master' of github.com:cosmos/cosmos-sdk into riccardo/b…
RiccardoM Oct 16, 2019
59aff26
Added tests
RiccardoM Oct 16, 2019
b520774
Fixed the missing of some fields
RiccardoM Oct 17, 2019
d8daeab
Merge branch 'master' into riccardo/block-response-conversion
alexanderbez Oct 17, 2019
8e04baf
Merge branch 'master' into riccardo/block-response-conversion
alexanderbez Oct 17, 2019
3a4d706
Updated the comments and removed some useless variables as said by @a…
RiccardoM Oct 18, 2019
8a24e6d
Merge branch 'master' into riccardo/block-response-conversion
fedekunze Oct 28, 2019
dbeb759
Removed global test variable and implemented nil test
RiccardoM Nov 13, 2019
37f2585
Merge remote-tracking branch 'origin/riccardo/block-response-conversi…
RiccardoM Nov 13, 2019
f241029
Merge branch 'master' of github.com:cosmos/cosmos-sdk into riccardo/b…
RiccardoM Nov 13, 2019
1f9e9db
Renamed variables to solve Golint errors
RiccardoM Nov 13, 2019
56a37b9
Merge branch 'master' into riccardo/block-response-conversion
fedekunze Nov 19, 2019
1815c3a
Merge branch 'master' into riccardo/block-response-conversion
alexanderbez Dec 2, 2019
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
2 changes: 1 addition & 1 deletion client/rpc/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
return codec.Cdc.MarshalJSONIndent(res, "", " ")
}

return codec.Cdc.MarshalJSON(res)
return codec.Cdc.MarshalJSON(ConvertBlockResult(res))
}

// get the current blockchain height
Expand Down
221 changes: 221 additions & 0 deletions client/rpc/block_converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package rpc

import (
"encoding/json"

sdk "github.com/cosmos/cosmos-sdk/types"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/version"
)

// ResultBlock represents a single block with metadata
type ResultBlock struct {
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
BlockMeta BlockMeta `json:"block_meta"`
Block Block `json:"block"`
}

// BlockMeta contains meta information about a block - namely, it's ID and Header.
type BlockMeta struct {
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
BlockID types.BlockID `json:"block_id"` // the block hash and partsethash
Header Header `json:"header"` // The block's Header
}

// Header defines a wrapper around Tendermint's Header type overriding various fields.
// nolint: structtag
type Header struct {
// embed original type
types.Header

// override fields so json.Marshal will marshal in accordance with amino JSON format
Version Consensus `json:"version"`
Height int64 `json:"height,string"`
NumTxs int64 `json:"num_txs,string"`
TotalTxs int64 `json:"total_txs,string"`
LastBlockID BlockID `json:"last_block_id"`
ProposerAddress sdk.ValAddress `json:"proposer_address"`
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
}

// MarshalJSON implements the json.Marshaler interface. We do this because Amino
// does not respect the JSON stdlib embedding semantics.
func (h Header) MarshalJSON() ([]byte, error) {
type headerJSON Header
return json.Marshal(headerJSON(h))
}

// BlockID defines a wrapper around Tendermint's BlockID type overriding various fields.
// nolint: structtag
type BlockID struct {
// embed original type
types.BlockID

// override fields from original type
PartsHeader PartSetHeader `json:"parts"`
}

// MarshalJSON implements the json.Marshaler interface. We do this because Amino
// does not respect the JSON stdlib embedding semantics.
func (b BlockID) MarshalJSON() ([]byte, error) {
type blockIDJSON BlockID
return json.Marshal(blockIDJSON(b))
}

// PartSetHeader defines a wrapper around Tendermint's PartSetHeader type overriding various fields.
// nolint: structtag
type PartSetHeader struct {
// embed original type
types.PartSetHeader

// override fields from original type
Total int `json:"total,string"`
}

// MarshalJSON implements the json.Marshaler interface. We do this because Amino
// does not respect the JSON stdlib embedding semantics.
func (b PartSetHeader) MarshalJSON() ([]byte, error) {
type partSetHeadJSON PartSetHeader
return json.Marshal(partSetHeadJSON(b))
}

// Consensus defines a wrapper around Tendermint's Consensus type overriding various fields.
// nolint: structtag
type Consensus struct {
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
// embed original type
version.Consensus

// override fields so json.Marshal will marshal in accordance with amino JSON format
App uint64 `json:"app,string"`
Block uint64 `json:"block,string"`
}

// MarshalJSON implements the json.Marshaler interface. We do this because Amino
// does not respect the JSON stdlib embedding semantics.
func (b Consensus) MarshalJSON() ([]byte, error) {
type consensusJSON Consensus
return json.Marshal(consensusJSON(b))
}

// Block defines the atomic unit of a Tendermint blockchain.
// nolint: structtag.
type Block struct {
// embed original type
types.Block

// override fields
Header Header `json:"header"`
LastCommit Commit `json:"last_commit"`
}

// MarshalJSON implements the json.Marshaler interface. We do this because Amino
// does not respect the JSON stdlib embedding semantics.
func (b Block) MarshalJSON() ([]byte, error) {
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
type blockJSON Block
return json.Marshal(blockJSON(b))
Copy link
Contributor

Choose a reason for hiding this comment

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

copylocks: call of blockJSON copies lock value: github.com/cosmos/cosmos-sdk/client/rpc.Block contains github.com/tendermint/tendermint/types.Block contains sync.Mutex (from govet)

Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably return a reference to avoid mutex copying.

}

// Commit defines a wrapper around Tendermint's Commit type overriding various fields.
// nolint: structtag.
type Commit struct {
// embed original type
*types.Commit

// override fields
BlockID BlockID `json:"block_id"`
Precommits []CommitSig `json:"precommits"`
}

// CommitSig defines a wrapper around Tendermint's CommitSig type overriding various fields.
// nolint: structtag
type CommitSig struct {
// embed original type
types.CommitSig

// override fields so json.Marshal will marshal in accordance with amino JSON format
Height int64 `json:"height,string"`
Round int `json:"round,string"`
BlockID BlockID `json:"block_id"`
ValidatorAddress sdk.ValAddress `json:"validator_address"`
RiccardoM marked this conversation as resolved.
Show resolved Hide resolved
ValidatorIndex int `json:"validator_index,string"`
}

// MarshalJSON implements the json.Marshaler interface. We do this because Amino
// does not respect the JSON stdlib embedding semantics.
func (c CommitSig) MarshalJSON() ([]byte, error) {
type commitSigJSON CommitSig
return json.Marshal(commitSigJSON(c))
}

// ConvertBlockResult allows to convert the given standard ResultBlock into a new ResultBlock having all the
// validator addresses as Bech32 strings instead of HEX ones.
func ConvertBlockResult(res *ctypes.ResultBlock) (blockResult *ResultBlock) {

if res == nil {
return nil
}

header := Header{
Header: res.BlockMeta.Header,

Version: Consensus{
Consensus: res.BlockMeta.Header.Version,
App: res.BlockMeta.Header.Version.App.Uint64(),
Block: res.BlockMeta.Header.Version.Block.Uint64(),
},
Height: res.BlockMeta.Header.Height,
NumTxs: res.BlockMeta.Header.NumTxs,
TotalTxs: res.BlockMeta.Header.TotalTxs,
LastBlockID: BlockID{
BlockID: res.BlockMeta.Header.LastBlockID,
PartsHeader: PartSetHeader{
PartSetHeader: res.BlockMeta.Header.LastBlockID.PartsHeader,
Total: res.BlockMeta.Header.LastBlockID.PartsHeader.Total,
},
},

ProposerAddress: sdk.ValAddress(res.BlockMeta.Header.ProposerAddress),
}

return &ResultBlock{
BlockMeta: BlockMeta{
BlockID: res.BlockMeta.BlockID,
Header: header,
},
Block: Block{
Header: header,
LastCommit: Commit{
Commit: res.Block.LastCommit,
BlockID: BlockID{
BlockID: res.Block.LastCommit.BlockID,
PartsHeader: PartSetHeader{
PartSetHeader: res.Block.LastCommit.BlockID.PartsHeader,
Total: res.Block.LastCommit.BlockID.PartsHeader.Total,
},
},
Precommits: convertPreCommits(res.Block.LastCommit.Precommits),
},
},
}
}

func convertPreCommits(preCommits []*types.CommitSig) (sigs []CommitSig) {
for _, commit := range preCommits {
sig := CommitSig{
CommitSig: *commit,
Height: commit.Height,
Round: commit.Round,
BlockID: BlockID{
BlockID: commit.BlockID,
PartsHeader: PartSetHeader{
PartSetHeader: commit.BlockID.PartsHeader,
Total: commit.BlockID.PartsHeader.Total,
},
},
ValidatorAddress: sdk.ValAddress(commit.ValidatorAddress),
ValidatorIndex: commit.ValidatorIndex,
}

sigs = append(sigs, sig)
}

return sigs
}
27 changes: 27 additions & 0 deletions client/rpc/block_converter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package rpc

import (
"testing"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/stretchr/testify/assert"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)

func TestConvertBlockResult(t *testing.T) {
cdc := codec.New()

// test when the block is not nil
var block ctypes.ResultBlock
defaultBlockJSON := `{"block_meta":{"block_id":{"hash":"30CD3A9EF2082FF9F2575655E99E37ACC3936DC9E534ADF0BD7436C76258225C","parts":{"total":"1","hash":"463460DA73FA0AE441B6041BF2683AE625CE2D9FD290F4C86CD83D1A7FB9F439"}},"header":{"version":{"block":"10","app":"0"},"chain_id":"test-chain-RoCfX4","height":"16","time":"2019-10-16T09:47:21.054275229Z","num_txs":"0","total_txs":"0","last_block_id":{"hash":"168D140232DA3A59757658E257168B6BCE62024F574CA222C2D449BAB1AE4023","parts":{"total":"1","hash":"4E6D7A1C7DE6942470394C21B132A2D9F89B31908C3B6FA8C2AB96A87553B96E"}},"last_commit_hash":"3483509FCC8048496E9B85E1E4C90A3B6E5944F022FE3C0709096932A010F712","data_hash":"","validators_hash":"61E69204249E5EE6F777C0566040929276A1900CE665BCB11006CABF9A4ED9A3","next_validators_hash":"61E69204249E5EE6F777C0566040929276A1900CE665BCB11006CABF9A4ED9A3","consensus_hash":"048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F","app_hash":"070A8213F67F494F7D27A6296C29F0DF62DFA18FD37FB592D9761A44DEE96528","last_results_hash":"","evidence_hash":"","proposer_address":"E8B4AF895B301C3D7108CA93A0B9234E3A2A4B21"}},"block":{"header":{"version":{"block":"10","app":"0"},"chain_id":"test-chain-RoCfX4","height":"16","time":"2019-10-16T09:47:21.054275229Z","num_txs":"0","total_txs":"0","last_block_id":{"hash":"168D140232DA3A59757658E257168B6BCE62024F574CA222C2D449BAB1AE4023","parts":{"total":"1","hash":"4E6D7A1C7DE6942470394C21B132A2D9F89B31908C3B6FA8C2AB96A87553B96E"}},"last_commit_hash":"3483509FCC8048496E9B85E1E4C90A3B6E5944F022FE3C0709096932A010F712","data_hash":"","validators_hash":"61E69204249E5EE6F777C0566040929276A1900CE665BCB11006CABF9A4ED9A3","next_validators_hash":"61E69204249E5EE6F777C0566040929276A1900CE665BCB11006CABF9A4ED9A3","consensus_hash":"048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F","app_hash":"070A8213F67F494F7D27A6296C29F0DF62DFA18FD37FB592D9761A44DEE96528","last_results_hash":"","evidence_hash":"","proposer_address":"E8B4AF895B301C3D7108CA93A0B9234E3A2A4B21"},"data":{"txs":null},"evidence":{"evidence":null},"last_commit":{"block_id":{"hash":"168D140232DA3A59757658E257168B6BCE62024F574CA222C2D449BAB1AE4023","parts":{"total":"1","hash":"4E6D7A1C7DE6942470394C21B132A2D9F89B31908C3B6FA8C2AB96A87553B96E"}},"precommits":[{"type":2,"height":"15","round":"0","block_id":{"hash":"168D140232DA3A59757658E257168B6BCE62024F574CA222C2D449BAB1AE4023","parts":{"total":"1","hash":"4E6D7A1C7DE6942470394C21B132A2D9F89B31908C3B6FA8C2AB96A87553B96E"}},"timestamp":"2019-10-16T09:47:21.054275229Z","validator_address":"E8B4AF895B301C3D7108CA93A0B9234E3A2A4B21","validator_index":"0","signature":"eXB1NPmarUC5+2SCUQiWUCoGqtq0nrSn7giMAF/zWbfv3KmueE+1NoZ0CPLJAhBodz8Y8oL8xVy6ElA7J72kBw=="}]}}}`
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use block JSON that has all fields populated (i.e. txs and evidence). I submitted a tx (using this branch) and when querying the block, I don't see the tx under block.txs. I bet the same will hold true for block.evidence.

So ideally we have:

// ...

convertedBlock := ConvertBlockResult(&block)
expectedOut := `{...}`

bz, err := cdc.MarshalJSON(convertedBlock)
require.NoError(t, err)
require.Equal(t, expectedOut, string(bz))

cdc.MustUnmarshalJSON([]byte(defaultBlockJSON), &block)

convertedBlock := ConvertBlockResult(&block)
assert.Equal(t, "cosmosvaloper1az62lz2mxqwr6ugge2f6pwfrfcaz5jephqhvkt", convertedBlock.BlockMeta.Header.ProposerAddress.String())

convertedBlockString := string(cdc.MustMarshalJSON(&convertedBlock))
assert.NotContains(t, "E8B4AF895B301C3D7108CA93A0B9234E3A2A4B21", convertedBlockString)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

// test when the block is nil
assert.Nil(t, ConvertBlockResult(nil))
}