Repository: tendermint/tendermint (Go)

Issue:
WebSocket: Add block ID to "newblock" event
According to the comments here: https://github.com/tendermint/tendermint/blob/master/types/events.go#L14-L18, a `newblock` event is triggered after a block has been committed. 

That being the case, I would expect the block data included with the event to have the same structure as the `/block?height=xxxx` RPC call.

However, the `newblock` event seems to be missing the `block_id` section.

e.g. 

WS event: https://gist.github.com/clockworkgr/46adb9ae51af24b928f83d443fae617f
RPC call: https://gist.github.com/clockworkgr/61ed9fa97d40950a8ce8bfd06f7a87c6

Is this by omission or by design?

As it stands, someone would have to do an RPC call to get that additional data as soon as a `newblock` event is received which kind of defeats the purpose for a lot of use cases.



Discussion:
Although they are meshed together at the surface level, the web socket and RPC data structures stem from two different sources. The WS is tied with the event system within consensus (hence it returns a bunch of other events ) whilst RPC calls come from the block store. 

As to whether `block_id` was left out of the `newblock` event by design, I don't think so but nor would I call it a bug. 

In any case, I would think this should be quite an easy change to make if we feel that it might help those using WS.    
That makes sense on why they ended up different.

In any case, I think it would be helpful for WS users so would appreciate it if it gets added to the pipeline somewhere if it's an easy change.

Thanks
@clockworkgr do you want the BlockId in order to get the header hash, or do you have a need for the parts as well?

Block at height h has the BlockID of h-1. 
In the specific case I'm using it I make an extra request by height to get the hash yes. 

Don't think I have a need for the parts at this time, however I'd still consider it best practice for the RPC call and the WS event to contain the same information.



Patch:
diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md
index b094fc28e6f..dbe0fbdec56 100644
--- a/CHANGELOG_PENDING.md
+++ b/CHANGELOG_PENDING.md
@@ -67,6 +67,7 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermi
 
 ### IMPROVEMENTS
 
+- [types] \#6478 Add `block_id` to `newblock` event (@jeebster)
 - [crypto/ed25519] \#5632 Adopt zip215 `ed25519` verification. (@marbar3778)
 - [privval] \#5603 Add `--key` to `init`, `gen_validator`, `testnet` & `unsafe_reset_priv_validator` for use in generating `secp256k1` keys.
 - [privval] \#5725 Add gRPC support to private validator.
diff --git a/state/execution.go b/state/execution.go
index 437d027d05a..20f47c5b035 100644
--- a/state/execution.go
+++ b/state/execution.go
@@ -216,7 +216,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
 
 	// Events are fired after everything else.
 	// NOTE: if we crash between Commit and Save, events wont be fired during replay
-	fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates)
+	fireEvents(blockExec.logger, blockExec.eventBus, block, blockID, abciResponses, validatorUpdates)
 
 	return state, retainHeight, nil
 }
@@ -495,11 +495,13 @@ func fireEvents(
 	logger log.Logger,
 	eventBus types.BlockEventPublisher,
 	block *types.Block,
+	blockID types.BlockID,
 	abciResponses *tmstate.ABCIResponses,
 	validatorUpdates []*types.Validator,
 ) {
 	if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{
 		Block:            block,
+		BlockID:          blockID,
 		ResultBeginBlock: *abciResponses.BeginBlock,
 		ResultEndBlock:   *abciResponses.EndBlock,
 	}); err != nil {
@@ -579,7 +581,8 @@ func ExecCommitBlock(
 			return nil, err
 		}
 
-		fireEvents(be.logger, be.eventBus, block, abciResponses, validatorUpdates)
+		blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(types.BlockPartSizeBytes).Header()}
+		fireEvents(be.logger, be.eventBus, block, blockID, abciResponses, validatorUpdates)
 	}
 
 	// Commit block, get hash back
diff --git a/types/events.go b/types/events.go
index b71661a05e5..b926ca82887 100644
--- a/types/events.go
+++ b/types/events.go
@@ -62,7 +62,8 @@ func init() {
 // but some (an input to a call tx or a receive) are more exotic
 
 type EventDataNewBlock struct {
-	Block *Block `json:"block"`
+	Block   *Block  `json:"block"`
+	BlockID BlockID `json:"block_id"`
 
 	ResultBeginBlock abci.ResponseBeginBlock `json:"result_begin_block"`
 	ResultEndBlock   abci.ResponseEndBlock   `json:"result_end_block"`


Test patch:
diff --git a/types/event_bus_test.go b/types/event_bus_test.go
index aff9d20f51a..9813957184f 100644
--- a/types/event_bus_test.go
+++ b/types/event_bus_test.go
@@ -75,6 +75,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
 	})
 
 	block := MakeBlock(0, []Tx{}, nil, []Evidence{})
+	blockID := BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(BlockPartSizeBytes).Header()}
 	resultBeginBlock := abci.ResponseBeginBlock{
 		Events: []abci.Event{
 			{Type: "testType", Attributes: []abci.EventAttribute{{Key: "baz", Value: "1"}}},
@@ -96,6 +97,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
 		msg := <-blocksSub.Out()
 		edt := msg.Data().(EventDataNewBlock)
 		assert.Equal(t, block, edt.Block)
+		assert.Equal(t, blockID, edt.BlockID)
 		assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
 		assert.Equal(t, resultEndBlock, edt.ResultEndBlock)
 		close(done)
@@ -103,6 +105,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) {
 
 	err = eventBus.PublishEventNewBlock(EventDataNewBlock{
 		Block:            block,
+		BlockID:          blockID,
 		ResultBeginBlock: resultBeginBlock,
 		ResultEndBlock:   resultEndBlock,
 	})