Browse Source

Merge pull request #967 from tendermint/feature/total-tx

Add TotalTx to block header
pull/975/head
Anton Kaliaev 7 years ago
committed by GitHub
parent
commit
b78606d94f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 24 deletions
  1. +3
    -1
      blockchain/reactor_test.go
  2. +2
    -1
      consensus/mempool_test.go
  3. +3
    -2
      consensus/state.go
  4. +5
    -4
      lite/helpers.go
  5. +2
    -1
      state/execution.go
  6. +4
    -2
      state/execution_test.go
  7. +11
    -6
      state/state.go
  8. +22
    -7
      types/block.go
  9. +80
    -0
      types/block_test.go

+ 3
- 1
blockchain/reactor_test.go View File

@ -106,7 +106,9 @@ func makeBlock(height int64, state *sm.State) *types.Block {
valHash := state.Validators.Hash() valHash := state.Validators.Hash()
prevBlockID := types.BlockID{prevHash, prevParts} prevBlockID := types.BlockID{prevHash, prevParts}
block, _ := types.MakeBlock(height, "test_chain", makeTxs(height), block, _ := types.MakeBlock(height, "test_chain", makeTxs(height),
new(types.Commit), prevBlockID, valHash, state.AppHash, state.Params.BlockGossipParams.BlockPartSizeBytes)
state.LastBlockTotalTx, new(types.Commit),
prevBlockID, valHash, state.AppHash,
state.Params.BlockGossipParams.BlockPartSizeBytes)
return block return block
} }


+ 2
- 1
consensus/mempool_test.go View File

@ -108,7 +108,8 @@ func TestTxConcurrentWithCommit(t *testing.T) {
for nTxs := 0; nTxs < NTxs; { for nTxs := 0; nTxs < NTxs; {
select { select {
case b := <-newBlockCh: case b := <-newBlockCh:
nTxs += b.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block.Header.NumTxs
evt := b.(types.TMEventData).Unwrap().(types.EventDataNewBlock)
nTxs += int(evt.Block.Header.NumTxs)
case <-ticker.C: case <-ticker.C:
panic("Timed out waiting to commit blocks with transactions") panic("Timed out waiting to commit blocks with transactions")
} }


+ 3
- 2
consensus/state.go View File

@ -863,7 +863,8 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
// Mempool validated transactions // Mempool validated transactions
txs := cs.mempool.Reap(cs.config.MaxBlockSizeTxs) txs := cs.mempool.Reap(cs.config.MaxBlockSizeTxs)
return types.MakeBlock(cs.Height, cs.state.ChainID, txs, commit,
return types.MakeBlock(cs.Height, cs.state.ChainID, txs,
cs.state.LastBlockTotalTx, commit,
cs.state.LastBlockID, cs.state.Validators.Hash(), cs.state.LastBlockID, cs.state.Validators.Hash(),
cs.state.AppHash, cs.state.Params.BlockPartSizeBytes) cs.state.AppHash, cs.state.Params.BlockPartSizeBytes)
} }
@ -1200,7 +1201,7 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
// Create a copy of the state for staging // Create a copy of the state for staging
// and an event cache for txs // and an event cache for txs
stateCopy := cs.state.Copy() stateCopy := cs.state.Copy()
txEventBuffer := types.NewTxEventBuffer(cs.eventBus, block.NumTxs)
txEventBuffer := types.NewTxEventBuffer(cs.eventBus, int(block.NumTxs))
// Execute and commit the block, update and save the state, and update the mempool. // Execute and commit the block, update and save the state, and update the mempool.
// All calls to the proxyAppConn come here. // All calls to the proxyAppConn come here.


+ 5
- 4
lite/helpers.go View File

@ -113,10 +113,11 @@ func genHeader(chainID string, height int64, txs types.Txs,
vals *types.ValidatorSet, appHash []byte) *types.Header { vals *types.ValidatorSet, appHash []byte) *types.Header {
return &types.Header{ return &types.Header{
ChainID: chainID,
Height: height,
Time: time.Now(),
NumTxs: len(txs),
ChainID: chainID,
Height: height,
Time: time.Now(),
NumTxs: int64(len(txs)),
TotalTxs: int64(len(txs)),
// LastBlockID // LastBlockID
// LastCommitHash // LastCommitHash
ValidatorsHash: vals.Hash(), ValidatorsHash: vals.Hash(),


+ 2
- 1
state/execution.go View File

@ -178,7 +178,8 @@ func (s *State) ValidateBlock(block *types.Block) error {
func (s *State) validateBlock(block *types.Block) error { func (s *State) validateBlock(block *types.Block) error {
// Basic block validation. // Basic block validation.
err := block.ValidateBasic(s.ChainID, s.LastBlockHeight, s.LastBlockID, s.LastBlockTime, s.AppHash)
err := block.ValidateBasic(s.ChainID, s.LastBlockHeight,
s.LastBlockTotalTx, s.LastBlockID, s.LastBlockTime, s.AppHash)
if err != nil { if err != nil {
return err return err
} }


+ 4
- 2
state/execution_test.go View File

@ -66,7 +66,9 @@ func makeBlock(height int64, state *State) *types.Block {
prevParts := types.PartSetHeader{} prevParts := types.PartSetHeader{}
valHash := state.Validators.Hash() valHash := state.Validators.Hash()
prevBlockID := types.BlockID{prevHash, prevParts} prevBlockID := types.BlockID{prevHash, prevParts}
block, _ := types.MakeBlock(height, chainID, makeTxs(height), new(types.Commit),
prevBlockID, valHash, state.AppHash, testPartSize)
block, _ := types.MakeBlock(height, chainID,
makeTxs(height), state.LastBlockTotalTx,
new(types.Commit), prevBlockID, valHash,
state.AppHash, testPartSize)
return block return block
} }

+ 11
- 6
state/state.go View File

@ -45,11 +45,12 @@ type State struct {
// These fields are updated by SetBlockAndValidators. // These fields are updated by SetBlockAndValidators.
// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
// LastValidators is used to validate block.LastCommit. // LastValidators is used to validate block.LastCommit.
LastBlockHeight int64
LastBlockID types.BlockID
LastBlockTime time.Time
Validators *types.ValidatorSet
LastValidators *types.ValidatorSet
LastBlockHeight int64
LastBlockTotalTx int64
LastBlockID types.BlockID
LastBlockTime time.Time
Validators *types.ValidatorSet
LastValidators *types.ValidatorSet
// When a block returns a validator set change via EndBlock, // When a block returns a validator set change via EndBlock,
// the change only applies to the next block. // the change only applies to the next block.
// So, if s.LastBlockHeight causes a valset change, // So, if s.LastBlockHeight causes a valset change,
@ -113,6 +114,7 @@ func (s *State) Copy() *State {
return &State{ return &State{
db: s.db, db: s.db,
LastBlockHeight: s.LastBlockHeight, LastBlockHeight: s.LastBlockHeight,
LastBlockTotalTx: s.LastBlockTotalTx,
LastBlockID: s.LastBlockID, LastBlockID: s.LastBlockID,
LastBlockTime: s.LastBlockTime, LastBlockTime: s.LastBlockTime,
Validators: s.Validators.Copy(), Validators: s.Validators.Copy(),
@ -250,16 +252,19 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ
nextValSet.IncrementAccum(1) nextValSet.IncrementAccum(1)
s.setBlockAndValidators(header.Height, s.setBlockAndValidators(header.Height,
header.NumTxs,
types.BlockID{header.Hash(), blockPartsHeader}, types.BlockID{header.Hash(), blockPartsHeader},
header.Time, header.Time,
prevValSet, nextValSet) prevValSet, nextValSet)
} }
func (s *State) setBlockAndValidators(height int64, blockID types.BlockID, blockTime time.Time,
func (s *State) setBlockAndValidators(height int64,
newTxs int64, blockID types.BlockID, blockTime time.Time,
prevValSet, nextValSet *types.ValidatorSet) { prevValSet, nextValSet *types.ValidatorSet) {
s.LastBlockHeight = height s.LastBlockHeight = height
s.LastBlockTotalTx += newTxs
s.LastBlockID = blockID s.LastBlockID = blockID
s.LastBlockTime = blockTime s.LastBlockTime = blockTime
s.Validators = nextValSet s.Validators = nextValSet


+ 22
- 7
types/block.go View File

@ -23,14 +23,19 @@ type Block struct {
// MakeBlock returns a new block and corresponding partset from the given information. // MakeBlock returns a new block and corresponding partset from the given information.
// TODO: Add version information to the Block struct. // TODO: Add version information to the Block struct.
func MakeBlock(height int64, chainID string, txs []Tx, commit *Commit,
prevBlockID BlockID, valHash, appHash []byte, partSize int) (*Block, *PartSet) {
func MakeBlock(height int64, chainID string, txs []Tx,
totalTxs int64, commit *Commit,
prevBlockID BlockID, valHash, appHash []byte,
partSize int) (*Block, *PartSet) {
newTxs := int64(len(txs))
block := &Block{ block := &Block{
Header: &Header{ Header: &Header{
ChainID: chainID, ChainID: chainID,
Height: height, Height: height,
Time: time.Now(), Time: time.Now(),
NumTxs: len(txs),
NumTxs: newTxs,
TotalTxs: totalTxs + newTxs,
LastBlockID: prevBlockID, LastBlockID: prevBlockID,
ValidatorsHash: valHash, ValidatorsHash: valHash,
AppHash: appHash, // state merkle root of txs from the previous block. AppHash: appHash, // state merkle root of txs from the previous block.
@ -45,8 +50,10 @@ func MakeBlock(height int64, chainID string, txs []Tx, commit *Commit,
} }
// ValidateBasic performs basic validation that doesn't involve state data. // ValidateBasic performs basic validation that doesn't involve state data.
func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64, lastBlockID BlockID,
func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64,
lastBlockTotalTx int64, lastBlockID BlockID,
lastBlockTime time.Time, appHash []byte) error { lastBlockTime time.Time, appHash []byte) error {
if b.ChainID != chainID { if b.ChainID != chainID {
return errors.New(cmn.Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID)) return errors.New(cmn.Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID))
} }
@ -60,8 +67,12 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64, lastBlockID
return errors.New("Invalid Block.Header.Time") return errors.New("Invalid Block.Header.Time")
} }
*/ */
if b.NumTxs != len(b.Data.Txs) {
return errors.New(cmn.Fmt("Wrong Block.Header.NumTxs. Expected %v, got %v", len(b.Data.Txs), b.NumTxs))
newTxs := int64(len(b.Data.Txs))
if b.NumTxs != newTxs {
return errors.New(cmn.Fmt("Wrong Block.Header.NumTxs. Expected %v, got %v", newTxs, b.NumTxs))
}
if b.TotalTxs != lastBlockTotalTx+newTxs {
return errors.New(cmn.Fmt("Wrong Block.Header.TotalTxs. Expected %v, got %v", lastBlockTotalTx+newTxs, b.TotalTxs))
} }
if !b.LastBlockID.Equals(lastBlockID) { if !b.LastBlockID.Equals(lastBlockID) {
return errors.New(cmn.Fmt("Wrong Block.Header.LastBlockID. Expected %v, got %v", lastBlockID, b.LastBlockID)) return errors.New(cmn.Fmt("Wrong Block.Header.LastBlockID. Expected %v, got %v", lastBlockID, b.LastBlockID))
@ -160,7 +171,8 @@ type Header struct {
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
Height int64 `json:"height"` Height int64 `json:"height"`
Time time.Time `json:"time"` Time time.Time `json:"time"`
NumTxs int `json:"num_txs"` // XXX: Can we get rid of this?
NumTxs int64 `json:"num_txs"` // XXX: Can we get rid of this?
TotalTxs int64 `json:"total_txs"`
LastBlockID BlockID `json:"last_block_id"` LastBlockID BlockID `json:"last_block_id"`
LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block
DataHash data.Bytes `json:"data_hash"` // transactions DataHash data.Bytes `json:"data_hash"` // transactions
@ -179,6 +191,7 @@ func (h *Header) Hash() data.Bytes {
"Height": h.Height, "Height": h.Height,
"Time": h.Time, "Time": h.Time,
"NumTxs": h.NumTxs, "NumTxs": h.NumTxs,
"TotalTxs": h.TotalTxs,
"LastBlockID": h.LastBlockID, "LastBlockID": h.LastBlockID,
"LastCommit": h.LastCommitHash, "LastCommit": h.LastCommitHash,
"Data": h.DataHash, "Data": h.DataHash,
@ -197,6 +210,7 @@ func (h *Header) StringIndented(indent string) string {
%s Height: %v %s Height: %v
%s Time: %v %s Time: %v
%s NumTxs: %v %s NumTxs: %v
%s TotalTxs: %v
%s LastBlockID: %v %s LastBlockID: %v
%s LastCommit: %v %s LastCommit: %v
%s Data: %v %s Data: %v
@ -207,6 +221,7 @@ func (h *Header) StringIndented(indent string) string {
indent, h.Height, indent, h.Height,
indent, h.Time, indent, h.Time,
indent, h.NumTxs, indent, h.NumTxs,
indent, h.TotalTxs,
indent, h.LastBlockID, indent, h.LastBlockID,
indent, h.LastCommitHash, indent, h.LastCommitHash,
indent, h.DataHash, indent, h.DataHash,


+ 80
- 0
types/block_test.go View File

@ -0,0 +1,80 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
)
func TestValidateBlock(t *testing.T) {
txs := []Tx{Tx("foo"), Tx("bar")}
lastID := makeBlockID()
valHash := []byte("val")
appHash := []byte("app")
h := int64(3)
voteSet, _, vals := randVoteSet(h-1, 1, VoteTypePrecommit,
10, 1)
commit, err := makeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err)
block, _ := MakeBlock(h, "hello", txs, 10, commit,
lastID, valHash, appHash, 2)
require.NotNil(t, block)
// proper block must pass
err = block.ValidateBasic("hello", h-1, 10, lastID, block.Time, appHash)
require.NoError(t, err)
// wrong chain fails
err = block.ValidateBasic("other", h-1, 10, lastID, block.Time, appHash)
require.Error(t, err)
// wrong height fails
err = block.ValidateBasic("hello", h+4, 10, lastID, block.Time, appHash)
require.Error(t, err)
// wrong total tx fails
err = block.ValidateBasic("hello", h-1, 15, lastID, block.Time, appHash)
require.Error(t, err)
// wrong blockid fails
err = block.ValidateBasic("hello", h-1, 10, makeBlockID(), block.Time, appHash)
require.Error(t, err)
// wrong app hash fails
err = block.ValidateBasic("hello", h-1, 10, lastID, block.Time, []byte("bad-hash"))
require.Error(t, err)
}
func makeBlockID() BlockID {
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
return BlockID{blockHash, blockPartsHeader}
}
func makeCommit(blockID BlockID, height int64, round int,
voteSet *VoteSet,
validators []*PrivValidatorFS) (*Commit, error) {
voteProto := &Vote{
ValidatorAddress: nil,
ValidatorIndex: -1,
Height: height,
Round: round,
Type: VoteTypePrecommit,
BlockID: blockID,
}
// all sign
for i := 0; i < len(validators); i++ {
vote := withValidator(voteProto, validators[i].GetAddress(), i)
_, err := signAddVote(validators[i], vote, voteSet)
if err != nil {
return nil, err
}
}
return voteSet.MakeCommit(), nil
}

Loading…
Cancel
Save