Browse Source

Merge pull request #972 from tendermint/feature/enhance-endblock

Update EndBlock parameters
pull/1011/head
Ethan Buchman 7 years ago
committed by GitHub
parent
commit
c2436c46e6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 750 additions and 302 deletions
  1. +2
    -2
      blockchain/reactor.go
  2. +2
    -9
      blockchain/reactor_test.go
  3. +0
    -1
      circle.yml
  4. +4
    -4
      consensus/replay_test.go
  5. +4
    -6
      consensus/state.go
  6. +7
    -7
      consensus/state_test.go
  7. +1
    -1
      consensus/wal.go
  8. +2
    -3
      consensus/wal_generator.go
  9. +10
    -9
      docs/architecture/adr-005-consensus-params.md
  10. +19
    -17
      glide.lock
  11. +2
    -2
      glide.yaml
  12. +3
    -3
      lite/dynamic_test.go
  13. +1
    -1
      lite/files/commit_test.go
  14. +1
    -1
      lite/files/provider_test.go
  15. +6
    -5
      lite/helpers.go
  16. +6
    -3
      lite/inquirer_test.go
  17. +2
    -2
      lite/performance_test.go
  18. +2
    -2
      lite/provider_test.go
  19. +1
    -1
      lite/static_test.go
  20. +3
    -4
      rpc/test/helpers.go
  21. +8
    -0
      state/errors.go
  22. +60
    -13
      state/execution.go
  23. +50
    -14
      state/execution_test.go
  24. +154
    -40
      state/state.go
  25. +145
    -5
      state/state_test.go
  26. +33
    -55
      types/block.go
  27. +26
    -44
      types/block_test.go
  28. +1
    -1
      types/genesis_test.go
  29. +88
    -33
      types/params.go
  30. +50
    -2
      types/params_test.go
  31. +20
    -2
      types/protobuf.go
  32. +37
    -0
      types/test_util.go
  33. +0
    -10
      types/vote_set_test.go

+ 2
- 2
blockchain/reactor.go View File

@ -183,7 +183,7 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
// maxMsgSize returns the maximum allowable size of a
// message on the blockchain reactor.
func (bcR *BlockchainReactor) maxMsgSize() int {
return bcR.state.Params.BlockSizeParams.MaxBytes + 2
return bcR.state.ConsensusParams.BlockSize.MaxBytes + 2
}
// Handle messages from the poolReactor telling the reactor what to do.
@ -251,7 +251,7 @@ FOR_LOOP:
// We need both to sync the first block.
break SYNC_LOOP
}
firstParts := first.MakePartSet(bcR.state.Params.BlockPartSizeBytes)
firstParts := first.MakePartSet(bcR.state.ConsensusParams.BlockPartSizeBytes)
firstPartsHeader := firstParts.Header()
// Finally, verify the first block using the second's commit
// NOTE: we can probably make this more efficient, but note that calling


+ 2
- 9
blockchain/reactor_test.go View File

@ -41,7 +41,7 @@ func newBlockchainReactor(logger log.Logger, maxBlockHeight int64) *BlockchainRe
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
firstBlock := makeBlock(blockHeight, state)
secondBlock := makeBlock(blockHeight+1, state)
firstParts := firstBlock.MakePartSet(state.Params.BlockGossipParams.BlockPartSizeBytes)
firstParts := firstBlock.MakePartSet(state.ConsensusParams.BlockGossip.BlockPartSizeBytes)
blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit)
}
@ -105,14 +105,7 @@ func makeTxs(height int64) (txs []types.Tx) {
}
func makeBlock(height int64, state *sm.State) *types.Block {
prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{}
valHash := state.Validators.Hash()
prevBlockID := types.BlockID{prevHash, prevParts}
block, _ := types.MakeBlock(height, "test_chain", makeTxs(height),
state.LastBlockTotalTx, new(types.Commit),
prevBlockID, valHash, state.AppHash,
state.Params.BlockGossipParams.BlockPartSizeBytes)
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit))
return block
}


+ 0
- 1
circle.yml View File

@ -25,7 +25,6 @@ dependencies:
test:
override:
- cd "$PROJECT_PATH" && make tools && make get_vendor_deps && make metalinter_test
- cd "$PROJECT_PATH" && set -o pipefail && make test_integrations 2>&1 | tee test_integrations.log:
timeout: 1800
post:


+ 4
- 4
consensus/replay_test.go View File

@ -107,9 +107,9 @@ func TestWALCrash(t *testing.T) {
{"block with a smaller part size",
func(cs *ConsensusState, ctx context.Context) {
// XXX: is there a better way to change BlockPartSizeBytes?
params := cs.state.Params
params := cs.state.ConsensusParams
params.BlockPartSizeBytes = 512
cs.state.Params = params
cs.state.ConsensusParams = params
sendTxs(cs, ctx)
},
1},
@ -392,7 +392,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
}
func applyBlock(st *sm.State, blk *types.Block, proxyApp proxy.AppConns) {
testPartSize := st.Params.BlockPartSizeBytes
testPartSize := st.ConsensusParams.BlockPartSizeBytes
err := st.ApplyBlock(types.NopEventBus{}, proxyApp.Consensus(), blk, blk.MakePartSet(testPartSize).Header(), mempool)
if err != nil {
panic(err)
@ -590,7 +590,7 @@ func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBl
state, _ := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile())
state.SetLogger(log.TestingLogger().With("module", "state"))
store := NewMockBlockStore(config, state.Params)
store := NewMockBlockStore(config, state.ConsensusParams)
return state, store
}


+ 4
- 6
consensus/state.go View File

@ -863,10 +863,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
// Mempool validated transactions
txs := cs.mempool.Reap(cs.config.MaxBlockSizeTxs)
return types.MakeBlock(cs.Height, cs.state.ChainID, txs,
cs.state.LastBlockTotalTx, commit,
cs.state.LastBlockID, cs.state.Validators.Hash(),
cs.state.AppHash, cs.state.Params.BlockPartSizeBytes)
return cs.state.MakeBlock(cs.Height, txs, commit)
}
// Enter: `timeoutPropose` after entering Propose.
@ -1306,7 +1303,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
var n int
var err error
cs.ProposalBlock = wire.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(),
cs.state.Params.BlockSizeParams.MaxBytes, &n, &err).(*types.Block)
cs.state.ConsensusParams.BlockSize.MaxBytes, &n, &err).(*types.Block)
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() {
@ -1341,7 +1338,8 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error {
return err
} else {
// Probably an invalid signature. Bad peer.
// Probably an invalid signature / Bad peer.
// Seems this can also err sometimes with "Unexpected step" - perhaps not from a bad peer ?
cs.Logger.Error("Error attempting to add vote", "err", err)
return ErrAddingVote
}


+ 7
- 7
consensus/state_test.go View File

@ -184,7 +184,7 @@ func TestBadProposal(t *testing.T) {
height, round := cs1.Height, cs1.Round
vs2 := vss[1]
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
voteCh := subscribe(cs1.eventBus, types.EventQueryVote)
@ -339,7 +339,7 @@ func TestLockNoPOL(t *testing.T) {
vs2 := vss[1]
height := cs1.Height
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
@ -507,7 +507,7 @@ func TestLockPOLRelock(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
@ -622,7 +622,7 @@ func TestLockPOLUnlock(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
@ -719,7 +719,7 @@ func TestLockPOLSafety1(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
@ -842,7 +842,7 @@ func TestLockPOLSafety2(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
@ -1021,7 +1021,7 @@ func TestHalt1(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := cs1.state.Params.BlockPartSizeBytes
partSize := cs1.state.ConsensusParams.BlockPartSizeBytes
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)


+ 1
- 1
consensus/wal.go View File

@ -18,7 +18,7 @@ import (
)
const (
// must be greater than params.BlockGossipParams.BlockPartSizeBytes + a few bytes
// must be greater than params.BlockGossip.BlockPartSizeBytes + a few bytes
maxMsgSizeBytes = 1024 * 1024 // 1MB
)


+ 2
- 3
consensus/wal_generator.go View File

@ -35,7 +35,6 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
logger := log.TestingLogger().With("wal_generator", "wal_generator")
logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks)
/////////////////////////////////////////////////////////////////////////////
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
// NOTE: we can't import node package because of circular dependency
@ -95,7 +94,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
wr.Flush()
return b.Bytes(), nil
case <-time.After(1 * time.Minute):
wr.Flush()
wr.Flush()
return b.Bytes(), fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks)
}
}
@ -147,7 +146,7 @@ type byteBufferWAL struct {
heightToStop int64
signalWhenStopsTo chan<- struct{}
logger log.Logger
logger log.Logger
}
// needed for determinism


+ 10
- 9
docs/architecture/adr-005-consensus-params.md View File

@ -22,30 +22,30 @@ The parameters are used to determine the validity of a block (and tx) via the un
```
type ConsensusParams struct {
BlockSizeParams
TxSizeParams
BlockGossipParams
BlockSize
TxSize
BlockGossip
}
type BlockSizeParams struct {
type BlockSize struct {
MaxBytes int
MaxTxs int
MaxGas int
}
type TxSizeParams struct {
type TxSize struct {
MaxBytes int
MaxGas int
}
type BlockGossipParams struct {
type BlockGossip struct {
BlockPartSizeBytes int
}
```
The `ConsensusParams` can evolve over time by adding new structs that cover different aspects of the consensus rules.
The `BlockPartSizeBytes` and the `BlockSizeParams.MaxBytes` are enforced to be greater than 0.
The `BlockPartSizeBytes` and the `BlockSize.MaxBytes` are enforced to be greater than 0.
The former because we need a part size, the latter so that we always have at least some sanity check over the size of blocks.
### ABCI
@ -58,7 +58,7 @@ like the BlockPartSize, that the app shouldn't really know about.
#### EndBlock
The EndBlock response includes a `ConsensusParams`, which includes BlockSizeParams and TxSizeParams, but not BlockGossipParams.
The EndBlock response includes a `ConsensusParams`, which includes BlockSize and TxSize, but not BlockGossip.
Other param struct can be added to `ConsensusParams` in the future.
The `0` value is used to denote no change.
Any other value will update that parameter in the `State.ConsensusParams`, to be applied for the next block.
@ -82,4 +82,5 @@ Proposed.
### Neutral
- The TxSizeParams, which checks validity, may be in conflict with the config's `max_block_size_tx`, which determines proposal sizes
- The TxSize, which checks validity, may be in conflict with the config's `max_block_size_tx`, which determines proposal sizes

+ 19
- 17
glide.lock View File

@ -1,5 +1,5 @@
hash: f420f1f858100218dad50997d939eaaf129ff654a0648a47ddc60d626ab0b8e9
updated: 2017-12-10T05:37:46.41123196Z
hash: bd982742a0aee39426f3866914a59f7c47e68dc8dedb87219e2099c0d9c19f7e
updated: 2017-12-21T17:45:25.372327218Z
imports:
- name: github.com/btcsuite/btcd
version: 2e60448ffcc6bf78332d1fe590260095f554dd78
@ -10,7 +10,7 @@ imports:
- name: github.com/fsnotify/fsnotify
version: 4da3e2cfbabc9f751898f250b49f2439785783a1
- name: github.com/go-kit/kit
version: e3b2152e0063c5f05efea89ecbe297852af2a92d
version: 953e747656a7bbb5e1f998608b460458958b70cc
subpackages:
- log
- log/level
@ -64,17 +64,19 @@ imports:
- name: github.com/kr/logfmt
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
- name: github.com/magiconair/properties
version: 49d762b9817ba1c2e9d0c69183c2b4a8b8f1d934
version: 8d7837e64d3c1ee4e54a880c5a920ab4316fc90a
- name: github.com/mitchellh/mapstructure
version: 06020f85339e21b2478f756a78e295255ffa4d6a
- name: github.com/pelletier/go-buffruneio
version: c37440a7cf42ac63b919c752ca73a85067e05992
- name: github.com/pelletier/go-toml
version: 4e9e0ee19b60b13eb79915933f44d8ed5f268bdd
version: b8b5e7696574464b2f9bf303a7b37781bb52889f
- name: github.com/pkg/errors
version: 645ef00459ed84a119197bfb8d8205042c6df63d
- name: github.com/rcrowley/go-metrics
version: e181e095bae94582363434144c61a9653aff6e50
version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c
- name: github.com/spf13/afero
version: 8d919cbe7e2627e417f3e45c3c0e489a5b7e2536
version: 5660eeed305fe5f69c8fc6cf899132a459a97064
subpackages:
- mem
- name: github.com/spf13/cast
@ -88,7 +90,7 @@ imports:
- name: github.com/spf13/viper
version: 25b30aa063fc18e48662b86996252eabdcf2f0c7
- name: github.com/syndtr/goleveldb
version: adf24ef3f94bd13ec4163060b21a5678f22b429b
version: 34011bf325bce385408353a30b101fe5e923eb6e
subpackages:
- leveldb
- leveldb/cache
@ -103,7 +105,7 @@ imports:
- leveldb/table
- leveldb/util
- name: github.com/tendermint/abci
version: fca2b508c185b855af1446ec4afc19bdfc7b315d
version: e4b9f1abe794a2117a59738a1294e09b46d0fa00
subpackages:
- client
- example/code
@ -129,7 +131,7 @@ imports:
subpackages:
- iavl
- name: github.com/tendermint/tmlibs
version: a483e1ff486b577ba94e6a20f08bf52fbb7bff14
version: e236218516289f91bde3273e99fe5c1fc20eb304
subpackages:
- autofile
- cli
@ -144,7 +146,7 @@ imports:
- pubsub/query
- test
- name: golang.org/x/crypto
version: 94eea52f7b742c7cbe0b03b22f0c4c8631ece122
version: d585fd2cc9195196078f516b69daff6744ef5e84
subpackages:
- curve25519
- nacl/box
@ -155,7 +157,7 @@ imports:
- ripemd160
- salsa20/salsa
- name: golang.org/x/net
version: a8b9294777976932365dabb6640cf1468d95c70f
version: d866cfc389cec985d6fda2859936a575a55a3ab6
subpackages:
- context
- http2
@ -165,18 +167,18 @@ imports:
- lex/httplex
- trace
- name: golang.org/x/sys
version: 8b4580aae2a0dd0c231a45d3ccb8434ff533b840
version: d818ba11af4465e00c1998bd3f8a55603b422290
subpackages:
- unix
- name: golang.org/x/text
version: 75cc3cad82b5f47d3fb229ddda8c5167da14f294
version: eb22672bea55af56d225d4e35405f4d2e9f062a0
subpackages:
- secure/bidirule
- transform
- unicode/bidi
- unicode/norm
- name: google.golang.org/genproto
version: 7f0da29060c682909f650ad8ed4e515bd74fa12a
version: a8101f21cf983e773d0c1133ebc5424792003214
subpackages:
- googleapis/rpc/status
- name: google.golang.org/grpc
@ -199,9 +201,9 @@ imports:
- tap
- transport
- name: gopkg.in/go-playground/validator.v9
version: 61caf9d3038e1af346dbf5c2e16f6678e1548364
version: b1f51f36f1c98cc97f777d6fc9d4b05eaa0cabb5
- name: gopkg.in/yaml.v2
version: 287cf08546ab5e7e37d55a84f7ed3fd1db036de5
version: eb3733d160e74a9c7e442f435eb3bea458e1d19f
testImports:
- name: github.com/davecgh/go-spew
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9


+ 2
- 2
glide.yaml View File

@ -18,7 +18,7 @@ import:
- package: github.com/spf13/viper
version: v1.0.0
- package: github.com/tendermint/abci
version: ~v0.8.0
version: develop
subpackages:
- client
- example/dummy
@ -34,7 +34,7 @@ import:
subpackages:
- iavl
- package: github.com/tendermint/tmlibs
version: e4ef2835f0081c2ece83b9c1f777cf071f956e81
version: develop
subpackages:
- autofile
- cli


+ 3
- 3
lite/dynamic_test.go View File

@ -46,7 +46,7 @@ func TestDynamicCert(t *testing.T) {
for _, tc := range cases {
check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals,
[]byte("bar"), tc.first, tc.last)
[]byte("bar"), []byte("params"), tc.first, tc.last)
err := cert.Certify(check)
if tc.proper {
assert.Nil(err, "%+v", err)
@ -71,7 +71,7 @@ func TestDynamicUpdate(t *testing.T) {
// one valid block to give us a sense of time
h := int64(100)
good := keys.GenCommit(chainID, h, nil, vals, []byte("foo"), 0, len(keys))
good := keys.GenCommit(chainID, h, nil, vals, []byte("foo"), []byte("params"), 0, len(keys))
err := cert.Certify(good)
require.Nil(err, "%+v", err)
@ -109,7 +109,7 @@ func TestDynamicUpdate(t *testing.T) {
for _, tc := range cases {
fc := tc.keys.GenFullCommit(chainID, tc.height, nil, tc.vals,
[]byte("bar"), tc.first, tc.last)
[]byte("bar"), []byte("params"), tc.first, tc.last)
err := cert.Update(fc)
if tc.proper {
assert.Nil(err, "%d: %+v", tc.height, err)


+ 1
- 1
lite/files/commit_test.go View File

@ -29,7 +29,7 @@ func TestSerializeFullCommits(t *testing.T) {
// build a fc
keys := lite.GenValKeys(5)
vals := keys.ToValidators(10, 0)
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, 0, 5)
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
require.Equal(h, fc.Height())
require.Equal(vals.Hash(), fc.ValidatorsHash())


+ 1
- 1
lite/files/provider_test.go View File

@ -46,7 +46,7 @@ func TestFileProvider(t *testing.T) {
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
vals := keys.ToValidators(10, int64(count/2))
h := int64(20 + 10*i)
check := keys.GenCommit(chainID, h, nil, vals, appHash, 0, 5)
check := keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
seeds[i] = lite.NewFullCommit(check, vals)
}


+ 6
- 5
lite/helpers.go View File

@ -110,7 +110,7 @@ func makeVote(header *types.Header, vals *types.ValidatorSet, key crypto.PrivKey
// Silences warning that vals can also be merkle.Hashable
// nolint: interfacer
func genHeader(chainID string, height int64, txs types.Txs,
vals *types.ValidatorSet, appHash []byte) *types.Header {
vals *types.ValidatorSet, appHash, consHash []byte) *types.Header {
return &types.Header{
ChainID: chainID,
@ -123,14 +123,15 @@ func genHeader(chainID string, height int64, txs types.Txs,
ValidatorsHash: vals.Hash(),
DataHash: txs.Hash(),
AppHash: appHash,
ConsensusHash: consHash,
}
}
// GenCommit calls genHeader and signHeader and combines them into a Commit.
func (v ValKeys) GenCommit(chainID string, height int64, txs types.Txs,
vals *types.ValidatorSet, appHash []byte, first, last int) Commit {
vals *types.ValidatorSet, appHash, consHash []byte, first, last int) Commit {
header := genHeader(chainID, height, txs, vals, appHash)
header := genHeader(chainID, height, txs, vals, appHash, consHash)
check := Commit{
Header: header,
Commit: v.signHeader(header, first, last),
@ -140,9 +141,9 @@ func (v ValKeys) GenCommit(chainID string, height int64, txs types.Txs,
// GenFullCommit calls genHeader and signHeader and combines them into a Commit.
func (v ValKeys) GenFullCommit(chainID string, height int64, txs types.Txs,
vals *types.ValidatorSet, appHash []byte, first, last int) FullCommit {
vals *types.ValidatorSet, appHash, consHash []byte, first, last int) FullCommit {
header := genHeader(chainID, height, txs, vals, appHash)
header := genHeader(chainID, height, txs, vals, appHash, consHash)
commit := Commit{
Header: header,
Commit: v.signHeader(header, first, last),


+ 6
- 3
lite/inquirer_test.go View File

@ -22,6 +22,7 @@ func TestInquirerValidPath(t *testing.T) {
// construct a bunch of commits, each with one more height than the last
chainID := "inquiry-test"
consHash := []byte("params")
count := 50
commits := make([]lite.FullCommit, count)
for i := 0; i < count; i++ {
@ -30,7 +31,7 @@ func TestInquirerValidPath(t *testing.T) {
vals := keys.ToValidators(vote, 0)
h := int64(20 + 10*i)
appHash := []byte(fmt.Sprintf("h=%d", h))
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, 0, len(keys))
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, 0, len(keys))
}
// initialize a certifier with the initial state
@ -69,6 +70,7 @@ func TestInquirerMinimalPath(t *testing.T) {
// construct a bunch of commits, each with one more height than the last
chainID := "minimal-path"
consHash := []byte("other-params")
count := 12
commits := make([]lite.FullCommit, count)
for i := 0; i < count; i++ {
@ -77,7 +79,7 @@ func TestInquirerMinimalPath(t *testing.T) {
vals := keys.ToValidators(vote, 0)
h := int64(5 + 10*i)
appHash := []byte(fmt.Sprintf("h=%d", h))
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, 0, len(keys))
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, 0, len(keys))
}
// initialize a certifier with the initial state
@ -117,6 +119,7 @@ func TestInquirerVerifyHistorical(t *testing.T) {
// construct a bunch of commits, each with one more height than the last
chainID := "inquiry-test"
count := 10
consHash := []byte("special-params")
commits := make([]lite.FullCommit, count)
for i := 0; i < count; i++ {
// extend the keys by 1 each time
@ -124,7 +127,7 @@ func TestInquirerVerifyHistorical(t *testing.T) {
vals := keys.ToValidators(vote, 0)
h := int64(20 + 10*i)
appHash := []byte(fmt.Sprintf("h=%d", h))
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, 0, len(keys))
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, 0, len(keys))
}
// initialize a certifier with the initial state


+ 2
- 2
lite/performance_test.go View File

@ -33,7 +33,7 @@ func benchmarkGenCommit(b *testing.B, keys lite.ValKeys) {
for i := 0; i < b.N; i++ {
h := int64(1 + i)
appHash := []byte(fmt.Sprintf("h=%d", h))
keys.GenCommit(chainID, h, nil, vals, appHash, 0, len(keys))
keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, len(keys))
}
}
@ -105,7 +105,7 @@ func benchmarkCertifyCommit(b *testing.B, keys lite.ValKeys) {
chainID := "bench-certify"
vals := keys.ToValidators(20, 10)
cert := lite.NewStatic(chainID, vals)
check := keys.GenCommit(chainID, 123, nil, vals, []byte("foo"), 0, len(keys))
check := keys.GenCommit(chainID, 123, nil, vals, []byte("foo"), []byte("params"), 0, len(keys))
for i := 0; i < b.N; i++ {
err := cert.Certify(check)
if err != nil {


+ 2
- 2
lite/provider_test.go View File

@ -58,7 +58,7 @@ func checkProvider(t *testing.T, p lite.Provider, chainID, app string) {
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
vals := keys.ToValidators(10, int64(count/2))
h := int64(20 + 10*i)
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, 0, 5)
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
}
// check provider is empty
@ -129,7 +129,7 @@ func TestCacheGetsBestHeight(t *testing.T) {
for i := 0; i < count; i++ {
vals := keys.ToValidators(10, int64(count/2))
h := int64(10 * (i + 1))
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, 0, 5)
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
err := p2.StoreCommit(fc)
require.NoError(err)
}


+ 1
- 1
lite/static_test.go View File

@ -44,7 +44,7 @@ func TestStaticCert(t *testing.T) {
for _, tc := range cases {
check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals,
[]byte("foo"), tc.first, tc.last)
[]byte("foo"), []byte("params"), tc.first, tc.last)
err := cert.Certify(check)
if tc.proper {
assert.Nil(err, "%+v", err)


+ 3
- 4
rpc/test/helpers.go View File

@ -3,7 +3,6 @@ package rpctest
import (
"context"
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
@ -11,6 +10,8 @@ import (
"github.com/tendermint/tmlibs/log"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
cfg "github.com/tendermint/tendermint/config"
nm "github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/proxy"
@ -57,9 +58,7 @@ func makePathname() string {
}
func randPort() int {
// returns between base and base + spread
base, spread := 20000, 20000
return base + rand.Intn(spread)
return int(cmn.RandUint16()/2 + 10000)
}
func makeAddrs() (string, string, string) {


+ 8
- 0
state/errors.go View File

@ -37,6 +37,10 @@ type (
ErrNoValSetForHeight struct {
Height int64
}
ErrNoConsensusParamsForHeight struct {
Height int64
}
)
func (e ErrUnknownBlock) Error() string {
@ -61,3 +65,7 @@ func (e ErrStateMismatch) Error() string {
func (e ErrNoValSetForHeight) Error() string {
return cmn.Fmt("Could not find validator set for height #%d", e.Height)
}
func (e ErrNoConsensusParamsForHeight) Error() string {
return cmn.Fmt("Could not find consensus params for height #%d", e.Height)
}

+ 60
- 13
state/execution.go View File

@ -1,6 +1,7 @@
package state
import (
"bytes"
"errors"
"fmt"
@ -111,11 +112,11 @@ func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn p
return nil, err
}
valDiff := abciResponses.EndBlock.Diffs
valUpdates := abciResponses.EndBlock.ValidatorUpdates
logger.Info("Executed block", "height", block.Height, "validTxs", validTxs, "invalidTxs", invalidTxs)
if len(valDiff) > 0 {
logger.Info("Update to validator set", "updates", abci.ValidatorsString(valDiff))
if len(valUpdates) > 0 {
logger.Info("Updates to validators", "updates", abci.ValidatorsString(valUpdates))
}
return abciResponses, nil
@ -184,26 +185,69 @@ func (s *State) ValidateBlock(block *types.Block) error {
return s.validateBlock(block)
}
func (s *State) validateBlock(block *types.Block) error {
// MakeBlock builds a block with the given txs and commit from the current state.
func (s *State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) {
// build base block
block := types.MakeBlock(height, txs, commit)
// fill header with state data
block.ChainID = s.ChainID
block.TotalTxs = s.LastBlockTotalTx + block.NumTxs
block.LastBlockID = s.LastBlockID
block.ValidatorsHash = s.Validators.Hash()
block.AppHash = s.AppHash
block.ConsensusHash = s.LastConsensusParams.Hash()
return block, block.MakePartSet(s.ConsensusParams.BlockGossip.BlockPartSizeBytes)
}
func (s *State) validateBlock(b *types.Block) error {
// Basic block validation.
err := block.ValidateBasic(s.ChainID, s.LastBlockHeight,
s.LastBlockTotalTx, s.LastBlockID, s.LastBlockTime, s.AppHash)
if err != nil {
if err := b.ValidateBasic(); err != nil {
return err
}
if b.ChainID != s.ChainID {
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v", s.ChainID, b.ChainID)
}
if b.Height != s.LastBlockHeight+1 {
return fmt.Errorf("Wrong Block.Header.Height. Expected %v, got %v", s.LastBlockHeight+1, b.Height)
}
/* TODO: Determine bounds for Time
See blockchain/reactor "stopSyncingDurationMinutes"
if !b.Time.After(lastBlockTime) {
return errors.New("Invalid Block.Header.Time")
}
*/
newTxs := int64(len(b.Data.Txs))
if b.TotalTxs != s.LastBlockTotalTx+newTxs {
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", s.LastBlockTotalTx+newTxs, b.TotalTxs)
}
if !b.LastBlockID.Equals(s.LastBlockID) {
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", s.LastBlockID, b.LastBlockID)
}
if !bytes.Equal(b.AppHash, s.AppHash) {
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v", s.AppHash, b.AppHash)
}
if !bytes.Equal(b.ConsensusHash, s.LastConsensusParams.Hash()) {
return fmt.Errorf("Wrong Block.Header.ConsensusHash. Expected %X, got %v", s.LastConsensusParams.Hash(), b.ConsensusHash)
}
// Validate block LastCommit.
if block.Height == 1 {
if len(block.LastCommit.Precommits) != 0 {
if b.Height == 1 {
if len(b.LastCommit.Precommits) != 0 {
return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
}
} else {
if len(block.LastCommit.Precommits) != s.LastValidators.Size() {
if len(b.LastCommit.Precommits) != s.LastValidators.Size() {
return errors.New(cmn.Fmt("Invalid block commit size. Expected %v, got %v",
s.LastValidators.Size(), len(block.LastCommit.Precommits)))
s.LastValidators.Size(), len(b.LastCommit.Precommits)))
}
err := s.LastValidators.VerifyCommit(
s.ChainID, s.LastBlockID, block.Height-1, block.LastCommit)
s.ChainID, s.LastBlockID, b.Height-1, b.LastCommit)
if err != nil {
return err
}
@ -235,7 +279,10 @@ func (s *State) ApplyBlock(txEventPublisher types.TxEventPublisher, proxyAppConn
fail.Fail() // XXX
// now update the block and validators
s.SetBlockAndValidators(block.Header, partsHeader, abciResponses)
err = s.SetBlockAndValidators(block.Header, partsHeader, abciResponses)
if err != nil {
return fmt.Errorf("Commit failed for application: %v", err)
}
// lock mempool, commit state, update mempoool
err = s.CommitStateUpdateMempool(proxyAppConn, block, mempool)


+ 50
- 14
state/execution_test.go View File

@ -23,6 +23,52 @@ var (
nTxsPerBlock = 10
)
func TestValidateBlock(t *testing.T) {
state := state()
state.SetLogger(log.TestingLogger())
// proper block must pass
block := makeBlock(state, 1)
err := state.ValidateBlock(block)
require.NoError(t, err)
// wrong chain fails
block = makeBlock(state, 1)
block.ChainID = "not-the-real-one"
err = state.ValidateBlock(block)
require.Error(t, err)
// wrong height fails
block = makeBlock(state, 1)
block.Height += 10
err = state.ValidateBlock(block)
require.Error(t, err)
// wrong total tx fails
block = makeBlock(state, 1)
block.TotalTxs += 10
err = state.ValidateBlock(block)
require.Error(t, err)
// wrong blockid fails
block = makeBlock(state, 1)
block.LastBlockID.PartsHeader.Total += 10
err = state.ValidateBlock(block)
require.Error(t, err)
// wrong app hash fails
block = makeBlock(state, 1)
block.AppHash = []byte("wrong app hash")
err = state.ValidateBlock(block)
require.Error(t, err)
// wrong consensus hash fails
block = makeBlock(state, 1)
block.ConsensusHash = []byte("wrong consensus hash")
err = state.ValidateBlock(block)
require.Error(t, err)
}
func TestApplyBlock(t *testing.T) {
cc := proxy.NewLocalClientCreator(dummy.NewDummyApplication())
proxyApp := proxy.NewAppConns(cc, nil)
@ -33,7 +79,7 @@ func TestApplyBlock(t *testing.T) {
state := state()
state.SetLogger(log.TestingLogger())
block := makeBlock(1, state)
block := makeBlock(state, 1)
err = state.ApplyBlock(types.NopEventBus{}, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), types.MockMempool{})
require.Nil(t, err)
@ -79,10 +125,7 @@ func TestBeginBlockAbsentValidators(t *testing.T) {
for _, tc := range testCases {
lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: tc.lastCommitPrecommits}
valHash := state.Validators.Hash()
block, _ := types.MakeBlock(2, chainID, makeTxs(2), state.LastBlockTotalTx, lastCommit,
prevBlockID, valHash, state.AppHash, testPartSize)
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit)
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), lastValidators)
require.Nil(t, err, tc.desc)
@ -112,15 +155,8 @@ func state() *State {
return s
}
func makeBlock(height int64, state *State) *types.Block {
prevHash := state.LastBlockID.Hash
prevParts := types.PartSetHeader{}
valHash := state.Validators.Hash()
prevBlockID := types.BlockID{prevHash, prevParts}
block, _ := types.MakeBlock(height, chainID,
makeTxs(height), state.LastBlockTotalTx,
new(types.Commit), prevBlockID, valHash,
state.AppHash, testPartSize)
func makeBlock(state *State, height int64) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit))
return block
}


+ 154
- 40
state/state.go View File

@ -18,6 +18,7 @@ import (
"github.com/tendermint/tendermint/types"
)
// database keys
var (
stateKey = []byte("stateKey")
abciResponsesKey = []byte("abciResponsesKey")
@ -27,37 +28,50 @@ func calcValidatorsKey(height int64) []byte {
return []byte(cmn.Fmt("validatorsKey:%v", height))
}
func calcConsensusParamsKey(height int64) []byte {
return []byte(cmn.Fmt("consensusParamsKey:%v", height))
}
//-----------------------------------------------------------------------------
// State represents the latest committed state of the Tendermint consensus,
// including the last committed block and validator set.
// Newly committed blocks are validated and executed against the State.
// State is a short description of the latest committed block of the Tendermint consensus.
// It keeps all information necessary to validate new blocks,
// including the last validator set and the consensus params.
// All fields are exposed so the struct can be easily serialized,
// but the fields should only be changed by calling state.SetBlockAndValidators.
// NOTE: not goroutine-safe.
type State struct {
// mtx for writing to db
mtx sync.Mutex
db dbm.DB
// Immutable
ChainID string
// Consensus parameters used for validating blocks
Params types.ConsensusParams
// These fields are updated by SetBlockAndValidators.
// Exposed fields are updated by SetBlockAndValidators.
// LastBlockHeight=0 at genesis (ie. block(H=0) does not exist)
// LastValidators is used to validate block.LastCommit.
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,
// the change only applies to the next block.
// So, if s.LastBlockHeight causes a valset change,
// LastValidators is used to validate block.LastCommit.
// Validators are persisted to the database separately every time they change,
// so we can query for historical validator sets.
// Note that if s.LastBlockHeight causes a valset change,
// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1
Validators *types.ValidatorSet
LastValidators *types.ValidatorSet
LastHeightValidatorsChanged int64
// AppHash is updated after Commit
// Consensus parameters used for validating blocks.
// Changes returned by EndBlock and updated after Commit.
ConsensusParams types.ConsensusParams
LastConsensusParams types.ConsensusParams
LastHeightConsensusParamsChanged int64
// The latest AppHash we've received from calling abci.Commit()
AppHash []byte
logger log.Logger
@ -112,18 +126,26 @@ func (s *State) SetLogger(l log.Logger) {
// Copy makes a copy of the State for mutating.
func (s *State) Copy() *State {
return &State{
db: s.db,
LastBlockHeight: s.LastBlockHeight,
LastBlockTotalTx: s.LastBlockTotalTx,
LastBlockID: s.LastBlockID,
LastBlockTime: s.LastBlockTime,
db: s.db,
ChainID: s.ChainID,
LastBlockHeight: s.LastBlockHeight,
LastBlockTotalTx: s.LastBlockTotalTx,
LastBlockID: s.LastBlockID,
LastBlockTime: s.LastBlockTime,
Validators: s.Validators.Copy(),
LastValidators: s.LastValidators.Copy(),
AppHash: s.AppHash,
LastHeightValidatorsChanged: s.LastHeightValidatorsChanged,
logger: s.logger,
ChainID: s.ChainID,
Params: s.Params,
ConsensusParams: s.ConsensusParams,
LastConsensusParams: s.LastConsensusParams,
LastHeightConsensusParamsChanged: s.LastHeightConsensusParamsChanged,
AppHash: s.AppHash,
logger: s.logger,
}
}
@ -133,6 +155,7 @@ func (s *State) Save() {
defer s.mtx.Unlock()
s.saveValidatorsInfo()
s.saveConsensusParamsInfo()
s.db.SetSync(stateKey, s.Bytes())
}
@ -166,13 +189,13 @@ func (s *State) LoadABCIResponses() *ABCIResponses {
// LoadValidators loads the ValidatorSet for a given height.
func (s *State) LoadValidators(height int64) (*types.ValidatorSet, error) {
valInfo := s.loadValidators(height)
valInfo := s.loadValidatorsInfo(height)
if valInfo == nil {
return nil, ErrNoValSetForHeight{height}
}
if valInfo.ValidatorSet == nil {
valInfo = s.loadValidators(valInfo.LastHeightChanged)
valInfo = s.loadValidatorsInfo(valInfo.LastHeightChanged)
if valInfo == nil {
cmn.PanicSanity(fmt.Sprintf(`Couldn't find validators at height %d as
last changed from height %d`, valInfo.LastHeightChanged, height))
@ -182,7 +205,7 @@ func (s *State) LoadValidators(height int64) (*types.ValidatorSet, error) {
return valInfo.ValidatorSet, nil
}
func (s *State) loadValidators(height int64) *ValidatorsInfo {
func (s *State) loadValidatorsInfo(height int64) *ValidatorsInfo {
buf := s.db.Get(calcValidatorsKey(height))
if len(buf) == 0 {
return nil
@ -217,6 +240,61 @@ func (s *State) saveValidatorsInfo() {
s.db.SetSync(calcValidatorsKey(nextHeight), valInfo.Bytes())
}
// LoadConsensusParams loads the ConsensusParams for a given height.
func (s *State) LoadConsensusParams(height int64) (types.ConsensusParams, error) {
empty := types.ConsensusParams{}
paramsInfo := s.loadConsensusParamsInfo(height)
if paramsInfo == nil {
return empty, ErrNoConsensusParamsForHeight{height}
}
if paramsInfo.ConsensusParams == empty {
paramsInfo = s.loadConsensusParamsInfo(paramsInfo.LastHeightChanged)
if paramsInfo == nil {
cmn.PanicSanity(fmt.Sprintf(`Couldn't find consensus params at height %d as
last changed from height %d`, paramsInfo.LastHeightChanged, height))
}
}
return paramsInfo.ConsensusParams, nil
}
func (s *State) loadConsensusParamsInfo(height int64) *ConsensusParamsInfo {
buf := s.db.Get(calcConsensusParamsKey(height))
if len(buf) == 0 {
return nil
}
paramsInfo := new(ConsensusParamsInfo)
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(paramsInfo, r, 0, n, err)
if *err != nil {
// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
cmn.Exit(cmn.Fmt(`LoadConsensusParams: Data has been corrupted or its spec has changed:
%v\n`, *err))
}
// TODO: ensure that buf is completely read.
return paramsInfo
}
// saveConsensusParamsInfo persists the consensus params for the next block to disk.
// It should be called from s.Save(), right before the state itself is persisted.
// If the consensus params did not change after processing the latest block,
// only the last height for which they changed is persisted.
func (s *State) saveConsensusParamsInfo() {
changeHeight := s.LastHeightConsensusParamsChanged
nextHeight := s.LastBlockHeight + 1
paramsInfo := &ConsensusParamsInfo{
LastHeightChanged: changeHeight,
}
if changeHeight == nextHeight {
paramsInfo.ConsensusParams = s.ConsensusParams
}
s.db.SetSync(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes())
}
// Equals returns true if the States are identical.
func (s *State) Equals(s2 *State) bool {
return bytes.Equal(s.Bytes(), s2.Bytes())
@ -230,7 +308,7 @@ func (s *State) Bytes() []byte {
// SetBlockAndValidators mutates State variables
// to update block and validators after running EndBlock.
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader,
abciResponses *ABCIResponses) {
abciResponses *ABCIResponses) error {
// copy the valset so we can apply changes from EndBlock
// and update s.LastValidators and s.Validators
@ -238,11 +316,10 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ
nextValSet := prevValSet.Copy()
// update the validator set with the latest abciResponses
if len(abciResponses.EndBlock.Diffs) > 0 {
err := updateValidators(nextValSet, abciResponses.EndBlock.Diffs)
if len(abciResponses.EndBlock.ValidatorUpdates) > 0 {
err := updateValidators(nextValSet, abciResponses.EndBlock.ValidatorUpdates)
if err != nil {
s.logger.Error("Error changing validator set", "err", err)
// TODO: err or carry on?
return fmt.Errorf("Error changing validator set: %v", err)
}
// change results from this height but only applies to the next height
s.LastHeightValidatorsChanged = header.Height + 1
@ -251,24 +328,43 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ
// Update validator accums and set state variables
nextValSet.IncrementAccum(1)
// update the params with the latest abciResponses
nextParams := s.ConsensusParams
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
// NOTE: must not mutate s.ConsensusParams
nextParams = s.ConsensusParams.Update(abciResponses.EndBlock.ConsensusParamUpdates)
err := nextParams.Validate()
if err != nil {
return fmt.Errorf("Error updating consensus params: %v", err)
}
// change results from this height but only applies to the next height
s.LastHeightConsensusParamsChanged = header.Height + 1
}
s.setBlockAndValidators(header.Height,
header.NumTxs,
types.BlockID{header.Hash(), blockPartsHeader},
header.Time,
prevValSet, nextValSet)
nextValSet,
nextParams)
return nil
}
func (s *State) setBlockAndValidators(height int64,
newTxs int64, blockID types.BlockID, blockTime time.Time,
prevValSet, nextValSet *types.ValidatorSet) {
valSet *types.ValidatorSet,
params types.ConsensusParams) {
s.LastBlockHeight = height
s.LastBlockTotalTx += newTxs
s.LastBlockID = blockID
s.LastBlockTime = blockTime
s.Validators = nextValSet
s.LastValidators = prevValSet
s.LastValidators = s.Validators.Copy()
s.Validators = valSet
s.LastConsensusParams = s.ConsensusParams
s.ConsensusParams = params
}
// GetValidators returns the last and current validator sets.
@ -316,6 +412,19 @@ func (valInfo *ValidatorsInfo) Bytes() []byte {
return wire.BinaryBytes(*valInfo)
}
//-----------------------------------------------------------------------------
// ConsensusParamsInfo represents the latest consensus params, or the last height it changed
type ConsensusParamsInfo struct {
ConsensusParams types.ConsensusParams
LastHeightChanged int64
}
// Bytes serializes the ConsensusParamsInfo using go-wire
func (params ConsensusParamsInfo) Bytes() []byte {
return wire.BinaryBytes(params)
}
//------------------------------------------------------------------------
// Genesis
@ -369,14 +478,19 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) {
db: db,
ChainID: genDoc.ChainID,
Params: *genDoc.ConsensusParams,
LastBlockHeight: 0,
LastBlockID: types.BlockID{},
LastBlockTime: genDoc.GenesisTime,
LastBlockHeight: 0,
LastBlockID: types.BlockID{},
LastBlockTime: genDoc.GenesisTime,
Validators: types.NewValidatorSet(validators),
LastValidators: types.NewValidatorSet(nil),
AppHash: genDoc.AppHash,
LastHeightValidatorsChanged: 1,
ConsensusParams: *genDoc.ConsensusParams,
LastConsensusParams: types.ConsensusParams{},
LastHeightConsensusParamsChanged: 1,
AppHash: genDoc.AppHash,
}, nil
}

+ 145
- 5
state/state_test.go View File

@ -76,11 +76,11 @@ func TestABCIResponsesSaveLoad(t *testing.T) {
state.LastBlockHeight++
// build mock responses
block := makeBlock(2, state)
block := makeBlock(state, 2)
abciResponses := NewABCIResponses(block)
abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Tags: []*abci.KVPair{}}
abciResponses.DeliverTx[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Tags: []*abci.KVPair{}}
abciResponses.EndBlock = &abci.ResponseEndBlock{Diffs: []*abci.Validator{
abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{
{
PubKey: crypto.GenPrivKeyEd25519().PubKey().Bytes(),
Power: 10,
@ -192,20 +192,144 @@ func TestValidatorChangesSaveLoad(t *testing.T) {
}
}
// TestConsensusParamsChangesSaveLoad tests saving and loading consensus params with changes.
func TestConsensusParamsChangesSaveLoad(t *testing.T) {
tearDown, _, state := setupTestCase(t)
defer tearDown(t)
// nolint: vetshadow
assert := assert.New(t)
// change vals at these heights
changeHeights := []int64{1, 2, 4, 5, 10, 15, 16, 17, 20}
N := len(changeHeights)
// each valset is just one validator.
// create list of them
params := make([]types.ConsensusParams, N+1)
params[0] = state.ConsensusParams
for i := 1; i < N+1; i++ {
params[i] = *types.DefaultConsensusParams()
params[i].BlockSize.MaxBytes += i
}
// build the params history by running SetBlockAndValidators
// with the right params set for each height
highestHeight := changeHeights[N-1] + 5
changeIndex := 0
cp := params[changeIndex]
for i := int64(1); i < highestHeight; i++ {
// when we get to a change height,
// use the next params
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex] {
changeIndex++
cp = params[changeIndex]
}
header, parts, responses := makeHeaderPartsResponsesParams(state, i, cp)
state.SetBlockAndValidators(header, parts, responses)
state.saveConsensusParamsInfo()
}
// make all the test cases by using the same params until after the change
testCases := make([]paramsChangeTestCase, highestHeight)
changeIndex = 0
cp = params[changeIndex]
for i := int64(1); i < highestHeight+1; i++ {
// we we get to the height after a change height
// use the next pubkey (note our counter starts at 0 this time)
if changeIndex < len(changeHeights) && i == changeHeights[changeIndex]+1 {
changeIndex++
cp = params[changeIndex]
}
testCases[i-1] = paramsChangeTestCase{i, cp}
}
for _, testCase := range testCases {
p, err := state.LoadConsensusParams(testCase.height)
assert.Nil(err, fmt.Sprintf("expected no err at height %d", testCase.height))
assert.Equal(testCase.params, p, fmt.Sprintf(`unexpected consensus params at
height %d`, testCase.height))
}
}
func makeParams(blockBytes, blockTx, blockGas, txBytes,
txGas, partSize int) types.ConsensusParams {
return types.ConsensusParams{
BlockSize: types.BlockSize{
MaxBytes: blockBytes,
MaxTxs: blockTx,
MaxGas: int64(blockGas),
},
TxSize: types.TxSize{
MaxBytes: txBytes,
MaxGas: int64(txGas),
},
BlockGossip: types.BlockGossip{
BlockPartSizeBytes: partSize,
},
}
}
func TestApplyUpdates(t *testing.T) {
initParams := makeParams(1, 2, 3, 4, 5, 6)
cases := [...]struct {
init types.ConsensusParams
updates *abci.ConsensusParams
expected types.ConsensusParams
}{
0: {initParams, nil, initParams},
1: {initParams, &abci.ConsensusParams{}, initParams},
2: {initParams,
&abci.ConsensusParams{
TxSize: &abci.TxSize{
MaxBytes: 123,
},
},
makeParams(1, 2, 3, 123, 5, 6)},
3: {initParams,
&abci.ConsensusParams{
BlockSize: &abci.BlockSize{
MaxTxs: 44,
MaxGas: 55,
},
},
makeParams(1, 44, 55, 4, 5, 6)},
4: {initParams,
&abci.ConsensusParams{
BlockSize: &abci.BlockSize{
MaxTxs: 789,
},
TxSize: &abci.TxSize{
MaxGas: 888,
},
BlockGossip: &abci.BlockGossip{
BlockPartSizeBytes: 2002,
},
},
makeParams(1, 789, 3, 4, 888, 2002)},
}
for i, tc := range cases {
res := tc.init.Update(tc.updates)
assert.Equal(t, tc.expected, res, "case %d", i)
}
}
func makeHeaderPartsResponses(state *State, height int64,
pubkey crypto.PubKey) (*types.Header, types.PartSetHeader, *ABCIResponses) {
block := makeBlock(height, state)
block := makeBlock(state, height)
_, val := state.Validators.GetByIndex(0)
abciResponses := &ABCIResponses{
Height: height,
EndBlock: &abci.ResponseEndBlock{Diffs: []*abci.Validator{}},
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{}},
}
// if the pubkey is new, remove the old and add the new
if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) {
abciResponses.EndBlock = &abci.ResponseEndBlock{
Diffs: []*abci.Validator{
ValidatorUpdates: []*abci.Validator{
{val.PubKey.Bytes(), 0},
{pubkey.Bytes(), 10},
},
@ -219,3 +343,19 @@ type valChangeTestCase struct {
height int64
vals crypto.PubKey
}
func makeHeaderPartsResponsesParams(state *State, height int64,
params types.ConsensusParams) (*types.Header, types.PartSetHeader, *ABCIResponses) {
block := makeBlock(state, height)
abciResponses := &ABCIResponses{
Height: height,
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(&params)},
}
return block.Header, types.PartSetHeader{}, abciResponses
}
type paramsChangeTestCase struct {
height int64
params types.ConsensusParams
}

+ 33
- 55
types/block.go View File

@ -15,30 +15,21 @@ import (
)
// Block defines the atomic unit of a Tendermint blockchain.
// TODO: add Version byte
type Block struct {
*Header `json:"header"`
*Data `json:"data"`
LastCommit *Commit `json:"last_commit"`
}
// MakeBlock returns a new block and corresponding partset from the given information.
// TODO: Add version information to the Block struct.
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))
// MakeBlock returns a new block with an empty header, except what can be computed from itself.
// It populates the same set of fields validated by ValidateBasic
func MakeBlock(height int64, txs []Tx, commit *Commit) *Block {
block := &Block{
Header: &Header{
ChainID: chainID,
Height: height,
Time: time.Now(),
NumTxs: newTxs,
TotalTxs: totalTxs + newTxs,
LastBlockID: prevBlockID,
ValidatorsHash: valHash,
AppHash: appHash, // state merkle root of txs from the previous block.
Height: height,
Time: time.Now(),
NumTxs: int64(len(txs)),
},
LastCommit: commit,
Data: &Data{
@ -46,39 +37,18 @@ func MakeBlock(height int64, chainID string, txs []Tx,
},
}
block.FillHeader()
return block, block.MakePartSet(partSize)
return block
}
// ValidateBasic performs basic validation that doesn't involve state data.
func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64,
lastBlockTotalTx int64, lastBlockID BlockID,
lastBlockTime time.Time, appHash []byte) error {
if b.ChainID != chainID {
return errors.New(cmn.Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID))
}
if b.Height != lastBlockHeight+1 {
return errors.New(cmn.Fmt("Wrong Block.Header.Height. Expected %v, got %v", lastBlockHeight+1, b.Height))
}
/* TODO: Determine bounds for Time
See blockchain/reactor "stopSyncingDurationMinutes"
if !b.Time.After(lastBlockTime) {
return errors.New("Invalid Block.Header.Time")
}
*/
// It checks the internal consistency of the block.
func (b *Block) ValidateBasic() error {
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) {
return errors.New(cmn.Fmt("Wrong Block.Header.LastBlockID. Expected %v, got %v", lastBlockID, b.LastBlockID))
return fmt.Errorf("Wrong Block.Header.NumTxs. Expected %v, got %v", newTxs, b.NumTxs)
}
if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) {
return errors.New(cmn.Fmt("Wrong Block.Header.LastCommitHash. Expected %v, got %v", b.LastCommitHash, b.LastCommit.Hash()))
return fmt.Errorf("Wrong Block.Header.LastCommitHash. Expected %v, got %v", b.LastCommitHash, b.LastCommit.Hash())
}
if b.Header.Height != 1 {
if err := b.LastCommit.ValidateBasic(); err != nil {
@ -86,12 +56,8 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int64,
}
}
if !bytes.Equal(b.DataHash, b.Data.Hash()) {
return errors.New(cmn.Fmt("Wrong Block.Header.DataHash. Expected %v, got %v", b.DataHash, b.Data.Hash()))
}
if !bytes.Equal(b.AppHash, appHash) {
return errors.New(cmn.Fmt("Wrong Block.Header.AppHash. Expected %X, got %v", appHash, b.AppHash))
return fmt.Errorf("Wrong Block.Header.DataHash. Expected %v, got %v", b.DataHash, b.Data.Hash())
}
// NOTE: the AppHash and ValidatorsHash are validated later.
return nil
}
@ -167,17 +133,26 @@ func (b *Block) StringShort() string {
//-----------------------------------------------------------------------------
// Header defines the structure of a Tendermint block header
// TODO: limit header size
type Header struct {
ChainID string `json:"chain_id"`
Height int64 `json:"height"`
Time time.Time `json:"time"`
NumTxs int64 `json:"num_txs"` // XXX: Can we get rid of this?
TotalTxs int64 `json:"total_txs"`
LastBlockID BlockID `json:"last_block_id"`
// basic block info
ChainID string `json:"chain_id"`
Height int64 `json:"height"`
Time time.Time `json:"time"`
NumTxs int64 `json:"num_txs"`
// prev block info
LastBlockID BlockID `json:"last_block_id"`
TotalTxs int64 `json:"total_txs"`
// hashes of block data
LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block
DataHash data.Bytes `json:"data_hash"` // transactions
ValidatorsHash data.Bytes `json:"validators_hash"` // validators for the current block
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
// hashes from the app
ValidatorsHash data.Bytes `json:"validators_hash"` // validators for the current block
ConsensusHash data.Bytes `json:"consensus_hash"` // consensus params for current block
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
}
// Hash returns the hash of the header.
@ -197,6 +172,7 @@ func (h *Header) Hash() data.Bytes {
"Data": h.DataHash,
"Validators": h.ValidatorsHash,
"App": h.AppHash,
"Consensus": h.ConsensusHash,
})
}
@ -216,6 +192,7 @@ func (h *Header) StringIndented(indent string) string {
%s Data: %v
%s Validators: %v
%s App: %v
%s Conensus: %v
%s}#%v`,
indent, h.ChainID,
indent, h.Height,
@ -227,6 +204,7 @@ func (h *Header) StringIndented(indent string) string {
indent, h.DataHash,
indent, h.ValidatorsHash,
indent, h.AppHash,
indent, h.ConsensusHash,
indent, h.Hash())
}


+ 26
- 44
types/block_test.go View File

@ -2,81 +2,63 @@ package types
import (
"testing"
"time"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
)
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)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err)
block, _ := MakeBlock(h, "hello", txs, 10, commit,
lastID, valHash, appHash, 2)
block := MakeBlock(h, txs, commit)
require.NotNil(t, block)
// proper block must pass
err = block.ValidateBasic("hello", h-1, 10, lastID, block.Time, appHash)
err = block.ValidateBasic()
require.NoError(t, err)
// wrong chain fails
err = block.ValidateBasic("other", h-1, 10, lastID, block.Time, appHash)
// tamper with NumTxs
block = MakeBlock(h, txs, commit)
block.NumTxs += 1
err = block.ValidateBasic()
require.Error(t, err)
// wrong height fails
err = block.ValidateBasic("hello", h+4, 10, lastID, block.Time, appHash)
// remove 1/2 the commits
block = MakeBlock(h, txs, commit)
block.LastCommit.Precommits = commit.Precommits[:commit.Size()/2]
block.LastCommit.hash = nil // clear hash or change wont be noticed
err = block.ValidateBasic()
require.Error(t, err)
// wrong total tx fails
err = block.ValidateBasic("hello", h-1, 15, lastID, block.Time, appHash)
// tamper with LastCommitHash
block = MakeBlock(h, txs, commit)
block.LastCommitHash = []byte("something else")
err = block.ValidateBasic()
require.Error(t, err)
// wrong blockid fails
err = block.ValidateBasic("hello", h-1, 10, makeBlockID(), block.Time, appHash)
// tamper with data
block = MakeBlock(h, txs, commit)
block.Data.Txs[0] = Tx("something else")
block.Data.hash = nil // clear hash or change wont be noticed
err = block.ValidateBasic()
require.Error(t, err)
// wrong app hash fails
err = block.ValidateBasic("hello", h-1, 10, lastID, block.Time, []byte("bad-hash"))
// tamper with DataHash
block = MakeBlock(h, txs, commit)
block.DataHash = cmn.RandBytes(len(block.DataHash))
err = block.ValidateBasic()
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,
Timestamp: time.Now().UTC(),
}
// 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
}

+ 1
- 1
types/genesis_test.go View File

@ -54,7 +54,7 @@ func TestGenesis(t *testing.T) {
assert.NoError(t, err, "expected no error for valid genDoc json")
// test with invalid consensus params
genDoc.ConsensusParams.BlockSizeParams.MaxBytes = 0
genDoc.ConsensusParams.BlockSize.MaxBytes = 0
genDocBytes, err = json.Marshal(genDoc)
assert.NoError(t, err, "error marshalling genDoc")
genDoc, err = GenesisDocFromJSON(genDocBytes)


+ 88
- 33
types/params.go View File

@ -2,6 +2,9 @@ package types
import (
"github.com/pkg/errors"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/merkle"
)
const (
@ -11,58 +14,58 @@ const (
// ConsensusParams contains consensus critical parameters
// that determine the validity of blocks.
type ConsensusParams struct {
BlockSizeParams `json:"block_size_params"`
TxSizeParams `json:"tx_size_params"`
BlockGossipParams `json:"block_gossip_params"`
BlockSize `json:"block_size_params"`
TxSize `json:"tx_size_params"`
BlockGossip `json:"block_gossip_params"`
}
// BlockSizeParams contain limits on the block size.
type BlockSizeParams struct {
MaxBytes int `json:"max_bytes"` // NOTE: must not be 0 nor greater than 100MB
MaxTxs int `json:"max_txs"`
MaxGas int `json:"max_gas"`
// BlockSize contain limits on the block size.
type BlockSize struct {
MaxBytes int `json:"max_bytes"` // NOTE: must not be 0 nor greater than 100MB
MaxTxs int `json:"max_txs"`
MaxGas int64 `json:"max_gas"`
}
// TxSizeParams contain limits on the tx size.
type TxSizeParams struct {
MaxBytes int `json:"max_bytes"`
MaxGas int `json:"max_gas"`
// TxSize contain limits on the tx size.
type TxSize struct {
MaxBytes int `json:"max_bytes"`
MaxGas int64 `json:"max_gas"`
}
// BlockGossipParams determine consensus critical elements of how blocks are gossiped
type BlockGossipParams struct {
// BlockGossip determine consensus critical elements of how blocks are gossiped
type BlockGossip struct {
BlockPartSizeBytes int `json:"block_part_size_bytes"` // NOTE: must not be 0
}
// DefaultConsensusParams returns a default ConsensusParams.
func DefaultConsensusParams() *ConsensusParams {
return &ConsensusParams{
DefaultBlockSizeParams(),
DefaultTxSizeParams(),
DefaultBlockGossipParams(),
DefaultBlockSize(),
DefaultTxSize(),
DefaultBlockGossip(),
}
}
// DefaultBlockSizeParams returns a default BlockSizeParams.
func DefaultBlockSizeParams() BlockSizeParams {
return BlockSizeParams{
// DefaultBlockSize returns a default BlockSize.
func DefaultBlockSize() BlockSize {
return BlockSize{
MaxBytes: 22020096, // 21MB
MaxTxs: 100000,
MaxGas: -1,
}
}
// DefaultTxSizeParams returns a default TxSizeParams.
func DefaultTxSizeParams() TxSizeParams {
return TxSizeParams{
// DefaultTxSize returns a default TxSize.
func DefaultTxSize() TxSize {
return TxSize{
MaxBytes: 10240, // 10kB
MaxGas: -1,
}
}
// DefaultBlockGossipParams returns a default BlockGossipParams.
func DefaultBlockGossipParams() BlockGossipParams {
return BlockGossipParams{
// DefaultBlockGossip returns a default BlockGossip.
func DefaultBlockGossip() BlockGossip {
return BlockGossip{
BlockPartSizeBytes: 65536, // 64kB,
}
}
@ -71,17 +74,69 @@ func DefaultBlockGossipParams() BlockGossipParams {
// are within their allowed limits, and returns an error if they are not.
func (params *ConsensusParams) Validate() error {
// ensure some values are greater than 0
if params.BlockSizeParams.MaxBytes <= 0 {
return errors.Errorf("BlockSizeParams.MaxBytes must be greater than 0. Got %d", params.BlockSizeParams.MaxBytes)
if params.BlockSize.MaxBytes <= 0 {
return errors.Errorf("BlockSize.MaxBytes must be greater than 0. Got %d", params.BlockSize.MaxBytes)
}
if params.BlockGossipParams.BlockPartSizeBytes <= 0 {
return errors.Errorf("BlockGossipParams.BlockPartSizeBytes must be greater than 0. Got %d", params.BlockGossipParams.BlockPartSizeBytes)
if params.BlockGossip.BlockPartSizeBytes <= 0 {
return errors.Errorf("BlockGossip.BlockPartSizeBytes must be greater than 0. Got %d", params.BlockGossip.BlockPartSizeBytes)
}
// ensure blocks aren't too big
if params.BlockSizeParams.MaxBytes > maxBlockSizeBytes {
return errors.Errorf("BlockSizeParams.MaxBytes is too big. %d > %d",
params.BlockSizeParams.MaxBytes, maxBlockSizeBytes)
if params.BlockSize.MaxBytes > maxBlockSizeBytes {
return errors.Errorf("BlockSize.MaxBytes is too big. %d > %d",
params.BlockSize.MaxBytes, maxBlockSizeBytes)
}
return nil
}
// Hash returns a merkle hash of the parameters to store
// in the block header
func (params *ConsensusParams) Hash() []byte {
return merkle.SimpleHashFromMap(map[string]interface{}{
"block_gossip_part_size_bytes": params.BlockGossip.BlockPartSizeBytes,
"block_size_max_bytes": params.BlockSize.MaxBytes,
"block_size_max_gas": params.BlockSize.MaxGas,
"block_size_max_txs": params.BlockSize.MaxTxs,
"tx_size_max_bytes": params.TxSize.MaxBytes,
"tx_size_max_gas": params.TxSize.MaxGas,
})
}
// Update returns a copy of the params with updates from the non-zero fields of p2.
// NOTE: note: must not modify the original
func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusParams {
res := params // explicit copy
if params2 == nil {
return res
}
// we must defensively consider any structs may be nil
// XXX: it's cast city over here. It's ok because we only do int32->int
// but still, watch it champ.
if params2.BlockSize != nil {
if params2.BlockSize.MaxBytes > 0 {
res.BlockSize.MaxBytes = int(params2.BlockSize.MaxBytes)
}
if params2.BlockSize.MaxTxs > 0 {
res.BlockSize.MaxTxs = int(params2.BlockSize.MaxTxs)
}
if params2.BlockSize.MaxGas > 0 {
res.BlockSize.MaxGas = params2.BlockSize.MaxGas
}
}
if params2.TxSize != nil {
if params2.TxSize.MaxBytes > 0 {
res.TxSize.MaxBytes = int(params2.TxSize.MaxBytes)
}
if params2.TxSize.MaxGas > 0 {
res.TxSize.MaxGas = params2.TxSize.MaxGas
}
}
if params2.BlockGossip != nil {
if params2.BlockGossip.BlockPartSizeBytes > 0 {
res.BlockGossip.BlockPartSizeBytes = int(params2.BlockGossip.BlockPartSizeBytes)
}
}
return res
}

+ 50
- 2
types/params_test.go View File

@ -1,6 +1,8 @@
package types
import (
"bytes"
"sort"
"testing"
"github.com/stretchr/testify/assert"
@ -8,8 +10,8 @@ import (
func newConsensusParams(blockSize, partSize int) ConsensusParams {
return ConsensusParams{
BlockSizeParams: BlockSizeParams{MaxBytes: blockSize},
BlockGossipParams: BlockGossipParams{BlockPartSizeBytes: partSize},
BlockSize: BlockSize{MaxBytes: blockSize},
BlockGossip: BlockGossip{BlockPartSizeBytes: partSize},
}
}
@ -38,3 +40,49 @@ func TestConsensusParamsValidation(t *testing.T) {
}
}
}
func makeParams(blockBytes, blockTx, blockGas, txBytes,
txGas, partSize int) ConsensusParams {
return ConsensusParams{
BlockSize: BlockSize{
MaxBytes: blockBytes,
MaxTxs: blockTx,
MaxGas: int64(blockGas),
},
TxSize: TxSize{
MaxBytes: txBytes,
MaxGas: int64(txGas),
},
BlockGossip: BlockGossip{
BlockPartSizeBytes: partSize,
},
}
}
func TestConsensusParamsHash(t *testing.T) {
params := []ConsensusParams{
makeParams(1, 2, 3, 4, 5, 6),
makeParams(7, 2, 3, 4, 5, 6),
makeParams(1, 7, 3, 4, 5, 6),
makeParams(1, 2, 7, 4, 5, 6),
makeParams(1, 2, 3, 7, 5, 6),
makeParams(1, 2, 3, 4, 7, 6),
makeParams(1, 2, 3, 4, 5, 7),
makeParams(6, 5, 4, 3, 2, 1),
}
hashes := make([][]byte, len(params))
for i := range params {
hashes[i] = params[i].Hash()
}
// make sure there are no duplicates...
// sort, then check in order for matches
sort.Slice(hashes, func(i, j int) bool {
return bytes.Compare(hashes[i], hashes[j]) < 0
})
for i := 0; i < len(hashes)-1; i++ {
assert.NotEqual(t, hashes[i], hashes[i+1])
}
}

+ 20
- 2
types/protobuf.go View File

@ -12,11 +12,11 @@ type tm2pb struct{}
func (tm2pb) Header(header *Header) *types.Header {
return &types.Header{
ChainId: header.ChainID,
ChainID: header.ChainID,
Height: header.Height,
Time: header.Time.Unix(),
NumTxs: int32(header.NumTxs), // XXX: overflow
LastBlockId: TM2PB.BlockID(header.LastBlockID),
LastBlockID: TM2PB.BlockID(header.LastBlockID),
LastCommitHash: header.LastCommitHash,
DataHash: header.DataHash,
AppHash: header.AppHash,
@ -51,3 +51,21 @@ func (tm2pb) Validators(vals *ValidatorSet) []*types.Validator {
}
return validators
}
func (tm2pb) ConsensusParams(params *ConsensusParams) *types.ConsensusParams {
return &types.ConsensusParams{
BlockSize: &types.BlockSize{
MaxBytes: int32(params.BlockSize.MaxBytes),
MaxTxs: int32(params.BlockSize.MaxTxs),
MaxGas: params.BlockSize.MaxGas,
},
TxSize: &types.TxSize{
MaxBytes: int32(params.TxSize.MaxBytes),
MaxGas: params.TxSize.MaxGas,
},
BlockGossip: &types.BlockGossip{
BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
},
}
}

+ 37
- 0
types/test_util.go View File

@ -0,0 +1,37 @@
package types
import "time"
func MakeCommit(blockID BlockID, height int64, round int,
voteSet *VoteSet,
validators []*PrivValidatorFS) (*Commit, error) {
// all sign
for i := 0; i < len(validators); i++ {
vote := &Vote{
ValidatorAddress: validators[i].GetAddress(),
ValidatorIndex: i,
Height: height,
Round: round,
Type: VoteTypePrecommit,
BlockID: blockID,
Timestamp: time.Now().UTC(),
}
_, err := signAddVote(validators[i], vote, voteSet)
if err != nil {
return nil, err
}
}
return voteSet.MakeCommit(), nil
}
func signAddVote(privVal *PrivValidatorFS, vote *Vote, voteSet *VoteSet) (signed bool, err error) {
vote.Signature, err = privVal.Signer.Sign(SignBytes(voteSet.ChainID(), vote))
if err != nil {
return false, err
}
return voteSet.AddVote(vote)
}

+ 0
- 10
types/vote_set_test.go View File

@ -59,16 +59,6 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
return vote
}
func signAddVote(privVal *PrivValidatorFS, vote *Vote, voteSet *VoteSet) (bool, error) {
var err error
vote.Signature, err = privVal.Signer.Sign(SignBytes(voteSet.ChainID(), vote))
if err != nil {
return false, err
}
added, err := voteSet.AddVote(vote)
return added, err
}
func TestAddVote(t *testing.T) {
height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)


Loading…
Cancel
Save