diff --git a/blocksync/pool.go b/blocksync/pool.go index 57ba94ce8..b6a3f3db0 100644 --- a/blocksync/pool.go +++ b/blocksync/pool.go @@ -186,16 +186,20 @@ func (pool *BlockPool) IsCaughtUp() bool { return isCaughtUp } -// PeekTwoBlocks returns blocks at pool.height and pool.height+1. -// We need to see the second block's Commit to validate the first block. -// So we peek two blocks at a time. +// PeekTwoBlocks returns blocks at pool.height and pool.height+1. We need to +// see the second block's Commit to validate the first block. So we peek two +// blocks at a time. We return an extended commit, containing vote extensions +// and their associated signatures, as this is critical to consensus in ABCI++ +// as we switch from block sync to consensus mode. +// // The caller will verify the commit. -func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) { +func (pool *BlockPool) PeekTwoBlocks() (first, second *types.Block, firstExtCommit *types.ExtendedCommit) { pool.mtx.Lock() defer pool.mtx.Unlock() if r := pool.requesters[pool.height]; r != nil { first = r.getBlock() + firstExtCommit = r.getExtendedCommit() } if r := pool.requesters[pool.height+1]; r != nil { second = r.getBlock() @@ -204,7 +208,8 @@ func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) } // PopRequest pops the first block at pool.height. -// It must have been validated by 'second'.Commit from PeekTwoBlocks(). +// It must have been validated by the second Commit from PeekTwoBlocks. +// TODO(thane): (?) and its corresponding ExtendedCommit. func (pool *BlockPool) PopRequest() { pool.mtx.Lock() defer pool.mtx.Unlock() @@ -241,12 +246,23 @@ func (pool *BlockPool) RedoRequest(height int64) p2p.ID { return peerID } -// AddBlock validates that the block comes from the peer it was expected from and calls the requester to store it. +// AddBlock validates that the block comes from the peer it was expected from +// and calls the requester to store it. +// +// This requires an extended commit at the same height as the supplied block - +// the block contains the last commit, but we need the latest commit in case we +// need to switch over from block sync to consensus at this height. If the +// height of the extended commit and the height of the block do not match, we +// do not add the block and return an error. // TODO: ensure that blocks come in order for each peer. -func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) { +func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, extCommit *types.ExtendedCommit, blockSize int) error { pool.mtx.Lock() defer pool.mtx.Unlock() + if block.Height != extCommit.Height { + return fmt.Errorf("heights don't match, not adding block (block height: %d, commit height: %d)", block.Height, extCommit.Height) + } + requester := pool.requesters[block.Height] if requester == nil { pool.Logger.Info( @@ -264,19 +280,22 @@ func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int if diff > maxDiffBetweenCurrentAndReceivedBlockHeight { pool.sendError(errors.New("peer sent us a block we didn't expect with a height too far ahead/behind"), peerID) } - return + return fmt.Errorf("peer sent us a block we didn't expect (peer: %s, current height: %d, block height: %d)", peerID, pool.height, block.Height) } - if requester.setBlock(block, peerID) { + if requester.setBlock(block, extCommit, peerID) { atomic.AddInt32(&pool.numPending, -1) peer := pool.peers[peerID] if peer != nil { peer.decrPending(blockSize) } } else { - pool.Logger.Info("invalid peer", "peer", peerID, "blockHeight", block.Height) - pool.sendError(errors.New("invalid peer"), peerID) + err := errors.New("requester is different or block already exists") + pool.sendError(err, peerID) + return fmt.Errorf("%w (peer: %s, requester: %s, block height: %d)", err, peerID, requester.getPeerID(), block.Height) } + + return nil } // MaxPeerHeight returns the highest reported height. @@ -425,6 +444,7 @@ func (pool *BlockPool) debug() string { } else { str += fmt.Sprintf("H(%v):", h) str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil) + str += fmt.Sprintf("C?(%v) ", pool.requesters[h].extCommit != nil) } } return str @@ -513,9 +533,10 @@ type bpRequester struct { gotBlockCh chan struct{} redoCh chan p2p.ID // redo may send multitime, add peerId to identify repeat - mtx tmsync.Mutex - peerID p2p.ID - block *types.Block + mtx tmsync.Mutex + peerID p2p.ID + block *types.Block + extCommit *types.ExtendedCommit } func newBPRequester(pool *BlockPool, height int64) *bpRequester { @@ -538,13 +559,14 @@ func (bpr *bpRequester) OnStart() error { } // Returns true if the peer matches and block doesn't already exist. -func (bpr *bpRequester) setBlock(block *types.Block, peerID p2p.ID) bool { +func (bpr *bpRequester) setBlock(block *types.Block, extCommit *types.ExtendedCommit, peerID p2p.ID) bool { bpr.mtx.Lock() if bpr.block != nil || bpr.peerID != peerID { bpr.mtx.Unlock() return false } bpr.block = block + bpr.extCommit = extCommit bpr.mtx.Unlock() select { @@ -560,6 +582,12 @@ func (bpr *bpRequester) getBlock() *types.Block { return bpr.block } +func (bpr *bpRequester) getExtendedCommit() *types.ExtendedCommit { + bpr.mtx.Lock() + defer bpr.mtx.Unlock() + return bpr.extCommit +} + func (bpr *bpRequester) getPeerID() p2p.ID { bpr.mtx.Lock() defer bpr.mtx.Unlock() @@ -577,6 +605,7 @@ func (bpr *bpRequester) reset() { bpr.peerID = "" bpr.block = nil + bpr.extCommit = nil } // Tells bpRequester to pick another peer and try again. diff --git a/blocksync/pool_test.go b/blocksync/pool_test.go index 9fcc9dde4..cf29a1527 100644 --- a/blocksync/pool_test.go +++ b/blocksync/pool_test.go @@ -42,7 +42,10 @@ func (p testPeer) runInputRoutine() { // Request desired, pretend like we got the block immediately. func (p testPeer) simulateInput(input inputData) { block := &types.Block{Header: types.Header{Height: input.request.Height}} - input.pool.AddBlock(input.request.PeerID, block, 123) + extCommit := &types.ExtendedCommit{ + Height: input.request.Height, + } + _ = input.pool.AddBlock(input.request.PeerID, block, extCommit, 123) // TODO: uncommenting this creates a race which is detected by: // https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856 // see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890 @@ -112,7 +115,7 @@ func TestBlockPoolBasic(t *testing.T) { if !pool.IsRunning() { return } - first, second := pool.PeekTwoBlocks() + first, second, _ := pool.PeekTwoBlocks() if first != nil && second != nil { pool.PopRequest() } else { @@ -171,7 +174,7 @@ func TestBlockPoolTimeout(t *testing.T) { if !pool.IsRunning() { return } - first, second := pool.PeekTwoBlocks() + first, second, _ := pool.PeekTwoBlocks() if first != nil && second != nil { pool.PopRequest() } else { diff --git a/blocksync/reactor.go b/blocksync/reactor.go index eeada7da2..63e1fc56e 100644 --- a/blocksync/reactor.go +++ b/blocksync/reactor.go @@ -52,7 +52,7 @@ type Reactor struct { initialState sm.State blockExec *sm.BlockExecutor - store *store.BlockStore + store sm.BlockStore pool *BlockPool blockSync bool @@ -178,6 +178,11 @@ func (bcR *Reactor) respondToPeer(msg *bcproto.BlockRequest, block := bcR.store.LoadBlock(msg.Height) if block != nil { + extCommit := bcR.store.LoadBlockExtendedCommit(msg.Height) + if extCommit == nil { + bcR.Logger.Error("found block in store without extended commit", "block", block) + return false + } bl, err := block.ToProto() if err != nil { bcR.Logger.Error("could not convert msg to protobuf", "err", err) @@ -186,7 +191,10 @@ func (bcR *Reactor) respondToPeer(msg *bcproto.BlockRequest, return src.TrySend(p2p.Envelope{ ChannelID: BlocksyncChannel, - Message: &bcproto.BlockResponse{Block: bl}, + Message: &bcproto.BlockResponse{ + Block: bl, + ExtCommit: extCommit.ToProto(), + }, }) } @@ -216,7 +224,17 @@ func (bcR *Reactor) Receive(e p2p.Envelope) { bcR.Logger.Error("Block content is invalid", "err", err) return } - bcR.pool.AddBlock(e.Src.ID(), bi, msg.Block.Size()) + extCommit, err := types.ExtendedCommitFromProto(msg.ExtCommit) + if err != nil { + bcR.Logger.Error("failed to convert extended commit from proto", + "peer", e.Src, + "err", err) + return + } + + if err := bcR.pool.AddBlock(e.Src.ID(), bi, extCommit, msg.Block.Size()); err != nil { + bcR.Logger.Error("failed to add block", "err", err) + } case *bcproto.StatusRequest: // Send peer our state. e.Src.TrySend(p2p.Envelope{ @@ -302,6 +320,19 @@ FOR_LOOP: outbound, inbound, _ := bcR.Switch.NumPeers() bcR.Logger.Debug("Consensus ticker", "numPending", numPending, "total", lenRequesters, "outbound", outbound, "inbound", inbound) + + // TODO(sergio) Might be needed for implementing the upgrading solution. Remove after that + if state.LastBlockHeight > 0 && blocksSynced == 0 { + // Having state-synced, we need to blocksync at least one block + bcR.Logger.Info( + "no seen commit yet", + "height", height, + "last_block_height", state.LastBlockHeight, + "initial_height", state.InitialHeight, + "max_peer_height", bcR.pool.MaxPeerHeight(), + ) + continue FOR_LOOP + } if bcR.pool.IsCaughtUp() { bcR.Logger.Info("Time to switch to consensus reactor!", "height", height) if err := bcR.pool.Stop(); err != nil { @@ -334,10 +365,14 @@ FOR_LOOP: // routine. // See if there are any blocks to sync. - first, second := bcR.pool.PeekTwoBlocks() + first, second, extCommit := bcR.pool.PeekTwoBlocks() // bcR.Logger.Info("TrySync peeked", "first", first, "second", second) - if first == nil || second == nil { - // We need both to sync the first block. + if first == nil || second == nil || extCommit == nil { + if first != nil && extCommit == nil { + // See https://github.com/tendermint/tendermint/pull/8433#discussion_r866790631 + panic(fmt.Errorf("peeked first block without extended commit at height %d - possible node store corruption", first.Height)) + } + // we need all to sync the first block continue FOR_LOOP } else { // Try again quickly next loop. @@ -357,6 +392,7 @@ FOR_LOOP: // NOTE: we can probably make this more efficient, but note that calling // first.Hash() doesn't verify the tx contents, so MakePartSet() is // currently necessary. + // TODO(sergio): Should we also validate against the extended commit? err = state.Validators.VerifyCommitLight( chainID, firstID, first.Height, second.LastCommit) @@ -387,7 +423,7 @@ FOR_LOOP: bcR.pool.PopRequest() // TODO: batch saves so we dont persist to disk every block - bcR.store.SaveBlock(first, firstParts, second.LastCommit) + bcR.store.SaveBlock(first, firstParts, extCommit) // TODO: same thing for app - but we would need a way to // get the hash without persisting the state diff --git a/blocksync/reactor_test.go b/blocksync/reactor_test.go index 10c7105cf..c40cd06a7 100644 --- a/blocksync/reactor_test.go +++ b/blocksync/reactor_test.go @@ -109,40 +109,46 @@ func newReactor( panic(err) } + var lastExtCommit *types.ExtendedCommit + + // The commit we are building for the current height. + seenExtCommit := &types.ExtendedCommit{} + // let's add some blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { - lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil) - if blockHeight > 1 { - lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) - lastBlock := blockStore.LoadBlock(blockHeight - 1) + lastExtCommit = seenExtCommit.Clone() - vote, err := types.MakeVote( - lastBlock.Header.Height, - lastBlockMeta.BlockID, - state.Validators, - privVals[0], - lastBlock.Header.ChainID, - time.Now(), - ) - if err != nil { - panic(err) - } - lastCommit = types.NewCommit(vote.Height, vote.Round, - lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) - } - - thisBlock := state.MakeBlock(blockHeight, nil, lastCommit, nil, state.Validators.Proposer.Address) + thisBlock := state.MakeBlock(blockHeight, nil, lastExtCommit.StripExtensions(), nil, state.Validators.Proposer.Address) thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()} + // Simulate a commit for the current height + vote, err := types.MakeVote( + thisBlock.Header.Height, + blockID, + state.Validators, + privVals[0], + thisBlock.Header.ChainID, + time.Now(), + ) + if err != nil { + panic(err) + } + seenExtCommit = &types.ExtendedCommit{ + Height: vote.Height, + Round: vote.Round, + BlockID: blockID, + ExtendedSignatures: []types.ExtendedCommitSig{vote.ExtendedCommitSig()}, + } + state, err = blockExec.ApplyBlock(state, blockID, thisBlock) if err != nil { panic(fmt.Errorf("error apply block: %w", err)) } - blockStore.SaveBlock(thisBlock, thisParts, lastCommit) + blockStore.SaveBlock(thisBlock, thisParts, seenExtCommit) } bcReactor := NewReactor(state.Copy(), blockExec, blockStore, fastSync, NopMetrics()) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index ccfa98370..52e97bdf7 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -197,24 +197,22 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { panic("entered createProposalBlock with privValidator being nil") } - var commit *types.Commit - var votes []*types.Vote + var extCommit *types.ExtendedCommit switch { case lazyProposer.Height == lazyProposer.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. - commit = types.NewCommit(0, 0, types.BlockID{}, nil) + extCommit = &types.ExtendedCommit{} case lazyProposer.LastCommit.HasTwoThirdsMajority(): // Make the commit from LastCommit - commit = lazyProposer.LastCommit.MakeCommit() - votes = lazyProposer.LastCommit.GetVotes() + extCommit = lazyProposer.LastCommit.MakeExtendedCommit() default: // This shouldn't happen. lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block") return } // omit the last signature in the commit - commit.Signatures[len(commit.Signatures)-1] = types.NewCommitSigAbsent() + extCommit.ExtendedSignatures[len(extCommit.ExtendedSignatures)-1] = types.NewExtendedCommitSigAbsent() if lazyProposer.privValidatorPubKey == nil { // If this node is a validator & proposer in the current round, it will @@ -225,7 +223,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { proposerAddr := lazyProposer.privValidatorPubKey.Address() block, err := lazyProposer.blockExec.CreateProposalBlock( - lazyProposer.Height, lazyProposer.state, commit, proposerAddr, votes) + lazyProposer.Height, lazyProposer.state, extCommit, proposerAddr) require.NoError(t, err) blockParts, err := block.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) diff --git a/consensus/common_test.go b/consensus/common_test.go index ef65bc441..d020cf184 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -133,7 +133,8 @@ func (vs *validatorStub) signVote( // Sign vote for type/hash/header func signVote(vs *validatorStub, voteType tmproto.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { var ext []byte - if voteType == tmproto.PrecommitType { + // Only non-nil precommits are allowed to carry vote extensions. + if voteType == tmproto.PrecommitType && len(hash) != 0 { ext = []byte("extension") } v, err := vs.signVote(voteType, hash, header, ext) diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index 54bc453fb..b474a3ef6 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -2,7 +2,9 @@ package consensus import ( "testing" + "time" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -19,10 +21,15 @@ import ( // Ensure a testnet makes blocks func TestReactorInvalidPrecommit(t *testing.T) { N := 4 - css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore) + css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore, + func(c *cfg.Config) { + c.Consensus.TimeoutPropose = 3000 * time.Millisecond + c.Consensus.TimeoutPrevote = 1000 * time.Millisecond + c.Consensus.TimeoutPrecommit = 1000 * time.Millisecond + }) defer cleanup() - for i := 0; i < 4; i++ { + for i := 0; i < N; i++ { ticker := NewTimeoutTicker() ticker.SetLogger(css[i].Logger) css[i].SetTimeoutTicker(ticker) @@ -30,9 +37,10 @@ func TestReactorInvalidPrecommit(t *testing.T) { } reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // this val sends a random precommit at each height - byzValIdx := 0 + byzValIdx := N - 1 byzVal := css[byzValIdx] byzR := reactors[byzValIdx] @@ -44,7 +52,6 @@ func TestReactorInvalidPrecommit(t *testing.T) { invalidDoPrevoteFunc(t, height, round, byzVal, byzR.Switch, pv) } byzVal.mtx.Unlock() - defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // wait for a bunch of blocks // TODO: make this tighter by ensuring the halt happens by block 2 @@ -62,6 +69,7 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int32, cs *State, sw // - disable privValidator (so we don't do normal precommits) go func() { cs.mtx.Lock() + defer cs.mtx.Unlock() cs.privValidator = pv pubKey, err := cs.privValidator.GetPubKey() if err != nil { @@ -89,8 +97,8 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int32, cs *State, sw t.Error(err) } precommit.Signature = p.Signature + precommit.ExtensionSignature = p.ExtensionSignature cs.privValidator = nil // disable priv val so we don't do normal votes - cs.mtx.Unlock() peers := sw.Peers().List() for _, peer := range peers { diff --git a/consensus/msgs_test.go b/consensus/msgs_test.go index dffbdbbe3..a613a378c 100644 --- a/consensus/msgs_test.go +++ b/consensus/msgs_test.go @@ -63,7 +63,7 @@ func TestMsgToProto(t *testing.T) { val := types.NewValidator(pk, 100) vote, err := types.MakeVote( - 1, types.BlockID{}, &types.ValidatorSet{Proposer: val, Validators: []*types.Validator{val}}, + 1, bi, &types.ValidatorSet{Proposer: val, Validators: []*types.Validator{val}}, pv, "chainID", time.Now()) require.NoError(t, err) pbVote := vote.ToProto() diff --git a/consensus/reactor.go b/consensus/reactor.go index 27ac4a045..a027bac50 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -735,10 +735,10 @@ OUTER_LOOP: // If peer is lagging by more than 1, send Commit. blockStoreBase := conR.conS.blockStore.Base() if blockStoreBase > 0 && prs.Height != 0 && rs.Height >= prs.Height+2 && prs.Height >= blockStoreBase { - // Load the block commit for prs.Height, + // Load the block's extended commit for prs.Height, // which contains precommit signatures for prs.Height. - if commit := conR.conS.blockStore.LoadBlockCommit(prs.Height); commit != nil { - if ps.PickSendVote(commit) { + if ec := conR.conS.blockStore.LoadBlockExtendedCommit(prs.Height); ec != nil { + if ps.PickSendVote(ec) { logger.Debug("Picked Catchup commit to send", "height", prs.Height) continue OUTER_LOOP } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index df88ac096..a51e39f67 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -312,7 +312,7 @@ const numBlocks = 6 var modes = []uint{0, 1, 2, 3} // This is actually not a test, it's for storing validator change tx data for testHandshakeReplay -func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (*cfg.Config, []*types.Block, []*types.Commit, sm.State) { +func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (*cfg.Config, []*types.Block, []*types.ExtendedCommit, sm.State) { nPeers := 7 nVals := 4 css, genDoc, config, cleanup := randConsensusNetWithPeers( @@ -537,13 +537,13 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* } ensureNewRound(newRoundCh, height+1, 0) - chain := make([]*types.Block, 0) - commits := make([]*types.Commit, 0) + chain := []*types.Block{} + extCommits := []*types.ExtendedCommit{} for i := 1; i <= nBlocks; i++ { chain = append(chain, css[0].blockStore.LoadBlock(int64(i))) - commits = append(commits, css[0].blockStore.LoadBlockCommit(int64(i))) + extCommits = append(extCommits, css[0].blockStore.LoadBlockExtendedCommit(int64(i))) } - return config, chain, commits, genesisState + return config, chain, extCommits, genesisState } // Sync from scratch @@ -615,7 +615,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin var ( testConfig *cfg.Config chain []*types.Block - commits []*types.Commit + extCommits []*types.ExtendedCommit store *mockBlockStore stateDB dbm.DB genesisState sm.State @@ -624,7 +624,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin ) if testValidatorsChange { - testConfig, chain, commits, genesisState = setupChainWithChangingValidators(t, fmt.Sprintf("%d_%d_m", nBlocks, mode), numBlocks) + testConfig, chain, extCommits, genesisState = setupChainWithChangingValidators(t, fmt.Sprintf("%d_%d_m", nBlocks, mode), numBlocks) stateDB = dbm.NewMemDB() store = newMockBlockStore(t, config, genesisState.ConsensusParams) } else { @@ -649,7 +649,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin t.Error(err) } }) - chain, commits, err = makeBlockchainFromWAL(wal) + chain, extCommits, err = makeBlockchainFromWAL(wal) require.NoError(t, err) pubKey, err := privVal.GetPubKey() require.NoError(t, err) @@ -663,7 +663,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin _ = stateStore.Close() }) store.chain = chain - store.commits = commits + store.extCommits = extCommits state := genesisState.Copy() // run the chain through state.ApplyBlock to build up the tendermint state @@ -975,7 +975,7 @@ func (app *badApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlo //-------------------------- // utils for making blocks -func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { +func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.ExtendedCommit, error) { var height int64 // Search for height marker @@ -991,10 +991,10 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { // log.Notice("Build a blockchain by reading from the WAL") var ( - blocks []*types.Block - commits []*types.Commit - thisBlockParts *types.PartSet - thisBlockCommit *types.Commit + blocks []*types.Block + extCommits []*types.ExtendedCommit + thisBlockParts *types.PartSet + thisBlockExtCommit *types.ExtendedCommit ) dec := NewWALDecoder(gr) @@ -1032,12 +1032,12 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { if block.Height != height+1 { panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1)) } - commitHeight := thisBlockCommit.Height + commitHeight := thisBlockExtCommit.Height if commitHeight != height+1 { panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1)) } blocks = append(blocks, block) - commits = append(commits, thisBlockCommit) + extCommits = append(extCommits, thisBlockExtCommit) height++ } case *types.PartSetHeader: @@ -1049,8 +1049,12 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { } case *types.Vote: if p.Type == tmproto.PrecommitType { - thisBlockCommit = types.NewCommit(p.Height, p.Round, - p.BlockID, []types.CommitSig{p.CommitSig()}) + thisBlockExtCommit = &types.ExtendedCommit{ + Height: p.Height, + Round: p.Round, + BlockID: p.BlockID, + ExtendedSignatures: []types.ExtendedCommitSig{p.ExtendedCommitSig()}, + } } } } @@ -1071,13 +1075,13 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { if block.Height != height+1 { panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1)) } - commitHeight := thisBlockCommit.Height + commitHeight := thisBlockExtCommit.Height if commitHeight != height+1 { panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1)) } blocks = append(blocks, block) - commits = append(commits, thisBlockCommit) - return blocks, commits, nil + extCommits = append(extCommits, thisBlockExtCommit) + return blocks, extCommits, nil } func readPieceFromWAL(msg *TimedWALMessage) interface{} { @@ -1123,14 +1127,16 @@ func stateAndStore( // mock block store type mockBlockStore struct { - config *cfg.Config - params types.ConsensusParams - chain []*types.Block - commits []*types.Commit - base int64 - t *testing.T + config *cfg.Config + params types.ConsensusParams + chain []*types.Block + extCommits []*types.ExtendedCommit + base int64 + t *testing.T } +var _ sm.BlockStore = &mockBlockStore{} + // TODO: NewBlockStore(db.NewMemDB) ... func newMockBlockStore(t *testing.T, config *cfg.Config, params types.ConsensusParams) *mockBlockStore { return &mockBlockStore{ @@ -1159,13 +1165,17 @@ func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta { } } func (bs *mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil } -func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { +func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) { } + func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit { - return bs.commits[height-1] + return bs.extCommits[height-1].StripExtensions() } func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit { - return bs.commits[height-1] + return bs.extCommits[height-1].StripExtensions() +} +func (bs *mockBlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit { + return bs.extCommits[height-1] } func (bs *mockBlockStore) PruneBlocks(height int64, state sm.State) (uint64, int64, error) { @@ -1173,7 +1183,7 @@ func (bs *mockBlockStore) PruneBlocks(height int64, state sm.State) (uint64, int pruned := uint64(0) for i := int64(0); i < height-1; i++ { bs.chain[i] = nil - bs.commits[i] = nil + bs.extCommits[i] = nil pruned++ } bs.base = height diff --git a/consensus/state.go b/consensus/state.go index 5d665716c..8526c87bf 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -564,15 +564,15 @@ func (cs *State) sendInternalMessage(mi msgInfo) { // Reconstruct LastCommit from SeenCommit, which we saved along with the block, // (which happens even before saving the state) func (cs *State) reconstructLastCommit(state sm.State) { - seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) - if seenCommit == nil { + extCommit := cs.blockStore.LoadBlockExtendedCommit(state.LastBlockHeight) + if extCommit == nil { panic(fmt.Sprintf( "failed to reconstruct last commit; seen commit for height %v not found", state.LastBlockHeight, )) } - lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) + lastPrecommits := extCommit.ToVoteSet(state.ChainID, state.LastValidators) if !lastPrecommits.HasTwoThirdsMajority() { panic("failed to reconstruct last commit; does not have +2/3 maj") } @@ -1210,16 +1210,17 @@ func (cs *State) createProposalBlock() (*types.Block, error) { return nil, errors.New("entered createProposalBlock with privValidator being nil") } - var commit *types.Commit + // TODO(sergio): wouldn't it be easier if CreateProposalBlock accepted cs.LastCommit directly? + var lastExtCommit *types.ExtendedCommit switch { case cs.Height == cs.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. - commit = types.NewCommit(0, 0, types.BlockID{}, nil) + lastExtCommit = &types.ExtendedCommit{} case cs.LastCommit.HasTwoThirdsMajority(): // Make the commit from LastCommit - commit = cs.LastCommit.MakeCommit() + lastExtCommit = cs.LastCommit.MakeExtendedCommit() default: // This shouldn't happen. return nil, errors.New("propose step; cannot propose anything without commit for the previous block") @@ -1233,7 +1234,7 @@ func (cs *State) createProposalBlock() (*types.Block, error) { proposerAddr := cs.privValidatorPubKey.Address() - ret, err := cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, cs.LastCommit.GetVotes()) + ret, err := cs.blockExec.CreateProposalBlock(cs.Height, cs.state, lastExtCommit, proposerAddr) if err != nil { panic(err) } @@ -1656,8 +1657,7 @@ func (cs *State) finalizeCommit(height int64) { // NOTE: the seenCommit is local justification to commit this block, // but may differ from the LastCommit included in the next block precommits := cs.Votes.Precommits(cs.CommitRound) - seenCommit := precommits.MakeCommit() - cs.blockStore.SaveBlock(block, blockParts, seenCommit) + cs.blockStore.SaveBlock(block, blockParts, precommits.MakeExtendedCommit()) } else { // Happens during replay if we already saved the block but didn't commit logger.Debug("calling finalizeCommit on already stored block", "height", block.Height) @@ -1766,7 +1766,7 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { for i, val := range cs.LastValidators.Validators { commitSig := block.LastCommit.Signatures[i] - if commitSig.Absent() { + if commitSig.BlockIDFlag == types.BlockIDFlagAbsent { missingValidators++ missingValidatorsPower += val.VotingPower } @@ -1776,7 +1776,7 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { "validator_address", val.Address.String(), } cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower)) - if commitSig.ForBlock() { + if commitSig.BlockIDFlag == types.BlockIDFlagCommit { cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height)) } else { cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1)) @@ -2068,8 +2068,9 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error return } - // Verify VoteExtension if precommit - if vote.Type == tmproto.PrecommitType { + // Verify VoteExtension if precommit and not nil + // https://github.com/tendermint/tendermint/issues/8487 + if vote.Type == tmproto.PrecommitType && len(vote.BlockID.Hash) != 0 { if err = cs.blockExec.VerifyVoteExtension(vote); err != nil { return false, err } @@ -2236,9 +2237,9 @@ func (cs *State) signVote( BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, } - switch msgType { - case tmproto.PrecommitType: - // if the signedMessage type is for a precommit, add VoteExtension + if msgType == tmproto.PrecommitType && len(vote.BlockID.Hash) != 0 { + // if the signedMessage type is for a non-nil precommit, add + // VoteExtension ext, err := cs.blockExec.ExtendVote(vote) if err != nil { return nil, err diff --git a/consensus/state_test.go b/consensus/state_test.go index 2ee641805..29fad863f 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -1668,10 +1668,14 @@ func TestFinalizeBlockCalled(t *testing.T) { Status: abci.ResponseProcessProposal_ACCEPT, }, nil) m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) - m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil) - m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{ - Status: abci.ResponseVerifyVoteExtension_ACCEPT, - }, nil) + // We only expect VerifyVoteExtension to be called on non-nil precommits. + // https://github.com/tendermint/tendermint/issues/8487 + if !testCase.voteNil { + m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil) + m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{ + Status: abci.ResponseVerifyVoteExtension_ACCEPT, + }, nil) + } r := &abci.ResponseFinalizeBlock{AgreedAppData: []byte("the_hash")} m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(r, nil).Maybe() m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 8283bb2f9..514394d6e 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -196,8 +196,8 @@ func TestEvidencePoolUpdate(t *testing.T) { ev, err := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime.Add(21*time.Minute), val, evidenceChainID) require.NoError(t, err) - lastCommit := makeCommit(height, val.PrivKey.PubKey().Address()) - block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{ev}) + lastExtCommit := makeExtCommit(height, val.PrivKey.PubKey().Address()) + block := types.MakeBlock(height+1, []types.Tx{}, lastExtCommit.StripExtensions(), []types.Evidence{ev}) // update state (partially) state.LastBlockHeight = height + 1 state.LastBlockTime = defaultEvidenceTime.Add(22 * time.Minute) @@ -414,8 +414,8 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.Blo blockStore := store.NewBlockStore(db) for i := int64(1); i <= state.LastBlockHeight; i++ { - lastCommit := makeCommit(i-1, valAddr) - block := state.MakeBlock(i, test.MakeNTxs(i, 1), lastCommit, nil, state.Validators.Proposer.Address) + lastCommit := makeExtCommit(i-1, valAddr) + block := state.MakeBlock(i, test.MakeNTxs(i, 1), lastCommit.StripExtensions(), nil, state.Validators.Proposer.Address) block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute) block.Header.Version = tmversion.Consensus{Block: version.BlockProtocol, App: 1} const parts = 1 @@ -424,21 +424,25 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.Blo return nil, err } - seenCommit := makeCommit(i, valAddr) + seenCommit := makeExtCommit(i, valAddr) blockStore.SaveBlock(block, partSet, seenCommit) } return blockStore, nil } -func makeCommit(height int64, valAddr []byte) *types.Commit { - commitSigs := []types.CommitSig{{ - BlockIDFlag: types.BlockIDFlagCommit, - ValidatorAddress: valAddr, - Timestamp: defaultEvidenceTime, - Signature: []byte("Signature"), - }} - return types.NewCommit(height, 0, types.BlockID{}, commitSigs) +func makeExtCommit(height int64, valAddr []byte) *types.ExtendedCommit { + return &types.ExtendedCommit{ + Height: height, + ExtendedSignatures: []types.ExtendedCommitSig{{ + CommitSig: types.CommitSig{ + BlockIDFlag: types.BlockIDFlagCommit, + ValidatorAddress: valAddr, + Timestamp: defaultEvidenceTime, + Signature: []byte("Signature"), + }, + }}, + } } func defaultTestPool(t *testing.T, height int64) (*evidence.Pool, types.MockPV) { diff --git a/evidence/verify_test.go b/evidence/verify_test.go index f8021f436..3380de313 100644 --- a/evidence/verify_test.go +++ b/evidence/verify_test.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/evidence" "github.com/tendermint/tendermint/evidence/mocks" + "github.com/tendermint/tendermint/internal/test" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" @@ -207,8 +208,9 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { // except the last validator vote twice blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) - commit, err := types.MakeCommit(blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime) + extCommit, err := test.MakeExtendedCommitFromVoteSet(blockID, voteSet, conflictingPrivVals[:4], defaultEvidenceTime) require.NoError(t, err) + commit := extCommit.StripExtensions() ev := &types.LightClientAttackEvidence{ ConflictingBlock: &types.LightBlock{ SignedHeader: &types.SignedHeader{ @@ -225,8 +227,9 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) - trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) + trustedExtCommit, err := test.MakeExtendedCommitFromVoteSet(trustedBlockID, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) + trustedCommit := trustedExtCommit.StripExtensions() trustedSignedHeader := &types.SignedHeader{ Header: trustedHeader, Commit: trustedCommit, @@ -291,8 +294,9 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { // except the last validator vote twice. However this time the commits are of different rounds. blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) voteSet := types.NewVoteSet(evidenceChainID, 10, 0, tmproto.SignedMsgType(2), conflictingVals) - commit, err := types.MakeCommit(blockID, 10, 0, voteSet, conflictingPrivVals, defaultEvidenceTime) + extCommit, err := test.MakeExtendedCommitFromVoteSet(blockID, voteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) + commit := extCommit.StripExtensions() ev := &types.LightClientAttackEvidence{ ConflictingBlock: &types.LightBlock{ SignedHeader: &types.SignedHeader{ @@ -309,8 +313,9 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) - trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) + trustedExtCommit, err := test.MakeExtendedCommitFromVoteSet(trustedBlockID, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) + trustedCommit := trustedExtCommit.StripExtensions() trustedSignedHeader := &types.SignedHeader{ Header: trustedHeader, Commit: trustedCommit, @@ -483,8 +488,9 @@ func makeLunaticEvidence( blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) voteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals) - commit, err := types.MakeCommit(blockID, height, 1, voteSet, conflictingPrivVals, defaultEvidenceTime) + extCommit, err := test.MakeExtendedCommitFromVoteSet(blockID, voteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) + commit := extCommit.StripExtensions() ev = &types.LightClientAttackEvidence{ ConflictingBlock: &types.LightBlock{ SignedHeader: &types.SignedHeader{ @@ -510,8 +516,9 @@ func makeLunaticEvidence( trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) trustedVals, privVals := types.RandValidatorSet(totalVals, defaultVotingPower) trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), trustedVals) - trustedCommit, err := types.MakeCommit(trustedBlockID, height, 1, trustedVoteSet, privVals, defaultEvidenceTime) + trustedExtCommit, err := test.MakeExtendedCommitFromVoteSet(trustedBlockID, trustedVoteSet, privVals, defaultEvidenceTime) require.NoError(t, err) + trustedCommit := trustedExtCommit.StripExtensions() trusted = &types.LightBlock{ SignedHeader: &types.SignedHeader{ Header: trustedHeader, diff --git a/internal/test/commit.go b/internal/test/commit.go index d94bd1054..b9f603a78 100644 --- a/internal/test/commit.go +++ b/internal/test/commit.go @@ -9,7 +9,7 @@ import ( "github.com/tendermint/tendermint/types" ) -func MakeCommitFromVoteSet(blockID types.BlockID, voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.Commit, error) { +func MakeExtendedCommitFromVoteSet(blockID types.BlockID, voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.ExtendedCommit, error) { // all sign for i := 0; i < len(validators); i++ { pubKey, err := validators[i].GetPubKey() @@ -38,7 +38,7 @@ func MakeCommitFromVoteSet(blockID types.BlockID, voteSet *types.VoteSet, valida } } - return voteSet.MakeCommit(), nil + return voteSet.MakeExtendedCommit(), nil } func MakeVoteSet(lastState sm.State, round int32) *types.VoteSet { @@ -87,5 +87,5 @@ func MakeCommit(blockID types.BlockID, height int64, round int32, valSet *types. } } - return types.NewCommit(height, round, blockID, sigs), nil + return &types.Commit{Height: height, Round: round, BlockID: blockID, Signatures: sigs}, nil } diff --git a/light/helpers_test.go b/light/helpers_test.go index 980ded956..3d9d9d826 100644 --- a/light/helpers_test.go +++ b/light/helpers_test.go @@ -90,7 +90,12 @@ func (pkz privKeys) signHeader(header *types.Header, valSet *types.ValidatorSet, commitSigs[vote.ValidatorIndex] = vote.CommitSig() } - return types.NewCommit(header.Height, 1, blockID, commitSigs) + return &types.Commit{ + Height: header.Height, + Round: 1, + BlockID: blockID, + Signatures: commitSigs, + } } func makeVote(header *types.Header, valset *types.ValidatorSet, diff --git a/node/node_test.go b/node/node_test.go index 436218d78..bfc8c3e89 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -333,12 +333,12 @@ func TestCreateProposalBlock(t *testing.T) { blockStore, ) - commit := types.NewCommit(height-1, 0, types.BlockID{}, nil) + extCommit := &types.ExtendedCommit{Height: height - 1} block, err := blockExec.CreateProposalBlock( height, - state, commit, + state, + extCommit, proposerAddr, - nil, ) require.NoError(t, err) @@ -419,12 +419,12 @@ func TestMaxProposalBlockSize(t *testing.T) { blockStore, ) - commit := types.NewCommit(height-1, 0, types.BlockID{}, nil) + extCommit := &types.ExtendedCommit{Height: height - 1} block, err := blockExec.CreateProposalBlock( height, - state, commit, + state, + extCommit, proposerAddr, - nil, ) require.NoError(t, err) diff --git a/privval/file.go b/privval/file.go index 06e3176e1..200295b39 100644 --- a/privval/file.go +++ b/privval/file.go @@ -318,17 +318,17 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { // Vote extensions are non-deterministic, so it is possible that an // application may have created a different extension. We therefore always - // re-sign the vote extensions of precommits. For prevotes, the extension - // signature will always be empty. + // re-sign the vote extensions of precommits. For prevotes and nil + // precommits, the extension signature will always be empty. var extSig []byte - if vote.Type == tmproto.PrecommitType { + if vote.Type == tmproto.PrecommitType && !types.ProtoBlockIDIsNil(&vote.BlockID) { extSignBytes := types.VoteExtensionSignBytes(chainID, vote) extSig, err = pv.Key.PrivKey.Sign(extSignBytes) if err != nil { return err } } else if len(vote.Extension) > 0 { - return errors.New("unexpected vote extension - extensions are only allowed in precommits") + return errors.New("unexpected vote extension - extensions are only allowed in non-nil precommits") } // We might crash before writing to the wal, diff --git a/proto/tendermint/blocksync/types.pb.go b/proto/tendermint/blocksync/types.pb.go index 17d76a278..46a697cec 100644 --- a/proto/tendermint/blocksync/types.pb.go +++ b/proto/tendermint/blocksync/types.pb.go @@ -115,7 +115,8 @@ func (m *NoBlockResponse) GetHeight() int64 { // BlockResponse returns block to the requested type BlockResponse struct { - Block *types.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` + Block *types.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` + ExtCommit *types.ExtendedCommit `protobuf:"bytes,2,opt,name=ext_commit,json=extCommit,proto3" json:"ext_commit,omitempty"` } func (m *BlockResponse) Reset() { *m = BlockResponse{} } @@ -158,6 +159,13 @@ func (m *BlockResponse) GetBlock() *types.Block { return nil } +func (m *BlockResponse) GetExtCommit() *types.ExtendedCommit { + if m != nil { + return m.ExtCommit + } + return nil +} + // StatusRequest requests the status of a peer. type StatusRequest struct { } @@ -384,30 +392,33 @@ func init() { func init() { proto.RegisterFile("tendermint/blocksync/types.proto", fileDescriptor_19b397c236e0fa07) } var fileDescriptor_19b397c236e0fa07 = []byte{ - // 368 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x4d, 0x4f, 0xfa, 0x40, - 0x10, 0xc6, 0xdb, 0x7f, 0x81, 0x7f, 0x32, 0x50, 0x1a, 0x1b, 0xa3, 0xc4, 0x98, 0x86, 0xd4, 0x97, - 0xe8, 0xc1, 0x36, 0xc1, 0xa3, 0xc6, 0x03, 0x27, 0x4c, 0x7c, 0x49, 0x4a, 0xbc, 0x78, 0x21, 0x14, - 0x37, 0x40, 0x94, 0x2e, 0x32, 0xdb, 0x03, 0xdf, 0xc2, 0x2f, 0xe0, 0xf7, 0xf1, 0xc8, 0xd1, 0xa3, - 0x81, 0x2f, 0x62, 0x98, 0x2d, 0x65, 0x69, 0xb0, 0xb7, 0xdd, 0xe9, 0x33, 0xbf, 0x79, 0xfa, 0x64, - 0x16, 0xea, 0x82, 0x45, 0x2f, 0x6c, 0x32, 0x1a, 0x46, 0xc2, 0x0f, 0xdf, 0x78, 0xef, 0x15, 0xa7, - 0x51, 0xcf, 0x17, 0xd3, 0x31, 0x43, 0x6f, 0x3c, 0xe1, 0x82, 0xdb, 0xbb, 0x6b, 0x85, 0x97, 0x2a, - 0x0e, 0x0e, 0x95, 0x3e, 0x52, 0xcb, 0x6e, 0xd9, 0xe3, 0x9e, 0x42, 0xa5, 0xb9, 0xbc, 0x06, 0xec, - 0x3d, 0x66, 0x28, 0xec, 0x3d, 0x28, 0x0d, 0xd8, 0xb0, 0x3f, 0x10, 0x35, 0xbd, 0xae, 0x9f, 0x19, - 0x41, 0x72, 0x73, 0xcf, 0xc1, 0x7a, 0xe0, 0x89, 0x12, 0xc7, 0x3c, 0x42, 0xf6, 0xa7, 0xf4, 0x06, - 0xcc, 0x4d, 0xe1, 0x05, 0x14, 0x69, 0x24, 0xe9, 0xca, 0x8d, 0x7d, 0x4f, 0xf1, 0x29, 0xfd, 0x4b, - 0xbd, 0x54, 0xb9, 0x16, 0x98, 0x6d, 0xd1, 0x15, 0x31, 0x26, 0x9e, 0xdc, 0x6b, 0xa8, 0xae, 0x0a, - 0xf9, 0xa3, 0x6d, 0x1b, 0x0a, 0x61, 0x17, 0x59, 0xed, 0x1f, 0x55, 0xe9, 0xec, 0x7e, 0x1a, 0xf0, - 0xff, 0x9e, 0x21, 0x76, 0xfb, 0xcc, 0xbe, 0x05, 0x93, 0x66, 0x74, 0x26, 0x12, 0x9d, 0x38, 0x72, - 0xbd, 0x6d, 0xc9, 0x79, 0x6a, 0x30, 0x2d, 0x2d, 0xa8, 0x84, 0x6a, 0x50, 0x6d, 0xd8, 0x89, 0x78, - 0x67, 0x45, 0x93, 0xbe, 0x68, 0x6e, 0xb9, 0x71, 0xb2, 0x1d, 0x97, 0xc9, 0xaf, 0xa5, 0x05, 0x56, - 0x94, 0x89, 0xf4, 0x0e, 0xaa, 0x19, 0xa2, 0x41, 0xc4, 0xa3, 0x5c, 0x83, 0x29, 0xcf, 0x0c, 0xb3, - 0x34, 0xa4, 0xdc, 0xd2, 0xdf, 0x2d, 0xe4, 0xd1, 0x36, 0x42, 0x5f, 0xd2, 0x50, 0x2d, 0xd8, 0x8f, - 0x60, 0xa5, 0xb4, 0xc4, 0x5c, 0x91, 0x70, 0xc7, 0xf9, 0xb8, 0xd4, 0x5d, 0x15, 0x37, 0x2a, 0xcd, - 0x22, 0x18, 0x18, 0x8f, 0x9a, 0x4f, 0x5f, 0x73, 0x47, 0x9f, 0xcd, 0x1d, 0xfd, 0x67, 0xee, 0xe8, - 0x1f, 0x0b, 0x47, 0x9b, 0x2d, 0x1c, 0xed, 0x7b, 0xe1, 0x68, 0xcf, 0x57, 0xfd, 0xa1, 0x18, 0xc4, - 0xa1, 0xd7, 0xe3, 0x23, 0x5f, 0x5d, 0xe2, 0xf5, 0x91, 0x76, 0xd8, 0xdf, 0xf6, 0x30, 0xc2, 0x12, - 0x7d, 0xbb, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x1c, 0xa3, 0x45, 0x37, 0x03, 0x00, 0x00, + // 404 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0xcd, 0x4a, 0xeb, 0x50, + 0x10, 0xc7, 0x93, 0x9b, 0xb6, 0x97, 0x3b, 0xb7, 0x69, 0xb8, 0xe1, 0xa2, 0x45, 0x24, 0x94, 0xf8, + 0x81, 0x2e, 0x4c, 0x40, 0x97, 0x0a, 0x42, 0x45, 0xa8, 0xe0, 0x07, 0xa4, 0xb8, 0x71, 0x53, 0x9a, + 0xf4, 0xd0, 0x06, 0x4d, 0x4e, 0xed, 0x39, 0x81, 0x76, 0xe5, 0x2b, 0xf8, 0x02, 0xbe, 0x8f, 0xcb, + 0x2e, 0x5d, 0x4a, 0xfb, 0x22, 0xd2, 0x39, 0x69, 0x9a, 0xc6, 0x98, 0xdd, 0x64, 0xce, 0x7f, 0x7e, + 0xf9, 0xcf, 0x0c, 0x03, 0x0d, 0x4e, 0xc2, 0x1e, 0x19, 0x05, 0x7e, 0xc8, 0x6d, 0xf7, 0x89, 0x7a, + 0x8f, 0x6c, 0x12, 0x7a, 0x36, 0x9f, 0x0c, 0x09, 0xb3, 0x86, 0x23, 0xca, 0xa9, 0xfe, 0x7f, 0xa5, + 0xb0, 0x12, 0xc5, 0xd6, 0x76, 0xaa, 0x0e, 0xd5, 0xa2, 0x5a, 0xd4, 0xe4, 0xbc, 0xa6, 0x88, 0xe6, + 0x3e, 0x54, 0x9b, 0x0b, 0xb1, 0x43, 0x9e, 0x23, 0xc2, 0xb8, 0xbe, 0x01, 0x95, 0x01, 0xf1, 0xfb, + 0x03, 0x5e, 0x97, 0x1b, 0xf2, 0x81, 0xe2, 0xc4, 0x5f, 0xe6, 0x21, 0x68, 0xb7, 0x34, 0x56, 0xb2, + 0x21, 0x0d, 0x19, 0xf9, 0x51, 0xfa, 0x02, 0xea, 0xba, 0xf0, 0x08, 0xca, 0x68, 0x08, 0x75, 0x7f, + 0x8f, 0x37, 0xad, 0x54, 0x17, 0xc2, 0x8b, 0xd0, 0x0b, 0x95, 0x7e, 0x0e, 0x40, 0xc6, 0xbc, 0xe3, + 0xd1, 0x20, 0xf0, 0x79, 0xfd, 0x17, 0xd6, 0x34, 0xbe, 0xd7, 0x5c, 0x8e, 0x31, 0xd5, 0xbb, 0x40, + 0x9d, 0xf3, 0x87, 0x8c, 0xb9, 0x08, 0x4d, 0x0d, 0xd4, 0x36, 0xef, 0xf2, 0x88, 0xc5, 0x4d, 0x99, + 0x67, 0x50, 0x5b, 0x26, 0x8a, 0xbd, 0xeb, 0x3a, 0x94, 0xdc, 0x2e, 0x23, 0xf8, 0x57, 0xc5, 0xc1, + 0xd8, 0x7c, 0x53, 0xe0, 0xf7, 0x0d, 0x61, 0xac, 0xdb, 0x27, 0xfa, 0x15, 0xa8, 0x68, 0xb2, 0x33, + 0x12, 0xe8, 0xb8, 0x25, 0xd3, 0xca, 0x5b, 0x8c, 0x95, 0x9e, 0x6c, 0x4b, 0x72, 0xaa, 0x6e, 0x7a, + 0xd2, 0x6d, 0xf8, 0x17, 0xd2, 0xce, 0x92, 0x26, 0x7c, 0xc5, 0xdd, 0xee, 0xe5, 0xe3, 0x32, 0x0b, + 0x68, 0x49, 0x8e, 0x16, 0x66, 0x76, 0x72, 0x0d, 0xb5, 0x0c, 0x51, 0x41, 0xe2, 0x4e, 0xa1, 0xc1, + 0x84, 0xa7, 0xba, 0x59, 0x1a, 0xc3, 0xb9, 0x25, 0xed, 0x96, 0x8a, 0x68, 0x6b, 0x43, 0x5f, 0xd0, + 0x58, 0x3a, 0xa1, 0xdf, 0x81, 0x96, 0xd0, 0x62, 0x73, 0x65, 0xc4, 0xed, 0x16, 0xe3, 0x12, 0x77, + 0x35, 0xb6, 0x96, 0x69, 0x96, 0x41, 0x61, 0x51, 0xd0, 0xbc, 0x7f, 0x9f, 0x19, 0xf2, 0x74, 0x66, + 0xc8, 0x9f, 0x33, 0x43, 0x7e, 0x9d, 0x1b, 0xd2, 0x74, 0x6e, 0x48, 0x1f, 0x73, 0x43, 0x7a, 0x38, + 0xed, 0xfb, 0x7c, 0x10, 0xb9, 0x96, 0x47, 0x03, 0x3b, 0x7d, 0x05, 0xab, 0x10, 0x8f, 0xc0, 0xce, + 0xbb, 0x3b, 0xb7, 0x82, 0x6f, 0x27, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x13, 0x4f, 0x42, + 0x96, 0x03, 0x00, 0x00, } func (m *BlockRequest) Marshal() (dAtA []byte, err error) { @@ -486,6 +497,18 @@ func (m *BlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ExtCommit != nil { + { + size, err := m.ExtCommit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if m.Block != nil { { size, err := m.Block.MarshalToSizedBuffer(dAtA[:i]) @@ -739,6 +762,10 @@ func (m *BlockResponse) Size() (n int) { l = m.Block.Size() n += 1 + l + sovTypes(uint64(l)) } + if m.ExtCommit != nil { + l = m.ExtCommit.Size() + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -1048,6 +1075,42 @@ func (m *BlockResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtCommit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExtCommit == nil { + m.ExtCommit = &types.ExtendedCommit{} + } + if err := m.ExtCommit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/blocksync/types.proto b/proto/tendermint/blocksync/types.proto index 8c187c793..00f6db362 100644 --- a/proto/tendermint/blocksync/types.proto +++ b/proto/tendermint/blocksync/types.proto @@ -4,6 +4,7 @@ package tendermint.blocksync; option go_package = "github.com/tendermint/tendermint/proto/tendermint/blocksync"; import "tendermint/types/block.proto"; +import "tendermint/types/types.proto"; // BlockRequest requests a block for a specific height message BlockRequest { @@ -18,6 +19,7 @@ message NoBlockResponse { // BlockResponse returns block to the requested message BlockResponse { tendermint.types.Block block = 1; + tendermint.types.ExtendedCommit ext_commit = 2; } // StatusRequest requests the status of a peer. diff --git a/proto/tendermint/types/types.pb.go b/proto/tendermint/types/types.pb.go index ff108c664..2b1724a0d 100644 --- a/proto/tendermint/types/types.pb.go +++ b/proto/tendermint/types/types.pb.go @@ -832,6 +832,162 @@ func (m *CommitSig) GetVoteExtension() *VoteExtensionToSign { return nil } +type ExtendedCommit struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"` + ExtendedSignatures []ExtendedCommitSig `protobuf:"bytes,4,rep,name=extended_signatures,json=extendedSignatures,proto3" json:"extended_signatures"` +} + +func (m *ExtendedCommit) Reset() { *m = ExtendedCommit{} } +func (m *ExtendedCommit) String() string { return proto.CompactTextString(m) } +func (*ExtendedCommit) ProtoMessage() {} +func (*ExtendedCommit) Descriptor() ([]byte, []int) { + return fileDescriptor_d3a6e55e2345de56, []int{10} +} +func (m *ExtendedCommit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExtendedCommit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExtendedCommit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExtendedCommit) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtendedCommit.Merge(m, src) +} +func (m *ExtendedCommit) XXX_Size() int { + return m.Size() +} +func (m *ExtendedCommit) XXX_DiscardUnknown() { + xxx_messageInfo_ExtendedCommit.DiscardUnknown(m) +} + +var xxx_messageInfo_ExtendedCommit proto.InternalMessageInfo + +func (m *ExtendedCommit) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *ExtendedCommit) GetRound() int32 { + if m != nil { + return m.Round + } + return 0 +} + +func (m *ExtendedCommit) GetBlockID() BlockID { + if m != nil { + return m.BlockID + } + return BlockID{} +} + +func (m *ExtendedCommit) GetExtendedSignatures() []ExtendedCommitSig { + if m != nil { + return m.ExtendedSignatures + } + return nil +} + +// ExtendedCommitSig retains all the same fields as CommitSig but adds vote +// extension-related fields. +type ExtendedCommitSig struct { + BlockIdFlag BlockIDFlag `protobuf:"varint,1,opt,name=block_id_flag,json=blockIdFlag,proto3,enum=tendermint.types.BlockIDFlag" json:"block_id_flag,omitempty"` + ValidatorAddress []byte `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` + Timestamp time.Time `protobuf:"bytes,3,opt,name=timestamp,proto3,stdtime" json:"timestamp"` + Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` + // Vote extension data + Extension []byte `protobuf:"bytes,5,opt,name=extension,proto3" json:"extension,omitempty"` + // Vote extension signature + ExtensionSignature []byte `protobuf:"bytes,6,opt,name=extension_signature,json=extensionSignature,proto3" json:"extension_signature,omitempty"` +} + +func (m *ExtendedCommitSig) Reset() { *m = ExtendedCommitSig{} } +func (m *ExtendedCommitSig) String() string { return proto.CompactTextString(m) } +func (*ExtendedCommitSig) ProtoMessage() {} +func (*ExtendedCommitSig) Descriptor() ([]byte, []int) { + return fileDescriptor_d3a6e55e2345de56, []int{11} +} +func (m *ExtendedCommitSig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExtendedCommitSig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExtendedCommitSig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExtendedCommitSig) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtendedCommitSig.Merge(m, src) +} +func (m *ExtendedCommitSig) XXX_Size() int { + return m.Size() +} +func (m *ExtendedCommitSig) XXX_DiscardUnknown() { + xxx_messageInfo_ExtendedCommitSig.DiscardUnknown(m) +} + +var xxx_messageInfo_ExtendedCommitSig proto.InternalMessageInfo + +func (m *ExtendedCommitSig) GetBlockIdFlag() BlockIDFlag { + if m != nil { + return m.BlockIdFlag + } + return BlockIDFlagUnknown +} + +func (m *ExtendedCommitSig) GetValidatorAddress() []byte { + if m != nil { + return m.ValidatorAddress + } + return nil +} + +func (m *ExtendedCommitSig) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + +func (m *ExtendedCommitSig) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *ExtendedCommitSig) GetExtension() []byte { + if m != nil { + return m.Extension + } + return nil +} + +func (m *ExtendedCommitSig) GetExtensionSignature() []byte { + if m != nil { + return m.ExtensionSignature + } + return nil +} + type Proposal struct { Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"` Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` @@ -846,7 +1002,7 @@ func (m *Proposal) Reset() { *m = Proposal{} } func (m *Proposal) String() string { return proto.CompactTextString(m) } func (*Proposal) ProtoMessage() {} func (*Proposal) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{10} + return fileDescriptor_d3a6e55e2345de56, []int{12} } func (m *Proposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -933,7 +1089,7 @@ func (m *SignedHeader) Reset() { *m = SignedHeader{} } func (m *SignedHeader) String() string { return proto.CompactTextString(m) } func (*SignedHeader) ProtoMessage() {} func (*SignedHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{11} + return fileDescriptor_d3a6e55e2345de56, []int{13} } func (m *SignedHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -985,7 +1141,7 @@ func (m *LightBlock) Reset() { *m = LightBlock{} } func (m *LightBlock) String() string { return proto.CompactTextString(m) } func (*LightBlock) ProtoMessage() {} func (*LightBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{12} + return fileDescriptor_d3a6e55e2345de56, []int{14} } func (m *LightBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1039,7 +1195,7 @@ func (m *BlockMeta) Reset() { *m = BlockMeta{} } func (m *BlockMeta) String() string { return proto.CompactTextString(m) } func (*BlockMeta) ProtoMessage() {} func (*BlockMeta) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{13} + return fileDescriptor_d3a6e55e2345de56, []int{15} } func (m *BlockMeta) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1107,7 +1263,7 @@ func (m *TxProof) Reset() { *m = TxProof{} } func (m *TxProof) String() string { return proto.CompactTextString(m) } func (*TxProof) ProtoMessage() {} func (*TxProof) Descriptor() ([]byte, []int) { - return fileDescriptor_d3a6e55e2345de56, []int{14} + return fileDescriptor_d3a6e55e2345de56, []int{16} } func (m *TxProof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1170,6 +1326,8 @@ func init() { proto.RegisterType((*VoteExtensionToSign)(nil), "tendermint.types.VoteExtensionToSign") proto.RegisterType((*Commit)(nil), "tendermint.types.Commit") proto.RegisterType((*CommitSig)(nil), "tendermint.types.CommitSig") + proto.RegisterType((*ExtendedCommit)(nil), "tendermint.types.ExtendedCommit") + proto.RegisterType((*ExtendedCommitSig)(nil), "tendermint.types.ExtendedCommitSig") proto.RegisterType((*Proposal)(nil), "tendermint.types.Proposal") proto.RegisterType((*SignedHeader)(nil), "tendermint.types.SignedHeader") proto.RegisterType((*LightBlock)(nil), "tendermint.types.LightBlock") @@ -1180,97 +1338,101 @@ func init() { func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) } var fileDescriptor_d3a6e55e2345de56 = []byte{ - // 1435 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6f, 0x1a, 0xd7, - 0x16, 0xf7, 0xc0, 0xd8, 0xc0, 0x81, 0xb1, 0xf1, 0x8d, 0x93, 0x60, 0x12, 0x63, 0x34, 0x4f, 0x79, - 0x71, 0xf2, 0x9e, 0x70, 0xea, 0x54, 0xfd, 0x58, 0xb4, 0x15, 0x60, 0x92, 0xa0, 0xd8, 0x98, 0x0e, - 0x24, 0x55, 0xbb, 0x19, 0x0d, 0x70, 0x0d, 0xd3, 0x0c, 0x73, 0x47, 0x33, 0x17, 0xd7, 0xce, 0x5f, - 0x50, 0x79, 0x95, 0x55, 0x77, 0x5e, 0xb5, 0x8b, 0x4a, 0x5d, 0x76, 0xd1, 0x6d, 0xd5, 0x55, 0x96, - 0xd9, 0xb5, 0xab, 0xb4, 0x72, 0xa4, 0xfe, 0x1d, 0xd5, 0xfd, 0x60, 0x18, 0x8c, 0xdd, 0x46, 0x51, - 0xd4, 0x0d, 0x9a, 0x7b, 0xce, 0xef, 0x9c, 0x7b, 0x3e, 0x7e, 0xf7, 0xdc, 0x0b, 0x5c, 0xa7, 0xd8, - 0xed, 0x61, 0x7f, 0x68, 0xbb, 0x74, 0x93, 0x1e, 0x79, 0x38, 0x10, 0xbf, 0x25, 0xcf, 0x27, 0x94, - 0xa0, 0xec, 0x44, 0x5b, 0xe2, 0xf2, 0xfc, 0x4a, 0x9f, 0xf4, 0x09, 0x57, 0x6e, 0xb2, 0x2f, 0x81, - 0xcb, 0xaf, 0xf7, 0x09, 0xe9, 0x3b, 0x78, 0x93, 0xaf, 0x3a, 0xa3, 0xfd, 0x4d, 0x6a, 0x0f, 0x71, - 0x40, 0xad, 0xa1, 0x27, 0x01, 0x6b, 0x91, 0x6d, 0xba, 0xfe, 0x91, 0x47, 0x09, 0xc3, 0x92, 0x7d, - 0xa9, 0x2e, 0x44, 0xd4, 0x07, 0xd8, 0x0f, 0x6c, 0xe2, 0x46, 0xe3, 0xc8, 0x17, 0x67, 0xa2, 0x3c, - 0xb0, 0x1c, 0xbb, 0x67, 0x51, 0xe2, 0x0b, 0x84, 0xfe, 0x21, 0x68, 0x4d, 0xcb, 0xa7, 0x2d, 0x4c, - 0x1f, 0x60, 0xab, 0x87, 0x7d, 0xb4, 0x02, 0xf3, 0x94, 0x50, 0xcb, 0xc9, 0x29, 0x45, 0x65, 0x43, - 0x33, 0xc4, 0x02, 0x21, 0x50, 0x07, 0x56, 0x30, 0xc8, 0xc5, 0x8a, 0xca, 0x46, 0xc6, 0xe0, 0xdf, - 0xfa, 0x00, 0x54, 0x66, 0xca, 0x2c, 0x6c, 0xb7, 0x87, 0x0f, 0xc7, 0x16, 0x7c, 0xc1, 0xa4, 0x9d, - 0x23, 0x8a, 0x03, 0x69, 0x22, 0x16, 0xe8, 0x5d, 0x98, 0xe7, 0xf1, 0xe7, 0xe2, 0x45, 0x65, 0x23, - 0xbd, 0x95, 0x2b, 0x45, 0x0a, 0x25, 0xf2, 0x2b, 0x35, 0x99, 0xbe, 0xa2, 0x3e, 0x7f, 0xb9, 0x3e, - 0x67, 0x08, 0xb0, 0xee, 0x40, 0xa2, 0xe2, 0x90, 0xee, 0x93, 0xfa, 0x76, 0x18, 0x88, 0x32, 0x09, - 0x04, 0xed, 0xc2, 0x92, 0x67, 0xf9, 0xd4, 0x0c, 0x30, 0x35, 0x07, 0x3c, 0x0b, 0xbe, 0x69, 0x7a, - 0x6b, 0xbd, 0x74, 0xb6, 0x0f, 0xa5, 0xa9, 0x64, 0xe5, 0x2e, 0x9a, 0x17, 0x15, 0xea, 0x7f, 0xaa, - 0xb0, 0x20, 0x8b, 0xf1, 0x11, 0x24, 0x64, 0x59, 0xf9, 0x86, 0xe9, 0xad, 0xb5, 0xa8, 0x47, 0xa9, - 0x2a, 0x55, 0x89, 0x1b, 0x60, 0x37, 0x18, 0x05, 0xd2, 0xdf, 0xd8, 0x06, 0xfd, 0x17, 0x92, 0xdd, - 0x81, 0x65, 0xbb, 0xa6, 0xdd, 0xe3, 0x11, 0xa5, 0x2a, 0xe9, 0xd3, 0x97, 0xeb, 0x89, 0x2a, 0x93, - 0xd5, 0xb7, 0x8d, 0x04, 0x57, 0xd6, 0x7b, 0xe8, 0x0a, 0x2c, 0x0c, 0xb0, 0xdd, 0x1f, 0x50, 0x5e, - 0x96, 0xb8, 0x21, 0x57, 0xe8, 0x03, 0x50, 0x19, 0x21, 0x72, 0x2a, 0xdf, 0x3b, 0x5f, 0x12, 0x6c, - 0x29, 0x8d, 0xd9, 0x52, 0x6a, 0x8f, 0xd9, 0x52, 0x49, 0xb2, 0x8d, 0x9f, 0xfd, 0xbe, 0xae, 0x18, - 0xdc, 0x02, 0x55, 0x41, 0x73, 0xac, 0x80, 0x9a, 0x1d, 0x56, 0x36, 0xb6, 0xfd, 0x3c, 0x77, 0xb1, - 0x3a, 0x5b, 0x10, 0x59, 0x58, 0x19, 0x7a, 0x9a, 0x59, 0x09, 0x51, 0x0f, 0x6d, 0x40, 0x96, 0x3b, - 0xe9, 0x92, 0xe1, 0xd0, 0xa6, 0x26, 0xaf, 0xfb, 0x02, 0xaf, 0xfb, 0x22, 0x93, 0x57, 0xb9, 0xf8, - 0x01, 0xeb, 0xc0, 0x35, 0x48, 0xf5, 0x2c, 0x6a, 0x09, 0x48, 0x82, 0x43, 0x92, 0x4c, 0xc0, 0x95, - 0x37, 0x61, 0x29, 0x64, 0x5d, 0x20, 0x20, 0x49, 0xe1, 0x65, 0x22, 0xe6, 0xc0, 0x3b, 0xb0, 0xe2, - 0xe2, 0x43, 0x6a, 0x9e, 0x45, 0xa7, 0x38, 0x1a, 0x31, 0xdd, 0xe3, 0x69, 0x8b, 0x1b, 0xb0, 0xd8, - 0x1d, 0x17, 0x5f, 0x60, 0x81, 0x63, 0xb5, 0x50, 0xca, 0x61, 0xab, 0x90, 0xb4, 0x3c, 0x4f, 0x00, - 0xd2, 0x1c, 0x90, 0xb0, 0x3c, 0x8f, 0xab, 0x6e, 0xc3, 0x32, 0xcf, 0xd1, 0xc7, 0xc1, 0xc8, 0xa1, - 0xd2, 0x49, 0x86, 0x63, 0x96, 0x98, 0xc2, 0x10, 0x72, 0x8e, 0xfd, 0x0f, 0x68, 0xf8, 0xc0, 0xee, - 0x61, 0xb7, 0x8b, 0x05, 0x4e, 0xe3, 0xb8, 0xcc, 0x58, 0xc8, 0x41, 0xb7, 0x20, 0xeb, 0xf9, 0xc4, - 0x23, 0x01, 0xf6, 0x4d, 0xab, 0xd7, 0xf3, 0x71, 0x10, 0xe4, 0x16, 0x85, 0xbf, 0xb1, 0xbc, 0x2c, - 0xc4, 0x7a, 0x0e, 0xd4, 0x6d, 0x8b, 0x5a, 0x28, 0x0b, 0x71, 0x7a, 0x18, 0xe4, 0x94, 0x62, 0x7c, - 0x23, 0x63, 0xb0, 0x4f, 0xfd, 0xa7, 0x38, 0xa8, 0x8f, 0x09, 0xc5, 0xe8, 0x2e, 0xa8, 0xac, 0x4d, - 0x9c, 0x7d, 0x8b, 0xe7, 0xf1, 0xb9, 0x65, 0xf7, 0x5d, 0xdc, 0xdb, 0x0d, 0xfa, 0xed, 0x23, 0x0f, - 0x1b, 0x1c, 0x1c, 0xa1, 0x53, 0x6c, 0x8a, 0x4e, 0x2b, 0x30, 0xef, 0x93, 0x91, 0xdb, 0xe3, 0x2c, - 0x9b, 0x37, 0xc4, 0x02, 0xd5, 0x20, 0x19, 0xb2, 0x44, 0xfd, 0x27, 0x96, 0x2c, 0x31, 0x96, 0x30, - 0x0e, 0x4b, 0x81, 0x91, 0xe8, 0x48, 0xb2, 0x54, 0x20, 0x15, 0x0e, 0x2f, 0xc9, 0xb6, 0xd7, 0x23, - 0xec, 0xc4, 0x0c, 0xfd, 0x0f, 0x96, 0xc3, 0xde, 0x87, 0xc5, 0x13, 0x8c, 0xcb, 0x86, 0x0a, 0x59, - 0xbd, 0x29, 0x5a, 0x99, 0x62, 0x00, 0x25, 0x78, 0x5e, 0x13, 0x5a, 0xd5, 0xf9, 0x24, 0xba, 0x0e, - 0xa9, 0xc0, 0xee, 0xbb, 0x16, 0x1d, 0xf9, 0x58, 0x32, 0x6f, 0x22, 0x60, 0x5a, 0x7c, 0x48, 0xb1, - 0xcb, 0x0f, 0xb9, 0x60, 0xda, 0x44, 0x80, 0x36, 0xe1, 0x52, 0xb8, 0x30, 0x27, 0x5e, 0x04, 0xcb, - 0x50, 0xa8, 0x6a, 0x8d, 0x35, 0xfa, 0x11, 0x68, 0xac, 0x71, 0xb5, 0xd0, 0xc3, 0x4d, 0xc8, 0x32, - 0xee, 0xf1, 0xe3, 0x41, 0x09, 0xf7, 0x21, 0x87, 0x97, 0x66, 0x79, 0x1e, 0xeb, 0x7f, 0x9b, 0x30, - 0x73, 0xf4, 0x09, 0x5c, 0x0f, 0x81, 0x01, 0x76, 0xf6, 0x4d, 0x6b, 0x44, 0x07, 0xd8, 0xa5, 0x76, - 0xd7, 0xa2, 0xb6, 0xdb, 0x97, 0x73, 0x74, 0x55, 0x1a, 0xb5, 0xb0, 0xb3, 0x5f, 0x9e, 0x02, 0xe8, - 0x1f, 0xc3, 0xa5, 0xa9, 0xad, 0xa5, 0xdf, 0xd7, 0x0d, 0x40, 0xff, 0x59, 0x81, 0x05, 0x71, 0xa6, - 0x23, 0x0c, 0x52, 0xce, 0x67, 0x50, 0xec, 0x22, 0x06, 0xc5, 0xdf, 0x9c, 0x41, 0x65, 0x80, 0xb0, - 0xc2, 0x41, 0x4e, 0x2d, 0xc6, 0x37, 0xd2, 0x5b, 0xd7, 0x66, 0x1d, 0x89, 0x10, 0x5b, 0x76, 0x5f, - 0x8e, 0xac, 0x88, 0x91, 0xfe, 0x43, 0x0c, 0x52, 0xa1, 0x1e, 0x95, 0x41, 0x1b, 0xc7, 0x65, 0xee, - 0x3b, 0x56, 0x5f, 0x9e, 0xa2, 0xb5, 0x0b, 0x83, 0xbb, 0xe7, 0x58, 0x7d, 0x23, 0x2d, 0xe3, 0x61, - 0x8b, 0xf3, 0x19, 0x19, 0xbb, 0x80, 0x91, 0x53, 0x47, 0x20, 0xfe, 0x66, 0x47, 0x60, 0x8a, 0xac, - 0xea, 0x59, 0xb2, 0xee, 0xc0, 0xe2, 0x01, 0xa1, 0xd8, 0x9c, 0x30, 0x56, 0x9c, 0xb4, 0x1b, 0xb3, - 0x29, 0x9d, 0x43, 0x05, 0x43, 0x3b, 0x88, 0x0a, 0xf5, 0x1f, 0x63, 0x90, 0x6c, 0xf2, 0x99, 0x64, - 0x39, 0xff, 0xc6, 0xa4, 0xb9, 0x06, 0x29, 0x8f, 0x38, 0xa6, 0xd0, 0xa8, 0x5c, 0x93, 0xf4, 0x88, - 0x63, 0xcc, 0x90, 0x68, 0xfe, 0x2d, 0x8d, 0xa1, 0x85, 0xb7, 0xd0, 0x83, 0xc4, 0x99, 0x1e, 0xe8, - 0x3e, 0x64, 0x44, 0x29, 0xe4, 0x1b, 0xe1, 0x0e, 0xab, 0x01, 0x7f, 0x74, 0x28, 0xb3, 0x6f, 0x1a, - 0x11, 0xb6, 0x40, 0x1a, 0x12, 0xc7, 0x2c, 0xc4, 0x95, 0x2a, 0x9f, 0x29, 0xb9, 0x8b, 0x48, 0x6e, - 0x48, 0x9c, 0xfe, 0x8d, 0x02, 0xb0, 0xc3, 0x2a, 0xcb, 0xf3, 0x65, 0xb7, 0x7b, 0xc0, 0x43, 0x30, - 0xa7, 0x76, 0x2e, 0x5c, 0xd4, 0x34, 0xb9, 0x7f, 0x26, 0x88, 0xc6, 0x5d, 0x05, 0x6d, 0x42, 0xed, - 0x00, 0x8f, 0x83, 0x39, 0xc7, 0x49, 0x78, 0xe9, 0xb6, 0x30, 0x35, 0x32, 0x07, 0x91, 0x95, 0xfe, - 0x8b, 0x02, 0x29, 0x1e, 0xd3, 0x2e, 0xa6, 0xd6, 0x54, 0x0f, 0x95, 0x37, 0xef, 0xe1, 0x1a, 0x80, - 0x70, 0x13, 0xd8, 0x4f, 0xb1, 0x64, 0x56, 0x8a, 0x4b, 0x5a, 0xf6, 0x53, 0x8c, 0xde, 0x0b, 0x0b, - 0x1e, 0xff, 0xfb, 0x82, 0xcb, 0x01, 0x31, 0x2e, 0xfb, 0x55, 0x48, 0xb8, 0xa3, 0xa1, 0xc9, 0xae, - 0x5a, 0x55, 0xb0, 0xd5, 0x1d, 0x0d, 0xdb, 0x87, 0x81, 0xfe, 0x25, 0x24, 0xda, 0x87, 0xfc, 0xd9, - 0xc9, 0x28, 0xea, 0x13, 0x22, 0xdf, 0x3a, 0x62, 0x4a, 0x26, 0x99, 0x80, 0x5f, 0xed, 0x08, 0x54, - 0x36, 0x45, 0xc7, 0x8f, 0x60, 0xf6, 0x8d, 0x4a, 0xaf, 0xf9, 0xa0, 0x95, 0x4f, 0xd9, 0xdb, 0xbf, - 0x2a, 0x90, 0x8e, 0x4c, 0x1b, 0xf4, 0x0e, 0x5c, 0xae, 0xec, 0xec, 0x55, 0x1f, 0x9a, 0xf5, 0x6d, - 0xf3, 0xde, 0x4e, 0xf9, 0xbe, 0xf9, 0xa8, 0xf1, 0xb0, 0xb1, 0xf7, 0x59, 0x23, 0x3b, 0x97, 0xbf, - 0x72, 0x7c, 0x52, 0x44, 0x11, 0xec, 0x23, 0xf7, 0x89, 0x4b, 0xbe, 0x62, 0x77, 0xd2, 0xca, 0xb4, - 0x49, 0xb9, 0xd2, 0xaa, 0x35, 0xda, 0x59, 0x25, 0x7f, 0xf9, 0xf8, 0xa4, 0xb8, 0x1c, 0xb1, 0x28, - 0x77, 0x02, 0xec, 0xd2, 0x59, 0x83, 0xea, 0xde, 0xee, 0x6e, 0xbd, 0x9d, 0x8d, 0xcd, 0x18, 0xc8, - 0xf1, 0x7f, 0x0b, 0x96, 0xa7, 0x0d, 0x1a, 0xf5, 0x9d, 0x6c, 0x3c, 0x8f, 0x8e, 0x4f, 0x8a, 0x8b, - 0x11, 0x74, 0xc3, 0x76, 0xf2, 0xc9, 0xaf, 0xbf, 0x2d, 0xcc, 0x7d, 0xff, 0x5d, 0x41, 0x61, 0x99, - 0x69, 0x53, 0x33, 0x02, 0xfd, 0x1f, 0xae, 0xb6, 0xea, 0xf7, 0x1b, 0xb5, 0x6d, 0x73, 0xb7, 0x75, - 0xdf, 0x6c, 0x7f, 0xde, 0xac, 0x45, 0xb2, 0x5b, 0x3a, 0x3e, 0x29, 0xa6, 0x65, 0x4a, 0x17, 0xa1, - 0x9b, 0x46, 0xed, 0xf1, 0x5e, 0xbb, 0x96, 0x55, 0x04, 0xba, 0xe9, 0x63, 0x36, 0xc0, 0x38, 0xfa, - 0x0e, 0xac, 0x9e, 0x83, 0x0e, 0x13, 0x5b, 0x3e, 0x3e, 0x29, 0x6a, 0x4d, 0x1f, 0x8b, 0xf3, 0xc3, - 0x2d, 0x4a, 0x90, 0x9b, 0xb5, 0xd8, 0x6b, 0xee, 0xb5, 0xca, 0x3b, 0xd9, 0x62, 0x3e, 0x7b, 0x7c, - 0x52, 0xcc, 0x8c, 0x87, 0x21, 0xc3, 0x4f, 0x32, 0xab, 0x7c, 0xfa, 0xfc, 0xb4, 0xa0, 0xbc, 0x38, - 0x2d, 0x28, 0x7f, 0x9c, 0x16, 0x94, 0x67, 0xaf, 0x0a, 0x73, 0x2f, 0x5e, 0x15, 0xe6, 0x7e, 0x7b, - 0x55, 0x98, 0xfb, 0xe2, 0xfd, 0xbe, 0x4d, 0x07, 0xa3, 0x4e, 0xa9, 0x4b, 0x86, 0x9b, 0xd1, 0xbf, - 0x5a, 0x93, 0x4f, 0xf1, 0x97, 0xef, 0xec, 0xdf, 0xb0, 0xce, 0x02, 0x97, 0xdf, 0xfd, 0x2b, 0x00, - 0x00, 0xff, 0xff, 0x4f, 0xab, 0x7f, 0x9d, 0x47, 0x0e, 0x00, 0x00, + // 1493 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcb, 0x6f, 0xdb, 0x46, + 0x13, 0x37, 0x25, 0xda, 0x92, 0x46, 0x92, 0x2d, 0x6f, 0x9c, 0x44, 0x56, 0x62, 0x59, 0x60, 0x90, + 0x2f, 0x4e, 0xbe, 0x0f, 0x72, 0x3e, 0xa7, 0xe8, 0xe3, 0xd0, 0x16, 0x92, 0xed, 0x24, 0x42, 0xfc, + 0x50, 0x29, 0x25, 0x45, 0x73, 0x21, 0x68, 0x71, 0x2d, 0xb1, 0xa1, 0x48, 0x82, 0x5c, 0xb9, 0x76, + 0xfe, 0x82, 0xc2, 0xa7, 0x9c, 0x7a, 0xf3, 0xa9, 0x3d, 0x14, 0xe8, 0xb1, 0x05, 0x7a, 0x2d, 0x7a, + 0xca, 0x31, 0xb7, 0xf6, 0x94, 0xb6, 0x0e, 0xd0, 0xbf, 0xa3, 0xd8, 0x07, 0x5f, 0x96, 0xdd, 0x1a, + 0x41, 0xd0, 0x02, 0xbd, 0x08, 0xdc, 0x99, 0xdf, 0xcc, 0xce, 0xce, 0xfc, 0x76, 0x76, 0x57, 0x70, + 0x95, 0x60, 0xdb, 0xc0, 0xde, 0xd0, 0xb4, 0xc9, 0x32, 0x39, 0x70, 0xb1, 0xcf, 0x7f, 0xeb, 0xae, + 0xe7, 0x10, 0x07, 0x95, 0x22, 0x6d, 0x9d, 0xc9, 0x2b, 0x73, 0x7d, 0xa7, 0xef, 0x30, 0xe5, 0x32, + 0xfd, 0xe2, 0xb8, 0xca, 0x62, 0xdf, 0x71, 0xfa, 0x16, 0x5e, 0x66, 0xa3, 0x9d, 0xd1, 0xee, 0x32, + 0x31, 0x87, 0xd8, 0x27, 0xfa, 0xd0, 0x15, 0x80, 0x85, 0xd8, 0x34, 0x3d, 0xef, 0xc0, 0x25, 0x0e, + 0xc5, 0x3a, 0xbb, 0x42, 0x5d, 0x8d, 0xa9, 0xf7, 0xb0, 0xe7, 0x9b, 0x8e, 0x1d, 0x8f, 0xa3, 0x52, + 0x1b, 0x8b, 0x72, 0x4f, 0xb7, 0x4c, 0x43, 0x27, 0x8e, 0xc7, 0x11, 0xca, 0x7b, 0x50, 0x6c, 0xeb, + 0x1e, 0xe9, 0x60, 0x72, 0x1f, 0xeb, 0x06, 0xf6, 0xd0, 0x1c, 0x4c, 0x12, 0x87, 0xe8, 0x56, 0x59, + 0xaa, 0x49, 0x4b, 0x45, 0x95, 0x0f, 0x10, 0x02, 0x79, 0xa0, 0xfb, 0x83, 0x72, 0xaa, 0x26, 0x2d, + 0x15, 0x54, 0xf6, 0xad, 0x0c, 0x40, 0xa6, 0xa6, 0xd4, 0xc2, 0xb4, 0x0d, 0xbc, 0x1f, 0x58, 0xb0, + 0x01, 0x95, 0xee, 0x1c, 0x10, 0xec, 0x0b, 0x13, 0x3e, 0x40, 0x6f, 0xc1, 0x24, 0x8b, 0xbf, 0x9c, + 0xae, 0x49, 0x4b, 0xf9, 0x95, 0x72, 0x3d, 0x96, 0x28, 0xbe, 0xbe, 0x7a, 0x9b, 0xea, 0x9b, 0xf2, + 0xf3, 0x97, 0x8b, 0x13, 0x2a, 0x07, 0x2b, 0x16, 0x64, 0x9a, 0x96, 0xd3, 0x7b, 0xd2, 0x5a, 0x0b, + 0x03, 0x91, 0xa2, 0x40, 0xd0, 0x26, 0xcc, 0xb8, 0xba, 0x47, 0x34, 0x1f, 0x13, 0x6d, 0xc0, 0x56, + 0xc1, 0x26, 0xcd, 0xaf, 0x2c, 0xd6, 0x4f, 0xd6, 0xa1, 0x9e, 0x58, 0xac, 0x98, 0xa5, 0xe8, 0xc6, + 0x85, 0xca, 0xef, 0x32, 0x4c, 0x89, 0x64, 0xbc, 0x0f, 0x19, 0x91, 0x56, 0x36, 0x61, 0x7e, 0x65, + 0x21, 0xee, 0x51, 0xa8, 0xea, 0xab, 0x8e, 0xed, 0x63, 0xdb, 0x1f, 0xf9, 0xc2, 0x5f, 0x60, 0x83, + 0xfe, 0x03, 0xd9, 0xde, 0x40, 0x37, 0x6d, 0xcd, 0x34, 0x58, 0x44, 0xb9, 0x66, 0xfe, 0xf8, 0xe5, + 0x62, 0x66, 0x95, 0xca, 0x5a, 0x6b, 0x6a, 0x86, 0x29, 0x5b, 0x06, 0xba, 0x04, 0x53, 0x03, 0x6c, + 0xf6, 0x07, 0x84, 0xa5, 0x25, 0xad, 0x8a, 0x11, 0x7a, 0x17, 0x64, 0x4a, 0x88, 0xb2, 0xcc, 0xe6, + 0xae, 0xd4, 0x39, 0x5b, 0xea, 0x01, 0x5b, 0xea, 0xdd, 0x80, 0x2d, 0xcd, 0x2c, 0x9d, 0xf8, 0xd9, + 0x2f, 0x8b, 0x92, 0xca, 0x2c, 0xd0, 0x2a, 0x14, 0x2d, 0xdd, 0x27, 0xda, 0x0e, 0x4d, 0x1b, 0x9d, + 0x7e, 0x92, 0xb9, 0x98, 0x1f, 0x4f, 0x88, 0x48, 0xac, 0x08, 0x3d, 0x4f, 0xad, 0xb8, 0xc8, 0x40, + 0x4b, 0x50, 0x62, 0x4e, 0x7a, 0xce, 0x70, 0x68, 0x12, 0x8d, 0xe5, 0x7d, 0x8a, 0xe5, 0x7d, 0x9a, + 0xca, 0x57, 0x99, 0xf8, 0x3e, 0xad, 0xc0, 0x15, 0xc8, 0x19, 0x3a, 0xd1, 0x39, 0x24, 0xc3, 0x20, + 0x59, 0x2a, 0x60, 0xca, 0x1b, 0x30, 0x13, 0xb2, 0xce, 0xe7, 0x90, 0x2c, 0xf7, 0x12, 0x89, 0x19, + 0xf0, 0x36, 0xcc, 0xd9, 0x78, 0x9f, 0x68, 0x27, 0xd1, 0x39, 0x86, 0x46, 0x54, 0xf7, 0x28, 0x69, + 0x71, 0x1d, 0xa6, 0x7b, 0x41, 0xf2, 0x39, 0x16, 0x18, 0xb6, 0x18, 0x4a, 0x19, 0x6c, 0x1e, 0xb2, + 0xba, 0xeb, 0x72, 0x40, 0x9e, 0x01, 0x32, 0xba, 0xeb, 0x32, 0xd5, 0x2d, 0x98, 0x65, 0x6b, 0xf4, + 0xb0, 0x3f, 0xb2, 0x88, 0x70, 0x52, 0x60, 0x98, 0x19, 0xaa, 0x50, 0xb9, 0x9c, 0x61, 0xaf, 0x41, + 0x11, 0xef, 0x99, 0x06, 0xb6, 0x7b, 0x98, 0xe3, 0x8a, 0x0c, 0x57, 0x08, 0x84, 0x0c, 0x74, 0x13, + 0x4a, 0xae, 0xe7, 0xb8, 0x8e, 0x8f, 0x3d, 0x4d, 0x37, 0x0c, 0x0f, 0xfb, 0x7e, 0x79, 0x9a, 0xfb, + 0x0b, 0xe4, 0x0d, 0x2e, 0x56, 0xca, 0x20, 0xaf, 0xe9, 0x44, 0x47, 0x25, 0x48, 0x93, 0x7d, 0xbf, + 0x2c, 0xd5, 0xd2, 0x4b, 0x05, 0x95, 0x7e, 0x2a, 0xdf, 0xa7, 0x41, 0x7e, 0xe4, 0x10, 0x8c, 0xee, + 0x80, 0x4c, 0xcb, 0xc4, 0xd8, 0x37, 0x7d, 0x1a, 0x9f, 0x3b, 0x66, 0xdf, 0xc6, 0xc6, 0xa6, 0xdf, + 0xef, 0x1e, 0xb8, 0x58, 0x65, 0xe0, 0x18, 0x9d, 0x52, 0x09, 0x3a, 0xcd, 0xc1, 0xa4, 0xe7, 0x8c, + 0x6c, 0x83, 0xb1, 0x6c, 0x52, 0xe5, 0x03, 0xb4, 0x0e, 0xd9, 0x90, 0x25, 0xf2, 0x5f, 0xb1, 0x64, + 0x86, 0xb2, 0x84, 0x72, 0x58, 0x08, 0xd4, 0xcc, 0x8e, 0x20, 0x4b, 0x13, 0x72, 0x61, 0xf3, 0x12, + 0x6c, 0x3b, 0x1f, 0x61, 0x23, 0x33, 0xf4, 0x5f, 0x98, 0x0d, 0x6b, 0x1f, 0x26, 0x8f, 0x33, 0xae, + 0x14, 0x2a, 0x44, 0xf6, 0x12, 0xb4, 0xd2, 0x78, 0x03, 0xca, 0xb0, 0x75, 0x45, 0xb4, 0x6a, 0xb1, + 0x4e, 0x74, 0x15, 0x72, 0xbe, 0xd9, 0xb7, 0x75, 0x32, 0xf2, 0xb0, 0x60, 0x5e, 0x24, 0xa0, 0x5a, + 0xbc, 0x4f, 0xb0, 0xcd, 0x36, 0x39, 0x67, 0x5a, 0x24, 0x40, 0xcb, 0x70, 0x21, 0x1c, 0x68, 0x91, + 0x17, 0xce, 0x32, 0x14, 0xaa, 0x3a, 0x81, 0x46, 0x39, 0x80, 0x22, 0x2d, 0xdc, 0x7a, 0xe8, 0xe1, + 0x06, 0x94, 0x28, 0xf7, 0xd8, 0xf6, 0x20, 0x0e, 0xf3, 0x21, 0x9a, 0x57, 0x51, 0x77, 0x5d, 0x5a, + 0xff, 0xae, 0x43, 0xcd, 0xd1, 0x87, 0x70, 0x35, 0x04, 0xfa, 0xd8, 0xda, 0xd5, 0xf4, 0x11, 0x19, + 0x60, 0x9b, 0x98, 0x3d, 0x9d, 0x98, 0x76, 0x5f, 0xf4, 0xd1, 0x79, 0x61, 0xd4, 0xc1, 0xd6, 0x6e, + 0x23, 0x01, 0x50, 0x3e, 0x80, 0x0b, 0x89, 0xa9, 0x85, 0xdf, 0xf3, 0x06, 0xa0, 0xfc, 0x20, 0xc1, + 0x14, 0xdf, 0xd3, 0x31, 0x06, 0x49, 0xa7, 0x33, 0x28, 0x75, 0x16, 0x83, 0xd2, 0xaf, 0xcf, 0xa0, + 0x06, 0x40, 0x98, 0x61, 0xbf, 0x2c, 0xd7, 0xd2, 0x4b, 0xf9, 0x95, 0x2b, 0xe3, 0x8e, 0x78, 0x88, + 0x1d, 0xb3, 0x2f, 0x5a, 0x56, 0xcc, 0x48, 0xf9, 0x26, 0x05, 0xb9, 0x50, 0x8f, 0x1a, 0x50, 0x0c, + 0xe2, 0xd2, 0x76, 0x2d, 0xbd, 0x2f, 0x76, 0xd1, 0xc2, 0x99, 0xc1, 0xdd, 0xb5, 0xf4, 0xbe, 0x9a, + 0x17, 0xf1, 0xd0, 0xc1, 0xe9, 0x8c, 0x4c, 0x9d, 0xc1, 0xc8, 0xc4, 0x16, 0x48, 0xbf, 0xde, 0x16, + 0x48, 0x90, 0x55, 0x3e, 0x49, 0xd6, 0x0d, 0x98, 0xde, 0x73, 0x08, 0xd6, 0x22, 0xc6, 0xf2, 0x9d, + 0x76, 0x7d, 0x7c, 0x49, 0xa7, 0x50, 0x41, 0x2d, 0xee, 0xc5, 0x85, 0xca, 0x6f, 0x12, 0x4c, 0xb3, + 0x91, 0x81, 0x8d, 0x7f, 0xb2, 0xf0, 0x8f, 0xc5, 0x26, 0x33, 0xb0, 0xa1, 0x8d, 0x31, 0xe0, 0xda, + 0xb8, 0xc7, 0x64, 0xcc, 0x11, 0x13, 0x50, 0xe0, 0xa5, 0x13, 0x31, 0xe2, 0xbb, 0x14, 0xcc, 0x8e, + 0xe1, 0xff, 0x85, 0xcc, 0x48, 0xb4, 0xb1, 0xc9, 0x73, 0xb6, 0xb1, 0xa9, 0x33, 0xdb, 0xd8, 0xb7, + 0x29, 0xc8, 0xb6, 0xd9, 0x71, 0xa5, 0x5b, 0x7f, 0xc7, 0x21, 0x74, 0x05, 0x72, 0xae, 0x63, 0x69, + 0x5c, 0x23, 0x33, 0x4d, 0xd6, 0x75, 0x2c, 0x75, 0x8c, 0x66, 0x93, 0x6f, 0xe8, 0x84, 0x9a, 0x7a, + 0x03, 0x45, 0xc8, 0x9c, 0x28, 0x82, 0xe2, 0x41, 0x81, 0xa7, 0x42, 0x5c, 0x1f, 0x6f, 0xd3, 0x1c, + 0xb0, 0xfb, 0xa8, 0x34, 0x7e, 0xdd, 0xe5, 0x61, 0x73, 0xa4, 0x2a, 0x70, 0xd4, 0x82, 0xdf, 0xb6, + 0xc4, 0x0d, 0xb6, 0x7c, 0x56, 0xff, 0x53, 0x05, 0x4e, 0xf9, 0x42, 0x02, 0xd8, 0xa0, 0x99, 0x65, + 0xeb, 0xa5, 0x17, 0x3f, 0x9f, 0x85, 0xa0, 0x25, 0x66, 0xae, 0x9e, 0x55, 0x34, 0x31, 0x7f, 0xc1, + 0x8f, 0xc7, 0xbd, 0x0a, 0xc5, 0x88, 0xdb, 0x3e, 0x0e, 0x82, 0x39, 0xc5, 0x49, 0x78, 0x1f, 0xeb, + 0x60, 0xa2, 0x16, 0xf6, 0x62, 0x23, 0xe5, 0x47, 0x09, 0x72, 0x2c, 0xa6, 0x4d, 0x4c, 0xf4, 0x44, + 0x0d, 0xa5, 0xd7, 0xaf, 0xe1, 0x02, 0x00, 0x77, 0xe3, 0x9b, 0x4f, 0xb1, 0x60, 0x56, 0x8e, 0x49, + 0x3a, 0xe6, 0x53, 0x8c, 0xde, 0x0e, 0x13, 0x9e, 0xfe, 0xf3, 0x84, 0x8b, 0x8e, 0x11, 0xa4, 0xfd, + 0x32, 0x64, 0xec, 0xd1, 0x50, 0xa3, 0xb7, 0x30, 0x99, 0xb3, 0xd5, 0x1e, 0x0d, 0xbb, 0xfb, 0xbe, + 0xf2, 0x29, 0x64, 0xba, 0xfb, 0xec, 0x45, 0x42, 0x29, 0xea, 0x39, 0x8e, 0xb8, 0x06, 0xf3, 0x03, + 0x34, 0x4b, 0x05, 0xec, 0xd6, 0x87, 0x40, 0xa6, 0x07, 0x6c, 0xf0, 0x3e, 0xa2, 0xdf, 0xa8, 0x7e, + 0xce, 0xb7, 0x8e, 0x78, 0xe5, 0xdc, 0xfa, 0x49, 0x82, 0x7c, 0xac, 0xdd, 0xa0, 0xff, 0xc3, 0xc5, + 0xe6, 0xc6, 0xf6, 0xea, 0x03, 0xad, 0xb5, 0xa6, 0xdd, 0xdd, 0x68, 0xdc, 0xd3, 0x1e, 0x6e, 0x3d, + 0xd8, 0xda, 0xfe, 0x78, 0xab, 0x34, 0x51, 0xb9, 0x74, 0x78, 0x54, 0x43, 0x31, 0xec, 0x43, 0xfb, + 0x89, 0xed, 0x7c, 0x46, 0xf7, 0xf9, 0x5c, 0xd2, 0xa4, 0xd1, 0xec, 0xac, 0x6f, 0x75, 0x4b, 0x52, + 0xe5, 0xe2, 0xe1, 0x51, 0x6d, 0x36, 0x66, 0xd1, 0xd8, 0xf1, 0xb1, 0x4d, 0xc6, 0x0d, 0x56, 0xb7, + 0x37, 0x37, 0x5b, 0xdd, 0x52, 0x6a, 0xcc, 0x40, 0x1c, 0x10, 0x37, 0x61, 0x36, 0x69, 0xb0, 0xd5, + 0xda, 0x28, 0xa5, 0x2b, 0xe8, 0xf0, 0xa8, 0x36, 0x1d, 0x43, 0x6f, 0x99, 0x56, 0x25, 0xfb, 0xf9, + 0x97, 0xd5, 0x89, 0xaf, 0xbf, 0xaa, 0x4a, 0x74, 0x65, 0xc5, 0x44, 0x8f, 0x40, 0xff, 0x83, 0xcb, + 0x9d, 0xd6, 0xbd, 0xad, 0xf5, 0x35, 0x6d, 0xb3, 0x73, 0x4f, 0xeb, 0x7e, 0xd2, 0x5e, 0x8f, 0xad, + 0x6e, 0xe6, 0xf0, 0xa8, 0x96, 0x17, 0x4b, 0x3a, 0x0b, 0xdd, 0x56, 0xd7, 0x1f, 0x6d, 0x77, 0xd7, + 0x4b, 0x12, 0x47, 0xb7, 0x3d, 0x4c, 0xcf, 0x36, 0x86, 0xbe, 0x0d, 0xf3, 0xa7, 0xa0, 0xc3, 0x85, + 0xcd, 0x1e, 0x1e, 0xd5, 0x8a, 0x6d, 0x0f, 0xf3, 0xfd, 0xc3, 0x2c, 0xea, 0x50, 0x1e, 0xb7, 0xd8, + 0x6e, 0x6f, 0x77, 0x1a, 0x1b, 0xa5, 0x5a, 0xa5, 0x74, 0x78, 0x54, 0x2b, 0x04, 0xcd, 0x90, 0xe2, + 0xa3, 0x95, 0x35, 0x3f, 0x7a, 0x7e, 0x5c, 0x95, 0x5e, 0x1c, 0x57, 0xa5, 0x5f, 0x8f, 0xab, 0xd2, + 0xb3, 0x57, 0xd5, 0x89, 0x17, 0xaf, 0xaa, 0x13, 0x3f, 0xbf, 0xaa, 0x4e, 0x3c, 0x7e, 0xa7, 0x6f, + 0x92, 0xc1, 0x68, 0xa7, 0xde, 0x73, 0x86, 0xcb, 0xf1, 0x57, 0x78, 0xf4, 0xc9, 0xff, 0x0d, 0x38, + 0xf9, 0x42, 0xdf, 0x99, 0x62, 0xf2, 0x3b, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff, 0x61, 0x3b, 0x18, + 0x58, 0x62, 0x10, 0x00, 0x00, } func (m *PartSetHeader) Marshal() (dAtA []byte, err error) { @@ -1826,6 +1988,127 @@ func (m *CommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ExtendedCommit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExtendedCommit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExtendedCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ExtendedSignatures) > 0 { + for iNdEx := len(m.ExtendedSignatures) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ExtendedSignatures[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.BlockID.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.Round != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Round)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ExtendedCommitSig) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExtendedCommitSig) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExtendedCommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ExtensionSignature) > 0 { + i -= len(m.ExtensionSignature) + copy(dAtA[i:], m.ExtensionSignature) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ExtensionSignature))) + i-- + dAtA[i] = 0x32 + } + if len(m.Extension) > 0 { + i -= len(m.Extension) + copy(dAtA[i:], m.Extension) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Extension))) + i-- + dAtA[i] = 0x2a + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x22 + } + n12, err12 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) + if err12 != nil { + return 0, err12 + } + i -= n12 + i = encodeVarintTypes(dAtA, i, uint64(n12)) + i-- + dAtA[i] = 0x1a + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if m.BlockIdFlag != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.BlockIdFlag)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *Proposal) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1853,12 +2136,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x3a } - n11, err11 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) - if err11 != nil { - return 0, err11 + n13, err13 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) + if err13 != nil { + return 0, err13 } - i -= n11 - i = encodeVarintTypes(dAtA, i, uint64(n11)) + i -= n13 + i = encodeVarintTypes(dAtA, i, uint64(n13)) i-- dAtA[i] = 0x32 { @@ -2343,6 +2626,59 @@ func (m *CommitSig) Size() (n int) { return n } +func (m *ExtendedCommit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } + if m.Round != 0 { + n += 1 + sovTypes(uint64(m.Round)) + } + l = m.BlockID.Size() + n += 1 + l + sovTypes(uint64(l)) + if len(m.ExtendedSignatures) > 0 { + for _, e := range m.ExtendedSignatures { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + return n +} + +func (m *ExtendedCommitSig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockIdFlag != 0 { + n += 1 + sovTypes(uint64(m.BlockIdFlag)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovTypes(uint64(l)) + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Extension) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.ExtensionSignature) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *Proposal) Size() (n int) { if m == nil { return 0 @@ -4287,6 +4623,399 @@ func (m *CommitSig) Unmarshal(dAtA []byte) error { } return nil } +func (m *ExtendedCommit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExtendedCommit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExtendedCommit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType) + } + m.Round = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Round |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockID", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BlockID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtendedSignatures", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExtendedSignatures = append(m.ExtendedSignatures, ExtendedCommitSig{}) + if err := m.ExtendedSignatures[len(m.ExtendedSignatures)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExtendedCommitSig) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExtendedCommitSig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExtendedCommitSig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockIdFlag", wireType) + } + m.BlockIdFlag = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockIdFlag |= BlockIDFlag(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = append(m.ValidatorAddress[:0], dAtA[iNdEx:postIndex]...) + if m.ValidatorAddress == nil { + m.ValidatorAddress = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Extension", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Extension = append(m.Extension[:0], dAtA[iNdEx:postIndex]...) + if m.Extension == nil { + m.Extension = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionSignature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExtensionSignature = append(m.ExtensionSignature[:0], dAtA[iNdEx:postIndex]...) + if m.ExtensionSignature == nil { + m.ExtensionSignature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Proposal) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/proto/tendermint/types/types.proto b/proto/tendermint/types/types.proto index 0595049a3..d5870b94f 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -142,6 +142,28 @@ message CommitSig { VoteExtensionToSign vote_extension = 5; } +message ExtendedCommit { + int64 height = 1; + int32 round = 2; + BlockID block_id = 3 + [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; + repeated ExtendedCommitSig extended_signatures = 4 [(gogoproto.nullable) = false]; +} + +// ExtendedCommitSig retains all the same fields as CommitSig but adds vote +// extension-related fields. +message ExtendedCommitSig { + BlockIDFlag block_id_flag = 1; + bytes validator_address = 2; + google.protobuf.Timestamp timestamp = 3 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 4; + // Vote extension data + bytes extension = 5; + // Vote extension signature + bytes extension_signature = 6; +} + message Proposal { SignedMsgType type = 1; int64 height = 2; diff --git a/state/execution.go b/state/execution.go index d563c45c5..c67692ac7 100644 --- a/state/execution.go +++ b/state/execution.go @@ -1,12 +1,12 @@ package state import ( + "bytes" "context" "fmt" "time" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" @@ -100,9 +100,8 @@ func (blockExec *BlockExecutor) SetEventBus(eventBus types.BlockEventPublisher) func (blockExec *BlockExecutor) CreateProposalBlock( height int64, state State, - commit *types.Commit, + lastExtCommit *types.ExtendedCommit, proposerAddr []byte, - votes []*types.Vote, ) (*types.Block, error) { maxBytes := state.ConsensusParams.Block.MaxBytes @@ -114,14 +113,14 @@ func (blockExec *BlockExecutor) CreateProposalBlock( maxDataBytes := types.MaxDataBytes(maxBytes, evSize, state.Validators.Size()) txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas) + commit := lastExtCommit.StripExtensions() block := state.MakeBlock(height, txs, commit, evidence, proposerAddr) - localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight) rpp, err := blockExec.proxyApp.PrepareProposal(context.TODO(), &abci.RequestPrepareProposal{ MaxTxBytes: maxDataBytes, Txs: block.Txs.ToSliceOfBytes(), - LocalLastCommit: extendedCommitInfo(localLastCommit, votes), + LocalLastCommit: buildExtendedCommitInfo(lastExtCommit, blockExec.store, state.InitialHeight), Misbehavior: block.Evidence.Evidence.ToABCI(), Height: block.Height, Time: block.Time, @@ -418,44 +417,69 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a } } -// extendedCommitInfo expects a CommitInfo struct along with all of the -// original votes relating to that commit, including their vote extensions. The -// order of votes does not matter. -func extendedCommitInfo(c abci.CommitInfo, votes []*types.Vote) abci.ExtendedCommitInfo { - if len(c.Votes) != len(votes) { - panic(fmt.Sprintf("extendedCommitInfo: number of votes from commit differ from the number of votes supplied (%d != %d)", len(c.Votes), len(votes))) +// buildExtendedCommitInfo populates an ABCI extended commit from the +// corresponding Tendermint extended commit ec, using the stored validator set +// from ec. It requires ec to include the original precommit votes along with +// the vote extensions from the last commit. +// +// For heights below the initial height, for which we do not have the required +// data, it returns an empty record. +// +// Assumes that the commit signatures are sorted according to validator index. +func buildExtendedCommitInfo(ec *types.ExtendedCommit, store Store, initialHeight int64) abci.ExtendedCommitInfo { + if ec.Height < initialHeight { + // There are no extended commits for heights below the initial height. + return abci.ExtendedCommitInfo{} } - votesByVal := make(map[string]*types.Vote) - for _, vote := range votes { - if vote != nil { - valAddr := vote.ValidatorAddress.String() - if _, ok := votesByVal[valAddr]; ok { - panic(fmt.Sprintf("extendedCommitInfo: found duplicate vote for validator with address %s", valAddr)) - } - votesByVal[valAddr] = vote + + valSet, err := store.LoadValidators(ec.Height) + if err != nil { + panic(fmt.Errorf("failed to load validator set at height %d, initial height %d: %w", ec.Height, initialHeight, err)) + } + + var ( + ecSize = ec.Size() + valSetLen = len(valSet.Validators) + ) + + // Ensure that the size of the validator set in the extended commit matches + // the size of the validator set in the state store. + if ecSize != valSetLen { + panic(fmt.Errorf( + "extended commit size (%d) does not match validator set length (%d) at height %d\n\n%v\n\n%v", + ecSize, valSetLen, ec.Height, ec.ExtendedSignatures, valSet.Validators, + )) + } + + votes := make([]abci.ExtendedVoteInfo, ecSize) + for i, val := range valSet.Validators { + ecs := ec.ExtendedSignatures[i] + + // Absent signatures have empty validator addresses, but otherwise we + // expect the validator addresses to be the same. + if ecs.BlockIDFlag != types.BlockIDFlagAbsent && !bytes.Equal(ecs.ValidatorAddress, val.Address) { + panic(fmt.Errorf("validator address of extended commit signature in position %d (%s) does not match the corresponding validator's at height %d (%s)", + i, ecs.ValidatorAddress, ec.Height, val.Address, + )) } - } - vs := make([]abci.ExtendedVoteInfo, len(c.Votes)) - for i := range vs { + var ext []byte - // votes[i] will be nil if c.Votes[i].SignedLastBlock is false - if c.Votes[i].SignedLastBlock { - valAddr := crypto.Address(c.Votes[i].Validator.Address).String() - vote, ok := votesByVal[valAddr] - if !ok || vote == nil { - panic(fmt.Sprintf("extendedCommitInfo: validator with address %s signed last block, but could not find vote for it", valAddr)) - } - ext = vote.Extension + if ecs.BlockIDFlag == types.BlockIDFlagCommit { + // We only care about vote extensions if a validator has voted to + // commit. + ext = ecs.Extension } - vs[i] = abci.ExtendedVoteInfo{ - Validator: c.Votes[i].Validator, - SignedLastBlock: c.Votes[i].SignedLastBlock, + + votes[i] = abci.ExtendedVoteInfo{ + Validator: types.TM2PB.Validator(val), + SignedLastBlock: ecs.BlockIDFlag != types.BlockIDFlagAbsent, VoteExtension: ext, } } + return abci.ExtendedCommitInfo{ - Round: c.Round, - Votes: vs, + Round: ec.Round, + Votes: votes, } } diff --git a/state/execution_test.go b/state/execution_test.go index 9d516be2e..22070a009 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -94,7 +94,7 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) { stateStore := sm.NewStore(stateDB, sm.StoreOptions{ DiscardABCIResponses: false, }) - absentSig := types.NewCommitSigAbsent() + absentSig := types.NewExtendedCommitSigAbsent() testCases := []struct { name string @@ -134,12 +134,12 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) { for idx, isAbsent := range tc.absentCommitSigs { if isAbsent { - lastCommit.Signatures[idx] = absentSig + lastCommit.ExtendedSignatures[idx] = absentSig } } // block for height 2 - block := makeBlock(state, 2, lastCommit) + block := makeBlock(state, 2, lastCommit.StripExtensions()) bps, err := block.MakePartSet(testPartSize) require.NoError(t, err) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} @@ -175,16 +175,19 @@ func TestFinalizeBlockValidators(t *testing.T) { var ( now = tmtime.Now() - commitSig0 = types.NewCommitSigForBlock( - []byte("Signature1"), - state.Validators.Validators[0].Address, - now, - ) - commitSig1 = types.NewCommitSigForBlock( - []byte("Signature2"), - state.Validators.Validators[1].Address, - now, - ) + commitSig0 = types.CommitSig{ + BlockIDFlag: types.BlockIDFlagCommit, + ValidatorAddress: state.Validators.Validators[0].Address, + Timestamp: now, + Signature: []byte("Signature1"), + } + + commitSig1 = types.CommitSig{ + BlockIDFlag: types.BlockIDFlagCommit, + ValidatorAddress: state.Validators.Validators[1].Address, + Timestamp: now, + Signature: []byte("Signature2"), + } absentSig = types.NewCommitSigAbsent() ) @@ -199,7 +202,11 @@ func TestFinalizeBlockValidators(t *testing.T) { } for _, tc := range testCases { - lastCommit := types.NewCommit(1, 0, prevBlockID, tc.lastCommitSigs) + lastCommit := &types.Commit{ + Height: 1, + BlockID: prevBlockID, + Signatures: tc.lastCommitSigs, + } // block for height 2 block := makeBlock(state, 2, lastCommit) @@ -264,12 +271,15 @@ func TestFinalizeBlockMisbehavior(t *testing.T) { ConflictingBlock: &types.LightBlock{ SignedHeader: &types.SignedHeader{ Header: header, - Commit: types.NewCommit(10, 0, makeBlockID(header.Hash(), 100, []byte("partshash")), []types.CommitSig{{ - BlockIDFlag: types.BlockIDFlagNil, - ValidatorAddress: crypto.AddressHash([]byte("validator_address")), - Timestamp: defaultEvidenceTime, - Signature: crypto.CRandBytes(types.MaxSignatureSize), - }}), + Commit: &types.Commit{ + Height: 10, + BlockID: makeBlockID(header.Hash(), 100, []byte("partshash")), + Signatures: []types.CommitSig{{ + BlockIDFlag: types.BlockIDFlagNil, + ValidatorAddress: crypto.AddressHash([]byte("validator_address")), + Timestamp: defaultEvidenceTime, + Signature: crypto.CRandBytes(types.MaxSignatureSize)}}, + }, }, ValidatorSet: state.Validators, }, @@ -394,6 +404,7 @@ func TestProcessProposal(t *testing.T) { Height: height - 1, Signatures: lastCommitSig, }) + block1.Txs = txs expectedRpp := &abci.RequestProcessProposal{ @@ -713,9 +724,9 @@ func TestEmptyPrepareProposal(t *testing.T) { blockStore, ) pa, _ := state.Validators.GetByIndex(0) - commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) + commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) require.NoError(t, err) - _, err = blockExec.CreateProposalBlock(height, state, commit, pa, votes) + _, err = blockExec.CreateProposalBlock(height, state, commit, pa) require.NoError(t, err) } @@ -756,9 +767,9 @@ func TestPrepareProposalTxsAllIncluded(t *testing.T) { blockStore, ) pa, _ := state.Validators.GetByIndex(0) - commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) + commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) require.NoError(t, err) - block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes) + block, err := blockExec.CreateProposalBlock(height, state, commit, pa) require.NoError(t, err) for i, tx := range block.Data.Txs { @@ -809,9 +820,9 @@ func TestPrepareProposalReorderTxs(t *testing.T) { blockStore, ) pa, _ := state.Validators.GetByIndex(0) - commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) + commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) require.NoError(t, err) - block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes) + block, err := blockExec.CreateProposalBlock(height, state, commit, pa) require.NoError(t, err) for i, tx := range block.Data.Txs { require.Equal(t, txs[i], tx) @@ -864,10 +875,9 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) { blockStore, ) pa, _ := state.Validators.GetByIndex(0) - commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) + commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) require.NoError(t, err) - - block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes) + block, err := blockExec.CreateProposalBlock(height, state, commit, pa) require.Nil(t, block) require.ErrorContains(t, err, "transaction data size exceeds maximum") @@ -914,10 +924,9 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) { blockStore, ) pa, _ := state.Validators.GetByIndex(0) - commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) + commit, _, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals) require.NoError(t, err) - - block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes) + block, err := blockExec.CreateProposalBlock(height, state, commit, pa) require.Nil(t, block) require.ErrorContains(t, err, "an injected error") diff --git a/state/helpers_test.go b/state/helpers_test.go index 59d4344d7..841950689 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -38,7 +38,7 @@ func makeAndCommitGoodBlock( proposerAddr []byte, blockExec *sm.BlockExecutor, privVals map[string]types.PrivValidator, - evidence []types.Evidence) (sm.State, types.BlockID, *types.Commit, error) { + evidence []types.Evidence) (sm.State, types.BlockID, *types.ExtendedCommit, error) { // A good block passes state, blockID, err := makeAndApplyGoodBlock(state, height, lastCommit, proposerAddr, blockExec, evidence) if err != nil { @@ -88,8 +88,8 @@ func makeValidCommit( blockID types.BlockID, vals *types.ValidatorSet, privVals map[string]types.PrivValidator, -) (*types.Commit, []*types.Vote, error) { - sigs := make([]types.CommitSig, vals.Size()) +) (*types.ExtendedCommit, []*types.Vote, error) { + sigs := make([]types.ExtendedCommitSig, vals.Size()) votes := make([]*types.Vote, vals.Size()) for i := 0; i < vals.Size(); i++ { _, val := vals.GetByIndex(int32(i)) @@ -97,10 +97,14 @@ func makeValidCommit( if err != nil { return nil, nil, err } - sigs[i] = vote.CommitSig() + sigs[i] = vote.ExtendedCommitSig() votes[i] = vote } - return types.NewCommit(height, 0, blockID, sigs), votes, nil + return &types.ExtendedCommit{ + Height: height, + BlockID: blockID, + ExtendedSignatures: sigs, + }, votes, nil } func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) { diff --git a/state/mocks/block_store.go b/state/mocks/block_store.go index d449f6711..4e88d9ebe 100644 --- a/state/mocks/block_store.go +++ b/state/mocks/block_store.go @@ -120,6 +120,22 @@ func (_m *BlockStore) LoadBlockCommit(height int64) *types.Commit { return r0 } +// LoadBlockExtendedCommit provides a mock function with given fields: height +func (_m *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit { + ret := _m.Called(height) + + var r0 *types.ExtendedCommit + if rf, ok := ret.Get(0).(func(int64) *types.ExtendedCommit); ok { + r0 = rf(height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ExtendedCommit) + } + } + + return r0 +} + // LoadBlockMeta provides a mock function with given fields: height func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { ret := _m.Called(height) @@ -213,7 +229,7 @@ func (_m *BlockStore) PruneBlocks(height int64, _a1 state.State) (uint64, int64, } // SaveBlock provides a mock function with given fields: block, blockParts, seenCommit -func (_m *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { +func (_m *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) { _m.Called(block, blockParts, seenCommit) } diff --git a/state/rollback_test.go b/state/rollback_test.go index 9e2d03efc..3e8f33c82 100644 --- a/state/rollback_test.go +++ b/state/rollback_test.go @@ -118,7 +118,7 @@ func TestRollbackHard(t *testing.T) { partSet, err := block.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) - blockStore.SaveBlock(block, partSet, &types.Commit{Height: block.Height}) + blockStore.SaveBlock(block, partSet, &types.ExtendedCommit{Height: block.Height}) currState := state.State{ Version: tmstate.Version{ @@ -160,7 +160,7 @@ func TestRollbackHard(t *testing.T) { nextPartSet, err := nextBlock.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) - blockStore.SaveBlock(nextBlock, nextPartSet, &types.Commit{Height: nextBlock.Height}) + blockStore.SaveBlock(nextBlock, nextPartSet, &types.ExtendedCommit{Height: nextBlock.Height}) rollbackHeight, rollbackHash, err := state.Rollback(blockStore, stateStore, true) require.NoError(t, err) @@ -173,7 +173,7 @@ func TestRollbackHard(t *testing.T) { require.Equal(t, currState, loadedState) // resave the same block - blockStore.SaveBlock(nextBlock, nextPartSet, &types.Commit{Height: nextBlock.Height}) + blockStore.SaveBlock(nextBlock, nextPartSet, &types.ExtendedCommit{Height: nextBlock.Height}) params.Version.App = 11 diff --git a/state/services.go b/state/services.go index 0473b43b2..5553d160b 100644 --- a/state/services.go +++ b/state/services.go @@ -24,7 +24,7 @@ type BlockStore interface { LoadBlockMeta(height int64) *types.BlockMeta LoadBlock(height int64) *types.Block - SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) + SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) PruneBlocks(height int64, state State) (uint64, int64, error) @@ -34,6 +34,7 @@ type BlockStore interface { LoadBlockCommit(height int64) *types.Commit LoadSeenCommit(height int64) *types.Commit + LoadBlockExtendedCommit(height int64) *types.ExtendedCommit DeleteLatestBlock() error } diff --git a/state/state.go b/state/state.go index 171a4e713..d221786b3 100644 --- a/state/state.go +++ b/state/state.go @@ -271,7 +271,7 @@ func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time totalVotingPower := int64(0) for i, commitSig := range commit.Signatures { - if commitSig.Absent() { + if commitSig.BlockIDFlag == types.BlockIDFlagAbsent { continue } _, validator := validators.GetByAddress(commitSig.ValidatorAddress) diff --git a/state/validation_test.go b/state/validation_test.go index 152eb1d74..443e2ea85 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -57,7 +57,8 @@ func TestValidateBlockHeader(t *testing.T) { sm.EmptyEvidencePool{}, blockStore, ) - lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) + lastCommit := &types.Commit{} + var lastExtCommit *types.ExtendedCommit // some bad values wrongHash := tmhash.Sum([]byte("this hash is wrong")) @@ -108,9 +109,10 @@ func TestValidateBlockHeader(t *testing.T) { A good block passes */ var err error - state, _, lastCommit, err = makeAndCommitGoodBlock( + state, _, lastExtCommit, err = makeAndCommitGoodBlock( state, height, lastCommit, state.Validators.GetProposer().Address, blockExec, privVals, nil) require.NoError(t, err, "height %d", height) + lastCommit = lastExtCommit.StripExtensions() } } @@ -145,8 +147,9 @@ func TestValidateBlockCommit(t *testing.T) { sm.EmptyEvidencePool{}, blockStore, ) - lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) - wrongSigsCommit := types.NewCommit(1, 0, types.BlockID{}, nil) + lastCommit := &types.Commit{} + var lastExtCommit *types.ExtendedCommit + wrongSigsCommit := &types.Commit{Height: 1} badPrivVal := types.NewMockPV() for height := int64(1); height < validationTestsStopHeight; height++ { @@ -168,12 +171,12 @@ func TestValidateBlockCommit(t *testing.T) { time.Now(), ) require.NoError(t, err, "height %d", height) - wrongHeightCommit := types.NewCommit( - wrongHeightVote.Height, - wrongHeightVote.Round, - state.LastBlockID, - []types.CommitSig{wrongHeightVote.CommitSig()}, - ) + wrongHeightCommit := &types.Commit{ + Height: wrongHeightVote.Height, + Round: wrongHeightVote.Round, + BlockID: state.LastBlockID, + Signatures: []types.CommitSig{wrongHeightVote.CommitSig()}, + } block := makeBlock(state, height, wrongHeightCommit) err = blockExec.ValidateBlock(state, block) _, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight) @@ -197,7 +200,7 @@ func TestValidateBlockCommit(t *testing.T) { */ var err error var blockID types.BlockID - state, blockID, lastCommit, err = makeAndCommitGoodBlock( + state, blockID, lastExtCommit, err = makeAndCommitGoodBlock( state, height, lastCommit, @@ -207,6 +210,7 @@ func TestValidateBlockCommit(t *testing.T) { nil, ) require.NoError(t, err, "height %d", height) + lastCommit = lastExtCommit.StripExtensions() /* wrongSigsCommit is fine except for the extra bad precommit @@ -243,8 +247,12 @@ func TestValidateBlockCommit(t *testing.T) { goodVote.Signature, badVote.Signature = g.Signature, b.Signature - wrongSigsCommit = types.NewCommit(goodVote.Height, goodVote.Round, - blockID, []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()}) + wrongSigsCommit = &types.Commit{ + Height: goodVote.Height, + Round: goodVote.Round, + BlockID: blockID, + Signatures: []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()}, + } } } @@ -287,7 +295,8 @@ func TestValidateBlockEvidence(t *testing.T) { evpool, blockStore, ) - lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) + lastCommit := &types.Commit{} + var lastExtCommit *types.ExtendedCommit for height := int64(1); height < validationTestsStopHeight; height++ { proposerAddr := state.Validators.GetProposer().Address @@ -333,7 +342,7 @@ func TestValidateBlockEvidence(t *testing.T) { } var err error - state, _, lastCommit, err = makeAndCommitGoodBlock( + state, _, lastExtCommit, err = makeAndCommitGoodBlock( state, height, lastCommit, @@ -343,5 +352,7 @@ func TestValidateBlockEvidence(t *testing.T) { evidence, ) require.NoError(t, err, "height %d", height) + lastCommit = lastExtCommit.StripExtensions() + } } diff --git a/store/store.go b/store/store.go index c56622e47..3a33d6cac 100644 --- a/store/store.go +++ b/store/store.go @@ -237,11 +237,31 @@ func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit { } commit, err := types.CommitFromProto(pbc) if err != nil { - panic(fmt.Sprintf("Error reading block commit: %v", err)) + panic(fmt.Errorf("converting commit to proto: %w", err)) } return commit } +func (bs *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit { + var pbec = new(tmproto.ExtendedCommit) + bz, err := bs.db.Get(calcExtCommitKey(height)) + if err != nil { + panic(fmt.Errorf("fetching extended commit: %w", err)) + } + if len(bz) == 0 { + return nil + } + err = proto.Unmarshal(bz, pbec) + if err != nil { + panic(fmt.Errorf("decoding extended commit: %w", err)) + } + extCommit, err := types.ExtendedCommitFromProto(pbec) + if err != nil { + panic(fmt.Errorf("converting extended commit: %w", err)) + } + return extCommit +} + // LoadSeenCommit returns the locally seen Commit for the given height. // This is useful when we've seen a commit, but there has not yet been // a new block at `height + 1` that includes this commit in its block.LastCommit. @@ -261,7 +281,7 @@ func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { commit, err := types.CommitFromProto(pbc) if err != nil { - panic(fmt.Errorf("error from proto commit: %w", err)) + panic(fmt.Errorf("converting seen commit: %w", err)) } return commit } @@ -367,7 +387,7 @@ func (bs *BlockStore) PruneBlocks(height int64, state sm.State) (uint64, int64, // If all the nodes restart after committing a block, // we need this to reload the precommits to catch-up nodes to the // most recent height. Otherwise they'd stall at H-1. -func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { +func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) { if block == nil { panic("BlockStore can only save a non-nil block") } @@ -381,6 +401,10 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s if !blockParts.IsComplete() { panic("BlockStore can only save complete block part sets") } + if height != seenCommit.Height { + panic(fmt.Sprintf("BlockStore cannot save seen commit of a different height (block: %d, commit: %d)", + height, seenCommit.Height)) + } // Save block parts. This must be done before the block meta, since callers // typically load the block meta first as an indication that the block exists @@ -414,12 +438,18 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s // Save seen commit (seen +2/3 precommits for block) // NOTE: we can delete this at a later height - pbsc := seenCommit.ToProto() + pbsc := seenCommit.StripExtensions().ToProto() seenCommitBytes := mustEncode(pbsc) if err := bs.db.Set(calcSeenCommitKey(height), seenCommitBytes); err != nil { panic(err) } + pbec := seenCommit.ToProto() + extCommitBytes := mustEncode(pbec) + if err := bs.db.Set(calcExtCommitKey(height), extCommitBytes); err != nil { + panic(err) + } + // Done! bs.mtx.Lock() bs.height = height @@ -485,6 +515,10 @@ func calcSeenCommitKey(height int64) []byte { return []byte(fmt.Sprintf("SC:%v", height)) } +func calcExtCommitKey(height int64) []byte { + return []byte(fmt.Sprintf("EC:%v", height)) +} + func calcBlockHashKey(hash []byte) []byte { return []byte(fmt.Sprintf("BH:%x", hash)) } diff --git a/store/store_test.go b/store/store_test.go index 93c9227d6..31ad2d482 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -3,7 +3,6 @@ package store import ( "bytes" "fmt" - stdlog "log" "os" "runtime/debug" "strings" @@ -31,16 +30,25 @@ import ( // test. type cleanupFunc func() -// make a Commit with a single vote containing just the height and a timestamp -func makeTestCommit(height int64, timestamp time.Time) *types.Commit { - commitSigs := []types.CommitSig{{ - BlockIDFlag: types.BlockIDFlagCommit, - ValidatorAddress: tmrand.Bytes(crypto.AddressSize), - Timestamp: timestamp, - Signature: []byte("Signature"), +// make an extended commit with a single vote containing just the height and a +// timestamp +func makeTestExtCommit(height int64, timestamp time.Time) *types.ExtendedCommit { + extCommitSigs := []types.ExtendedCommitSig{{ + CommitSig: types.CommitSig{ + BlockIDFlag: types.BlockIDFlagCommit, + ValidatorAddress: tmrand.Bytes(crypto.AddressSize), + Timestamp: timestamp, + Signature: []byte("Signature"), + }, }} - return types.NewCommit(height, 0, - types.BlockID{Hash: []byte(""), PartSetHeader: types.PartSetHeader{Hash: []byte(""), Total: 2}}, commitSigs) + return &types.ExtendedCommit{ + Height: height, + BlockID: types.BlockID{ + Hash: crypto.CRandBytes(32), + PartSetHeader: types.PartSetHeader{Hash: crypto.CRandBytes(32), Total: 2}, + }, + ExtendedSignatures: extCommitSigs, + } } func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFunc) { @@ -119,38 +127,11 @@ func TestNewBlockStore(t *testing.T) { assert.Equal(t, bs.Height(), int64(0), "expecting empty bytes to be unmarshaled alright") } -func freshBlockStore() (*BlockStore, dbm.DB) { +func newInMemoryBlockStore() (*BlockStore, dbm.DB) { db := dbm.NewMemDB() return NewBlockStore(db), db } -var ( - state sm.State - block *types.Block - partSet *types.PartSet - part1 *types.Part - part2 *types.Part - seenCommit1 *types.Commit -) - -func TestMain(m *testing.M) { - var cleanup cleanupFunc - var err error - state, _, cleanup = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) - block = state.MakeBlock(state.LastBlockHeight+1, test.MakeNTxs(state.LastBlockHeight+1, 10), new(types.Commit), nil, state.Validators.GetProposer().Address) - - partSet, err = block.MakePartSet(2) - if err != nil { - stdlog.Fatal(err) - } - part1 = partSet.GetPart(0) - part2 = partSet.GetPart(1) - seenCommit1 = makeTestCommit(10, tmtime.Now()) - code := m.Run() - cleanup() - os.Exit(code) -} - // TODO: This test should be simplified ... func TestBlockStoreSaveLoadBlock(t *testing.T) { @@ -171,8 +152,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { block := state.MakeBlock(bs.Height()+1, nil, new(types.Commit), nil, state.Validators.GetProposer().Address) validPartSet, err := block.MakePartSet(2) require.NoError(t, err) - seenCommit := makeTestCommit(10, tmtime.Now()) - bs.SaveBlock(block, partSet, seenCommit) + part2 := validPartSet.GetPart(1) + + seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now()) + bs.SaveBlock(block, validPartSet, seenCommit) require.EqualValues(t, 1, bs.Base(), "expecting the new height to be changed") require.EqualValues(t, block.Header.Height, bs.Height(), "expecting the new height to be changed") @@ -191,11 +174,11 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { // End of setup, test data - commitAtH10 := makeTestCommit(10, tmtime.Now()) + commitAtH10 := makeTestExtCommit(10, tmtime.Now()).StripExtensions() tuples := []struct { block *types.Block parts *types.PartSet - seenCommit *types.Commit + seenCommit *types.ExtendedCommit wantPanic string wantErr bool @@ -208,7 +191,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { { block: newBlock(header1, commitAtH10), parts: validPartSet, - seenCommit: seenCommit1, + seenCommit: seenCommit, }, { @@ -224,10 +207,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { ChainID: "block_test", Time: tmtime.Now(), ProposerAddress: tmrand.Bytes(crypto.AddressSize)}, - makeTestCommit(5, tmtime.Now()), + makeTestExtCommit(5, tmtime.Now()).StripExtensions(), ), parts: validPartSet, - seenCommit: makeTestCommit(5, tmtime.Now()), + seenCommit: makeTestExtCommit(5, tmtime.Now()), }, { @@ -239,7 +222,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { { block: newBlock(header1, commitAtH10), parts: validPartSet, - seenCommit: seenCommit1, + seenCommit: seenCommit, corruptCommitInDB: true, // Corrupt the DB's commit entry wantPanic: "error reading block commit", }, @@ -247,7 +230,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { { block: newBlock(header1, commitAtH10), parts: validPartSet, - seenCommit: seenCommit1, + seenCommit: seenCommit, wantPanic: "unmarshal to tmproto.BlockMeta", corruptBlockInDB: true, // Corrupt the DB's block entry }, @@ -255,7 +238,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { { block: newBlock(header1, commitAtH10), parts: validPartSet, - seenCommit: seenCommit1, + seenCommit: seenCommit, // Expecting no error and we want a nil back eraseSeenCommitInDB: true, @@ -264,7 +247,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { { block: newBlock(header1, commitAtH10), parts: validPartSet, - seenCommit: seenCommit1, + seenCommit: seenCommit, corruptSeenCommitInDB: true, wantPanic: "error reading block seen commit", @@ -273,7 +256,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { { block: newBlock(header1, commitAtH10), parts: validPartSet, - seenCommit: seenCommit1, + seenCommit: seenCommit, // Expecting no error and we want a nil back eraseCommitInDB: true, @@ -290,7 +273,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { for i, tuple := range tuples { tuple := tuple - bs, db := freshBlockStore() + bs, db := newInMemoryBlockStore() // SaveBlock res, err, panicErr := doFn(func() (interface{}, error) { bs.SaveBlock(tuple.block, tuple.parts, tuple.seenCommit) @@ -377,7 +360,7 @@ func TestLoadBaseMeta(t *testing.T) { block := state.MakeBlock(h, test.MakeNTxs(h, 10), new(types.Commit), nil, state.Validators.GetProposer().Address) partSet, err := block.MakePartSet(2) require.NoError(t, err) - seenCommit := makeTestCommit(h, tmtime.Now()) + seenCommit := makeTestExtCommit(h, tmtime.Now()) bs.SaveBlock(block, partSet, seenCommit) } @@ -393,13 +376,18 @@ func TestLoadBaseMeta(t *testing.T) { } func TestLoadBlockPart(t *testing.T) { - bs, db := freshBlockStore() - height, index := int64(10), 1 + config := test.ResetTestRoot("blockchain_reactor_test") + + bs, db := newInMemoryBlockStore() + const height, index = 10, 1 loadPart := func() (interface{}, error) { part := bs.LoadBlockPart(height, index) return part, nil } + state, err := sm.MakeGenesisStateFromFile(config.GenesisFile()) + require.NoError(t, err) + // Initially no contents. // 1. Requesting for a non-existent block shouldn't fail res, _, panicErr := doFn(loadPart) @@ -407,13 +395,18 @@ func TestLoadBlockPart(t *testing.T) { require.Nil(t, res, "a non-existent block part should return nil") // 2. Next save a corrupted block then try to load it - err := db.Set(calcBlockPartKey(height, index), []byte("Tendermint")) + err = db.Set(calcBlockPartKey(height, index), []byte("Tendermint")) require.NoError(t, err) res, _, panicErr = doFn(loadPart) require.NotNil(t, panicErr, "expecting a non-nil panic") require.Contains(t, panicErr.Error(), "unmarshal to tmproto.Part failed") // 3. A good block serialized and saved to the DB should be retrievable + block := state.MakeBlock(height, nil, new(types.Commit), nil, state.Validators.GetProposer().Address) + partSet, err := block.MakePartSet(2) + require.NoError(t, err) + part1 := partSet.GetPart(0) + pb1, err := part1.ToProto() require.NoError(t, err) err = db.Set(calcBlockPartKey(height, index), mustEncode(pb1)) @@ -451,7 +444,7 @@ func TestPruneBlocks(t *testing.T) { block := state.MakeBlock(h, test.MakeNTxs(h, 10), new(types.Commit), nil, state.Validators.GetProposer().Address) partSet, err := block.MakePartSet(2) require.NoError(t, err) - seenCommit := makeTestCommit(h, tmtime.Now()) + seenCommit := makeTestExtCommit(h, tmtime.Now()) bs.SaveBlock(block, partSet, seenCommit) } @@ -527,7 +520,7 @@ func TestPruneBlocks(t *testing.T) { } func TestLoadBlockMeta(t *testing.T) { - bs, db := freshBlockStore() + bs, db := newInMemoryBlockStore() height := int64(10) loadMeta := func() (interface{}, error) { meta := bs.LoadBlockMeta(height) @@ -578,7 +571,7 @@ func TestLoadBlockMetaByHash(t *testing.T) { b1 := state.MakeBlock(state.LastBlockHeight+1, test.MakeNTxs(state.LastBlockHeight+1, 10), new(types.Commit), nil, state.Validators.GetProposer().Address) partSet, err := b1.MakePartSet(2) require.NoError(t, err) - seenCommit := makeTestCommit(1, tmtime.Now()) + seenCommit := makeTestExtCommit(1, tmtime.Now()) bs.SaveBlock(b1, partSet, seenCommit) baseBlock := bs.LoadBlockMetaByHash(b1.Hash()) @@ -595,7 +588,7 @@ func TestBlockFetchAtHeight(t *testing.T) { partSet, err := block.MakePartSet(2) require.NoError(t, err) - seenCommit := makeTestCommit(10, tmtime.Now()) + seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now()) bs.SaveBlock(block, partSet, seenCommit) require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") diff --git a/test/e2e/runner/evidence.go b/test/e2e/runner/evidence.go index 9b6e44267..5ac109f47 100644 --- a/test/e2e/runner/evidence.go +++ b/test/e2e/runner/evidence.go @@ -167,7 +167,7 @@ func generateLightClientAttackEvidence( // create a commit for the forged header blockID := makeBlockID(header.Hash(), 1000, []byte("partshash")) voteSet := types.NewVoteSet(chainID, forgedHeight, 0, tmproto.SignedMsgType(2), conflictingVals) - commit, err := test.MakeCommitFromVoteSet(blockID, voteSet, pv, forgedTime) + commit, err := test.MakeExtendedCommitFromVoteSet(blockID, voteSet, pv, forgedTime) if err != nil { return nil, err } @@ -176,7 +176,7 @@ func generateLightClientAttackEvidence( ConflictingBlock: &types.LightBlock{ SignedHeader: &types.SignedHeader{ Header: header, - Commit: commit, + Commit: commit.StripExtensions(), }, ValidatorSet: conflictingVals, }, diff --git a/types/block.go b/types/block.go index acafe9599..de60f66ea 100644 --- a/types/block.go +++ b/types/block.go @@ -600,16 +600,6 @@ type CommitSig struct { Signature []byte `json:"signature"` } -// NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit. -func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) CommitSig { - return CommitSig{ - BlockIDFlag: BlockIDFlagCommit, - ValidatorAddress: valAddr, - Timestamp: ts, - Signature: signature, - } -} - func MaxCommitBytes(valCount int) int64 { // From the repeated commit sig field var protoEncodingOverhead int64 = 2 @@ -624,16 +614,6 @@ func NewCommitSigAbsent() CommitSig { } } -// ForBlock returns true if CommitSig is for the block. -func (cs CommitSig) ForBlock() bool { - return cs.BlockIDFlag == BlockIDFlagCommit -} - -// Absent returns true if CommitSig is absent. -func (cs CommitSig) Absent() bool { - return cs.BlockIDFlag == BlockIDFlagAbsent -} - // CommitSig returns a string representation of CommitSig. // // 1. first 6 bytes of signature @@ -722,7 +702,6 @@ func (cs *CommitSig) ToProto() *tmproto.CommitSig { // FromProto sets a protobuf CommitSig to the given pointer. // It returns an error if the CommitSig is invalid. func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error { - cs.BlockIDFlag = BlockIDFlag(csp.BlockIdFlag) cs.ValidatorAddress = csp.ValidatorAddress cs.Timestamp = csp.Timestamp @@ -733,6 +712,95 @@ func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error { //------------------------------------- +// ExtendedCommitSig contains a commit signature along with its corresponding +// vote extension and vote extension signature. +type ExtendedCommitSig struct { + CommitSig // Commit signature + Extension []byte // Vote extension + ExtensionSignature []byte // Vote extension signature +} + +// NewExtendedCommitSigAbsent returns new ExtendedCommitSig with +// BlockIDFlagAbsent. Other fields are all empty. +func NewExtendedCommitSigAbsent() ExtendedCommitSig { + return ExtendedCommitSig{CommitSig: NewCommitSigAbsent()} +} + +// String returns a string representation of an ExtendedCommitSig. +// +// 1. commit sig +// 2. first 6 bytes of vote extension +// 3. first 6 bytes of vote extension signature +func (ecs ExtendedCommitSig) String() string { + return fmt.Sprintf("ExtendedCommitSig{%s with %X %X}", + ecs.CommitSig, + tmbytes.Fingerprint(ecs.Extension), + tmbytes.Fingerprint(ecs.ExtensionSignature), + ) +} + +// ValidateBasic checks whether the structure is well-formed. +func (ecs ExtendedCommitSig) ValidateBasic() error { + if err := ecs.CommitSig.ValidateBasic(); err != nil { + return err + } + + if ecs.BlockIDFlag == BlockIDFlagCommit { + if len(ecs.Extension) > MaxVoteExtensionSize { + return fmt.Errorf("vote extension is too big (max: %d)", MaxVoteExtensionSize) + } + if len(ecs.ExtensionSignature) == 0 { + return errors.New("vote extension signature is missing") + } + if len(ecs.ExtensionSignature) > MaxSignatureSize { + return fmt.Errorf("vote extension signature is too big (max: %d)", MaxSignatureSize) + } + return nil + } + + // We expect there to not be any vote extension or vote extension signature + // on nil or absent votes. + if len(ecs.Extension) != 0 { + return fmt.Errorf("vote extension is present for commit sig with block ID flag %v", ecs.BlockIDFlag) + } + if len(ecs.ExtensionSignature) != 0 { + return fmt.Errorf("vote extension signature is present for commit sig with block ID flag %v", ecs.BlockIDFlag) + } + return nil +} + +// ToProto converts the ExtendedCommitSig to its Protobuf representation. +func (ecs *ExtendedCommitSig) ToProto() *tmproto.ExtendedCommitSig { + if ecs == nil { + return nil + } + + return &tmproto.ExtendedCommitSig{ + BlockIdFlag: tmproto.BlockIDFlag(ecs.BlockIDFlag), + ValidatorAddress: ecs.ValidatorAddress, + Timestamp: ecs.Timestamp, + Signature: ecs.Signature, + Extension: ecs.Extension, + ExtensionSignature: ecs.ExtensionSignature, + } +} + +// FromProto populates the ExtendedCommitSig with values from the given +// Protobuf representation. Returns an error if the ExtendedCommitSig is +// invalid. +func (ecs *ExtendedCommitSig) FromProto(ecsp tmproto.ExtendedCommitSig) error { + ecs.BlockIDFlag = BlockIDFlag(ecsp.BlockIdFlag) + ecs.ValidatorAddress = ecsp.ValidatorAddress + ecs.Timestamp = ecsp.Timestamp + ecs.Signature = ecsp.Signature + ecs.Extension = ecsp.Extension + ecs.ExtensionSignature = ecsp.ExtensionSignature + + return ecs.ValidateBasic() +} + +//------------------------------------- + // Commit contains the evidence that a block was committed by a set of validators. // NOTE: Commit is empty for height 1, but never nil. type Commit struct { @@ -748,42 +816,12 @@ type Commit struct { // Memoized in first call to corresponding method. // NOTE: can't memoize in constructor because constructor isn't used for // unmarshaling. - hash tmbytes.HexBytes - bitArray *bits.BitArray + hash tmbytes.HexBytes } -// NewCommit returns a new Commit. -func NewCommit(height int64, round int32, blockID BlockID, commitSigs []CommitSig) *Commit { - return &Commit{ - Height: height, - Round: round, - BlockID: blockID, - Signatures: commitSigs, - } -} - -// CommitToVoteSet constructs a VoteSet from the Commit and validator set. -// Panics if signatures from the commit can't be added to the voteset. -// Inverse of VoteSet.MakeCommit(). -func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSet { - voteSet := NewVoteSet(chainID, commit.Height, commit.Round, tmproto.PrecommitType, vals) - for idx, commitSig := range commit.Signatures { - if commitSig.Absent() { - continue // OK, some precommits can be missing. - } - vote := commit.GetVote(int32(idx)) - if err := vote.ValidateBasic(); err != nil { - panic(fmt.Errorf("failed to validate vote reconstructed from LastCommit: %w", err)) - } - added, err := voteSet.AddVote(vote) - if !added || err != nil { - panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) - } - } - return voteSet -} - -// GetVote converts the CommitSig for the given valIdx to a Vote. +// GetVote converts the CommitSig for the given valIdx to a Vote. Commits do +// not contain vote extensions, so the vote extension and vote extension +// signature will not be present in the returned vote. // Returns nil if the precommit at valIdx is nil. // Panics if valIdx >= commit.Size(). func (commit *Commit) GetVote(valIdx int32) *Vote { @@ -814,26 +852,7 @@ func (commit *Commit) VoteSignBytes(chainID string, valIdx int32) []byte { return VoteSignBytes(chainID, v) } -// Type returns the vote type of the commit, which is always VoteTypePrecommit -// Implements VoteSetReader. -func (commit *Commit) Type() byte { - return byte(tmproto.PrecommitType) -} - -// GetHeight returns height of the commit. -// Implements VoteSetReader. -func (commit *Commit) GetHeight() int64 { - return commit.Height -} - -// GetRound returns height of the commit. -// Implements VoteSetReader. -func (commit *Commit) GetRound() int32 { - return commit.Round -} - // Size returns the number of signatures in the commit. -// Implements VoteSetReader. func (commit *Commit) Size() int { if commit == nil { return 0 @@ -841,33 +860,6 @@ func (commit *Commit) Size() int { return len(commit.Signatures) } -// BitArray returns a BitArray of which validators voted for BlockID or nil in this commit. -// Implements VoteSetReader. -func (commit *Commit) BitArray() *bits.BitArray { - if commit.bitArray == nil { - commit.bitArray = bits.NewBitArray(len(commit.Signatures)) - for i, commitSig := range commit.Signatures { - // TODO: need to check the BlockID otherwise we could be counting conflicts, - // not just the one with +2/3 ! - commit.bitArray.SetIndex(i, !commitSig.Absent()) - } - } - return commit.bitArray -} - -// GetByIndex returns the vote corresponding to a given validator index. -// Panics if `index >= commit.Size()`. -// Implements VoteSetReader. -func (commit *Commit) GetByIndex(valIdx int32) *Vote { - return commit.GetVote(valIdx) -} - -// IsCommit returns true if there is at least one signature. -// Implements VoteSetReader. -func (commit *Commit) IsCommit() bool { - return len(commit.Signatures) != 0 -} - // ValidateBasic performs basic validation that doesn't involve state data. // Does not actually check the cryptographic signatures. func (commit *Commit) ValidateBasic() error { @@ -991,7 +983,209 @@ func CommitFromProto(cp *tmproto.Commit) (*Commit, error) { return commit, commit.ValidateBasic() } -//----------------------------------------------------------------------------- +//------------------------------------- + +// ExtendedCommit is similar to Commit, except that its signatures also retain +// their corresponding vote extensions and vote extension signatures. +type ExtendedCommit struct { + Height int64 + Round int32 + BlockID BlockID + ExtendedSignatures []ExtendedCommitSig + + bitArray *bits.BitArray +} + +// Clone creates a deep copy of this extended commit. +func (ec *ExtendedCommit) Clone() *ExtendedCommit { + sigs := make([]ExtendedCommitSig, len(ec.ExtendedSignatures)) + copy(sigs, ec.ExtendedSignatures) + ecc := *ec + ecc.ExtendedSignatures = sigs + return &ecc +} + +// ToVoteSet constructs a VoteSet from the Commit and validator set. +// Panics if signatures from the commit can't be added to the voteset. +// Inverse of VoteSet.MakeExtendedCommit(). +func (ec *ExtendedCommit) ToVoteSet(chainID string, vals *ValidatorSet) *VoteSet { + voteSet := NewVoteSet(chainID, ec.Height, ec.Round, tmproto.PrecommitType, vals) + for idx, ecs := range ec.ExtendedSignatures { + if ecs.BlockIDFlag == BlockIDFlagAbsent { + continue // OK, some precommits can be missing. + } + vote := ec.GetExtendedVote(int32(idx)) + if err := vote.ValidateWithExtension(); err != nil { + panic(fmt.Errorf("failed to validate vote reconstructed from LastCommit: %w", err)) + } + added, err := voteSet.AddVote(vote) + if !added || err != nil { + panic(fmt.Errorf("failed to reconstruct vote set from extended commit: %w", err)) + } + } + return voteSet +} + +// StripExtensions converts an ExtendedCommit to a Commit by removing all vote +// extension-related fields. +func (ec *ExtendedCommit) StripExtensions() *Commit { + cs := make([]CommitSig, len(ec.ExtendedSignatures)) + for idx, ecs := range ec.ExtendedSignatures { + cs[idx] = ecs.CommitSig + } + return &Commit{ + Height: ec.Height, + Round: ec.Round, + BlockID: ec.BlockID, + Signatures: cs, + } +} + +// GetExtendedVote converts the ExtendedCommitSig for the given validator +// index to a Vote with a vote extensions. +// It panics if valIndex is out of range. +func (ec *ExtendedCommit) GetExtendedVote(valIndex int32) *Vote { + ecs := ec.ExtendedSignatures[valIndex] + return &Vote{ + Type: tmproto.PrecommitType, + Height: ec.Height, + Round: ec.Round, + BlockID: ecs.BlockID(ec.BlockID), + Timestamp: ecs.Timestamp, + ValidatorAddress: ecs.ValidatorAddress, + ValidatorIndex: valIndex, + Signature: ecs.Signature, + Extension: ecs.Extension, + ExtensionSignature: ecs.ExtensionSignature, + } +} + +// Type returns the vote type of the extended commit, which is always +// VoteTypePrecommit +// Implements VoteSetReader. +func (ec *ExtendedCommit) Type() byte { return byte(tmproto.PrecommitType) } + +// GetHeight returns height of the extended commit. +// Implements VoteSetReader. +func (ec *ExtendedCommit) GetHeight() int64 { return ec.Height } + +// GetRound returns height of the extended commit. +// Implements VoteSetReader. +func (ec *ExtendedCommit) GetRound() int32 { return ec.Round } + +// Size returns the number of signatures in the extended commit. +// Implements VoteSetReader. +func (ec *ExtendedCommit) Size() int { + if ec == nil { + return 0 + } + return len(ec.ExtendedSignatures) +} + +// BitArray returns a BitArray of which validators voted for BlockID or nil in +// this extended commit. +// Implements VoteSetReader. +func (ec *ExtendedCommit) BitArray() *bits.BitArray { + if ec.bitArray == nil { + ec.bitArray = bits.NewBitArray(len(ec.ExtendedSignatures)) + for i, extCommitSig := range ec.ExtendedSignatures { + // TODO: need to check the BlockID otherwise we could be counting conflicts, + // not just the one with +2/3 ! + ec.bitArray.SetIndex(i, extCommitSig.BlockIDFlag != BlockIDFlagAbsent) + } + } + return ec.bitArray +} + +// GetByIndex returns the vote corresponding to a given validator index. +// Panics if `index >= extCommit.Size()`. +// Implements VoteSetReader. +func (ec *ExtendedCommit) GetByIndex(valIdx int32) *Vote { + return ec.GetExtendedVote(valIdx) +} + +// IsCommit returns true if there is at least one signature. +// Implements VoteSetReader. +func (ec *ExtendedCommit) IsCommit() bool { + return len(ec.ExtendedSignatures) != 0 +} + +// ValidateBasic checks whether the extended commit is well-formed. Does not +// actually check the cryptographic signatures. +func (ec *ExtendedCommit) ValidateBasic() error { + if ec.Height < 0 { + return errors.New("negative Height") + } + if ec.Round < 0 { + return errors.New("negative Round") + } + + if ec.Height >= 1 { + if len(ec.BlockID.Hash) == 0 { + return errors.New("commit cannot be for nil block") + } + + if len(ec.ExtendedSignatures) == 0 { + return errors.New("no signatures in commit") + } + for i, extCommitSig := range ec.ExtendedSignatures { + if err := extCommitSig.ValidateBasic(); err != nil { + return fmt.Errorf("wrong ExtendedCommitSig #%d: %v", i, err) + } + } + } + return nil +} + +// ToProto converts ExtendedCommit to protobuf +func (ec *ExtendedCommit) ToProto() *tmproto.ExtendedCommit { + if ec == nil { + return nil + } + + c := new(tmproto.ExtendedCommit) + sigs := make([]tmproto.ExtendedCommitSig, len(ec.ExtendedSignatures)) + for i := range ec.ExtendedSignatures { + sigs[i] = *ec.ExtendedSignatures[i].ToProto() + } + c.ExtendedSignatures = sigs + + c.Height = ec.Height + c.Round = ec.Round + c.BlockID = ec.BlockID.ToProto() + + return c +} + +// ExtendedCommitFromProto constructs an ExtendedCommit from the given Protobuf +// representation. It returns an error if the extended commit is invalid. +func ExtendedCommitFromProto(ecp *tmproto.ExtendedCommit) (*ExtendedCommit, error) { + if ecp == nil { + return nil, errors.New("nil ExtendedCommit") + } + + extCommit := new(ExtendedCommit) + + bi, err := BlockIDFromProto(&ecp.BlockID) + if err != nil { + return nil, err + } + + sigs := make([]ExtendedCommitSig, len(ecp.ExtendedSignatures)) + for i := range ecp.ExtendedSignatures { + if err := sigs[i].FromProto(ecp.ExtendedSignatures[i]); err != nil { + return nil, err + } + } + extCommit.ExtendedSignatures = sigs + extCommit.Height = ecp.Height + extCommit.Round = ecp.Round + extCommit.BlockID = *bi + + return extCommit, extCommit.ValidateBasic() +} + +//------------------------------------- // Data contains the set of transactions included in the block type Data struct { @@ -1253,3 +1447,9 @@ func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) { return blockID, blockID.ValidateBasic() } + +// ProtoBlockIDIsNil is similar to the IsNil function on BlockID, but for the +// Protobuf representation. +func ProtoBlockIDIsNil(bID *tmproto.BlockID) bool { + return len(bID.Hash) == 0 && ProtoPartSetHeaderIsZero(&bID.PartSetHeader) +} diff --git a/types/block_test.go b/types/block_test.go index 041398dc4..bd0526c39 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -38,14 +38,14 @@ func TestBlockAddEvidence(t *testing.T) { h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev, err := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain") require.NoError(t, err) evList := []Evidence{ev} - block := MakeBlock(h, txs, commit, evList) + block := MakeBlock(h, txs, extCommit.StripExtensions(), evList) require.NotNil(t, block) require.Equal(t, 1, len(block.Evidence.Evidence)) require.NotNil(t, block.EvidenceHash) @@ -59,8 +59,9 @@ func TestBlockValidateBasic(t *testing.T) { h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) + commit := extCommit.StripExtensions() ev, err := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain") require.NoError(t, err) @@ -131,14 +132,14 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) { h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev, err := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain") require.NoError(t, err) evList := []Evidence{ev} - partSet, err := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(512) + partSet, err := MakeBlock(h, []Tx{Tx("Hello World")}, extCommit.StripExtensions(), evList).MakePartSet(512) require.NoError(t, err) assert.NotNil(t, partSet) @@ -151,14 +152,14 @@ func TestBlockHashesTo(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev, err := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain") require.NoError(t, err) evList := []Evidence{ev} - block := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList) + block := MakeBlock(h, []Tx{Tx("Hello World")}, extCommit.StripExtensions(), evList) block.ValidatorsHash = valSet.Hash() assert.False(t, block.HashesTo([]byte{})) assert.False(t, block.HashesTo([]byte("something else"))) @@ -230,7 +231,7 @@ func TestCommit(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) + commit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) assert.Equal(t, h-1, commit.Height) @@ -243,7 +244,7 @@ func TestCommit(t *testing.T) { require.NotNil(t, commit.BitArray()) assert.Equal(t, bits.NewBitArray(10).Size(), commit.BitArray().Size()) - assert.Equal(t, voteWithoutExtension(voteSet.GetByIndex(0)), commit.GetByIndex(0)) + assert.Equal(t, voteSet.GetByIndex(0), commit.GetByIndex(0)) assert.True(t, commit.IsCommit()) } @@ -438,11 +439,11 @@ func randCommit(now time.Time) *Commit { lastID := makeBlockIDRandom() h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, now) + commit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, now) if err != nil { panic(err) } - return commit + return commit.StripExtensions() } func hexBytesFromString(s string) bytes.HexBytes { @@ -514,21 +515,21 @@ func TestBlockMaxDataBytesNoEvidence(t *testing.T) { } } -func TestCommitToVoteSet(t *testing.T) { +func TestExtendedCommitToVoteSet(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(lastID, h-1, 1, voteSet, vals, time.Now()) assert.NoError(t, err) chainID := voteSet.ChainID() - voteSet2 := CommitToVoteSet(chainID, commit, valSet) + voteSet2 := extCommit.ToVoteSet(chainID, valSet) for i := int32(0); int(i) < len(vals); i++ { - vote1 := voteWithoutExtension(voteSet.GetByIndex(i)) + vote1 := voteSet.GetByIndex(i) vote2 := voteSet2.GetByIndex(i) - vote3 := commit.GetVote(i) + vote3 := extCommit.GetExtendedVote(i) vote1bz, err := vote1.ToProto().Marshal() require.NoError(t, err) @@ -587,12 +588,12 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { } if tc.valid { - commit := voteSet.MakeCommit() // panics without > 2/3 valid votes - assert.NotNil(t, commit) - err := valSet.VerifyCommit(voteSet.ChainID(), blockID, height-1, commit) + extCommit := voteSet.MakeExtendedCommit() // panics without > 2/3 valid votes + assert.NotNil(t, extCommit) + err := valSet.VerifyCommit(voteSet.ChainID(), blockID, height-1, extCommit.StripExtensions()) assert.Nil(t, err) } else { - assert.Panics(t, func() { voteSet.MakeCommit() }) + assert.Panics(t, func() { voteSet.MakeExtendedCommit() }) } } } diff --git a/types/evidence.go b/types/evidence.go index ba28060bf..b07646073 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -256,7 +256,7 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator // validators who are in the commonVals and voted for the lunatic header if l.ConflictingHeaderIsInvalid(trusted.Header) { for _, commitSig := range l.ConflictingBlock.Commit.Signatures { - if !commitSig.ForBlock() { + if commitSig.BlockIDFlag != BlockIDFlagCommit { continue } @@ -276,12 +276,12 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator // only need a single loop to find the validators that voted twice. for i := 0; i < len(l.ConflictingBlock.Commit.Signatures); i++ { sigA := l.ConflictingBlock.Commit.Signatures[i] - if sigA.Absent() { + if sigA.BlockIDFlag != BlockIDFlagCommit { continue } sigB := trusted.Commit.Signatures[i] - if sigB.Absent() { + if sigB.BlockIDFlag != BlockIDFlagCommit { continue } diff --git a/types/evidence_test.go b/types/evidence_test.go index e4d253531..363ebb11e 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -99,8 +99,10 @@ func TestLightClientAttackEvidenceBasic(t *testing.T) { header := makeHeaderRandom() header.Height = height blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) - commit, err := MakeCommit(blockID, height, 1, voteSet, privVals, defaultVoteTime) + extCommit, err := MakeExtCommit(blockID, height, 1, voteSet, privVals, defaultVoteTime) require.NoError(t, err) + commit := extCommit.StripExtensions() + lcae := &LightClientAttackEvidence{ ConflictingBlock: &LightBlock{ SignedHeader: &SignedHeader{ @@ -159,8 +161,10 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) { header.Height = height header.ValidatorsHash = valSet.Hash() blockID := makeBlockID(header.Hash(), math.MaxInt32, tmhash.Sum([]byte("partshash"))) - commit, err := MakeCommit(blockID, height, 1, voteSet, privVals, time.Now()) + extCommit, err := MakeExtCommit(blockID, height, 1, voteSet, privVals, time.Now()) require.NoError(t, err) + commit := extCommit.StripExtensions() + lcae := &LightClientAttackEvidence{ ConflictingBlock: &LightBlock{ SignedHeader: &SignedHeader{ diff --git a/types/part_set.go b/types/part_set.go index 5e76b57fa..153e11df4 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -145,6 +145,12 @@ func PartSetHeaderFromProto(ppsh *tmproto.PartSetHeader) (*PartSetHeader, error) return psh, psh.ValidateBasic() } +// ProtoPartSetHeaderIsZero is similar to the IsZero function for +// PartSetHeader, but for the Protobuf representation. +func ProtoPartSetHeaderIsZero(ppsh *tmproto.PartSetHeader) bool { + return ppsh.Total == 0 && len(ppsh.Hash) == 0 +} + //------------------------------------- type PartSet struct { diff --git a/types/priv_validator.go b/types/priv_validator.go index 7e878d797..961da7360 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -77,7 +77,6 @@ func (pv MockPV) SignVote(chainID string, vote *tmproto.Vote) error { } signBytes := VoteSignBytes(useChainID, vote) - extSignBytes := VoteExtensionSignBytes(useChainID, vote) sig, err := pv.PrivKey.Sign(signBytes) if err != nil { return err @@ -85,14 +84,15 @@ func (pv MockPV) SignVote(chainID string, vote *tmproto.Vote) error { vote.Signature = sig var extSig []byte - // We only sign vote extensions for precommits - if vote.Type == tmproto.PrecommitType { + // We only sign vote extensions for non-nil precommits + if vote.Type == tmproto.PrecommitType && !ProtoBlockIDIsNil(&vote.BlockID) { + extSignBytes := VoteExtensionSignBytes(useChainID, vote) extSig, err = pv.PrivKey.Sign(extSignBytes) if err != nil { return err } } else if len(vote.Extension) > 0 { - return errors.New("unexpected vote extension - vote extensions are only allowed in precommits") + return errors.New("unexpected vote extension - vote extensions are only allowed in non-nil precommits") } vote.ExtensionSignature = extSig return nil diff --git a/types/test_util.go b/types/test_util.go index ecb4d75d6..3d3965f4c 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -9,8 +9,8 @@ import ( "github.com/tendermint/tendermint/version" ) -func MakeCommit(blockID BlockID, height int64, round int32, - voteSet *VoteSet, validators []PrivValidator, now time.Time) (*Commit, error) { +func MakeExtCommit(blockID BlockID, height int64, round int32, + voteSet *VoteSet, validators []PrivValidator, now time.Time) (*ExtendedCommit, error) { // all sign for i := 0; i < len(validators); i++ { @@ -34,7 +34,7 @@ func MakeCommit(blockID BlockID, height int64, round int32, } } - return voteSet.MakeCommit(), nil + return voteSet.MakeExtendedCommit(), nil } func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (signed bool, err error) { @@ -101,13 +101,3 @@ func MakeBlock(height int64, txs []Tx, lastCommit *Commit, evidence []Evidence) block.fillHeader() return block } - -// Votes constructed from commits don't have extensions, because we don't store -// the extensions themselves in the commit. This method is used to construct a -// copy of a vote, but nil its extension and signature. -func voteWithoutExtension(v *Vote) *Vote { - vc := v.Copy() - vc.Extension = nil - vc.ExtensionSignature = nil - return vc -} diff --git a/types/validation.go b/types/validation.go index 3601f0479..b62a2c8f1 100644 --- a/types/validation.go +++ b/types/validation.go @@ -34,10 +34,10 @@ func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID, votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 // ignore all absent signatures - ignore := func(c CommitSig) bool { return c.Absent() } + ignore := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagAbsent } // only count the signatures that are for the block - count := func(c CommitSig) bool { return c.ForBlock() } + count := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagCommit } // attempt to batch verify if shouldBatchVerify(vals, commit) { @@ -67,7 +67,7 @@ func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID, votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 // ignore all commit signatures that are not for the block - ignore := func(c CommitSig) bool { return !c.ForBlock() } + ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit } // count all the remaining signatures count := func(c CommitSig) bool { return true } @@ -111,7 +111,7 @@ func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commi votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) // ignore all commit signatures that are not for the block - ignore := func(c CommitSig) bool { return !c.ForBlock() } + ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit } // count all the remaining signatures count := func(c CommitSig) bool { return true } diff --git a/types/validation_test.go b/types/validation_test.go index 518a1512d..f1f349a78 100644 --- a/types/validation_test.go +++ b/types/validation_test.go @@ -94,7 +94,12 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) { vi++ } - commit := NewCommit(tc.height, round, tc.blockID, sigs) + commit := &Commit{ + Height: tc.height, + Round: round, + BlockID: tc.blockID, + Signatures: sigs, + } err := valSet.VerifyCommit(chainID, blockID, height, commit) if tc.expErr { @@ -138,8 +143,9 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) { ) voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) - commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(blockID, h, 0, voteSet, vals, time.Now()) require.NoError(t, err) + commit := extCommit.StripExtensions() require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit)) // malleate 4th signature @@ -165,8 +171,9 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign ) voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) - commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(blockID, h, 0, voteSet, vals, time.Now()) require.NoError(t, err) + commit := extCommit.StripExtensions() require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit)) // malleate 4th signature (3 signatures are enough for 2/3+) @@ -190,8 +197,9 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin ) voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) - commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) + extCommit, err := MakeExtCommit(blockID, h, 0, voteSet, vals, time.Now()) require.NoError(t, err) + commit := extCommit.StripExtensions() require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit)) // malleate 3rd signature (2 signatures are enough for 1/3+ trust level) @@ -211,10 +219,11 @@ func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) { var ( blockID = makeBlockIDRandom() voteSet, originalValset, vals = randVoteSet(1, 1, tmproto.PrecommitType, 6, 1) - commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) + extCommit, err = MakeExtCommit(blockID, 1, 1, voteSet, vals, time.Now()) newValSet, _ = RandValidatorSet(2, 1) ) require.NoError(t, err) + commit := extCommit.StripExtensions() testCases := []struct { valSet *ValidatorSet @@ -252,11 +261,11 @@ func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) { var ( blockID = makeBlockIDRandom() voteSet, valSet, vals = randVoteSet(1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower) - commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) + extCommit, err = MakeExtCommit(blockID, 1, 1, voteSet, vals, time.Now()) ) require.NoError(t, err) - err = valSet.VerifyCommitLightTrusting("test_chain_id", commit, + err = valSet.VerifyCommitLightTrusting("test_chain_id", extCommit.StripExtensions(), tmmath.Fraction{Numerator: 25, Denominator: 55}) if assert.Error(t, err) { assert.Contains(t, err.Error(), "int64 overflow") diff --git a/types/vote.go b/types/vote.go index d15dbf872..13053f14f 100644 --- a/types/vote.go +++ b/types/vote.go @@ -14,6 +14,9 @@ import ( const ( nilVoteStr string = "nil-Vote" + + // The maximum supported number of bytes in a vote extension. + MaxVoteExtensionSize int = 1024 * 1024 ) var ( @@ -109,6 +112,26 @@ func (vote *Vote) CommitSig() CommitSig { } } +// ExtendedCommitSig attempts to construct an ExtendedCommitSig from this vote. +// Panics if either the vote extension signature is missing or if the block ID +// is not either empty or complete. +func (vote *Vote) ExtendedCommitSig() ExtendedCommitSig { + if vote == nil { + return NewExtendedCommitSigAbsent() + } + + cs := vote.CommitSig() + if vote.BlockID.IsComplete() && len(vote.ExtensionSignature) == 0 { + panic(fmt.Sprintf("Invalid vote %v - BlockID is complete but missing vote extension signature", vote)) + } + + return ExtendedCommitSig{ + CommitSig: cs, + Extension: vote.Extension, + ExtensionSignature: vote.ExtensionSignature, + } +} + // VoteSignBytes returns the proto-encoding of the canonicalized Vote, for // signing. Panics if the marshaling fails. // @@ -216,12 +239,10 @@ func (vote *Vote) VerifyWithExtension(chainID string, pubKey crypto.PubKey) erro if err != nil { return err } - // We only verify vote extension signatures for precommits. - if vote.Type == tmproto.PrecommitType { + // We only verify vote extension signatures for non-nil precommits. + if vote.Type == tmproto.PrecommitType && !ProtoBlockIDIsNil(&v.BlockID) { extSignBytes := VoteExtensionSignBytes(chainID, v) - // TODO: Remove extension signature nil check to enforce vote extension - // signing once we resolve https://github.com/tendermint/tendermint/issues/8272 - if vote.ExtensionSignature != nil && !pubKey.VerifySignature(extSignBytes, vote.ExtensionSignature) { + if !pubKey.VerifySignature(extSignBytes, vote.ExtensionSignature) { return ErrVoteInvalidSignature } } @@ -273,8 +294,10 @@ func (vote *Vote) ValidateBasic() error { return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) } - // We should only ever see vote extensions in precommits. - if vote.Type != tmproto.PrecommitType { + // We should only ever see vote extensions in non-nil precommits, otherwise + // this is a violation of the specification. + // https://github.com/tendermint/tendermint/issues/8487 + if vote.Type != tmproto.PrecommitType || (vote.Type == tmproto.PrecommitType && len(vote.BlockID.Hash) == 0) { if len(vote.Extension) > 0 { return errors.New("unexpected vote extension") } @@ -294,12 +317,9 @@ func (vote *Vote) ValidateWithExtension() error { return err } - // We should always see vote extension signatures in precommits - if vote.Type == tmproto.PrecommitType { - // TODO(thane): Remove extension length check once - // https://github.com/tendermint/tendermint/issues/8272 is - // resolved. - if len(vote.Extension) > 0 && len(vote.ExtensionSignature) == 0 { + // We should always see vote extension signatures in non-nil precommits + if vote.Type == tmproto.PrecommitType && len(vote.BlockID.Hash) != 0 { + if len(vote.ExtensionSignature) == 0 { return errors.New("vote extension signature is missing") } if len(vote.ExtensionSignature) > MaxSignatureSize { diff --git a/types/vote_set.go b/types/vote_set.go index 738e360ca..efbbaa57e 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -228,13 +228,6 @@ func (voteSet *VoteSet) getVote(valIndex int32, blockKey string) (vote *Vote, ok return nil, false } -func (voteSet *VoteSet) GetVotes() []*Vote { - if voteSet == nil { - return nil - } - return voteSet.votes -} - // Assumes signature is valid. // If conflicting vote exists, returns it. func (voteSet *VoteSet) addVerifiedVote( @@ -611,36 +604,41 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) { //-------------------------------------------------------------------------------- // Commit -// MakeCommit constructs a Commit from the VoteSet. It only includes precommits -// for the block, which has 2/3+ majority, and nil. +// MakeExtendedCommit constructs a Commit from the VoteSet. It only includes +// precommits for the block, which has 2/3+ majority, and nil. // // Panics if the vote type is not PrecommitType or if there's no +2/3 votes for // a single block. -func (voteSet *VoteSet) MakeCommit() *Commit { +func (voteSet *VoteSet) MakeExtendedCommit() *ExtendedCommit { if voteSet.signedMsgType != tmproto.PrecommitType { - panic("Cannot MakeCommit() unless VoteSet.Type is PrecommitType") + panic("Cannot MakeExtendCommit() unless VoteSet.Type is PrecommitType") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() // Make sure we have a 2/3 majority if voteSet.maj23 == nil { - panic("Cannot MakeCommit() unless a blockhash has +2/3") + panic("Cannot MakeExtendCommit() unless a blockhash has +2/3") } - // For every validator, get the precommit - commitSigs := make([]CommitSig, len(voteSet.votes)) + // For every validator, get the precommit with extensions + sigs := make([]ExtendedCommitSig, len(voteSet.votes)) for i, v := range voteSet.votes { - commitSig := v.CommitSig() + sig := v.ExtendedCommitSig() // if block ID exists but doesn't match, exclude sig - if commitSig.ForBlock() && !v.BlockID.Equals(*voteSet.maj23) { - commitSig = NewCommitSigAbsent() + if sig.BlockIDFlag == BlockIDFlagCommit && !v.BlockID.Equals(*voteSet.maj23) { + sig = NewExtendedCommitSigAbsent() } - commitSigs[i] = commitSig + sigs[i] = sig } - return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs) + return &ExtendedCommit{ + Height: voteSet.GetHeight(), + Round: voteSet.GetRound(), + BlockID: *voteSet.maj23, + ExtendedSignatures: sigs, + } } //-------------------------------------------------------------------------------- diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 5d70ed3bd..2246bb57a 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -427,7 +427,7 @@ func TestVoteSet_MakeCommit(t *testing.T) { } // MakeCommit should fail. - assert.Panics(t, func() { voteSet.MakeCommit() }, "Doesn't have +2/3 majority") + assert.Panics(t, func() { voteSet.MakeExtendedCommit() }, "Doesn't have +2/3 majority") // 7th voted for some other block. { @@ -464,13 +464,13 @@ func TestVoteSet_MakeCommit(t *testing.T) { require.NoError(t, err) } - commit := voteSet.MakeCommit() + extCommit := voteSet.MakeExtendedCommit() // Commit should have 10 elements - assert.Equal(t, 10, len(commit.Signatures)) + assert.Equal(t, 10, len(extCommit.ExtendedSignatures)) // Ensure that Commit is good. - if err := commit.ValidateBasic(); err != nil { + if err := extCommit.ValidateBasic(); err != nil { t.Errorf("error in Commit.ValidateBasic(): %v", err) } } diff --git a/types/vote_test.go b/types/vote_test.go index 493019dac..7de1932db 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -216,26 +216,22 @@ func TestVoteExtension(t *testing.T) { includeSignature: true, expectError: false, }, - // TODO(thane): Re-enable once - // https://github.com/tendermint/tendermint/issues/8272 is resolved - //{ - // name: "no extension signature", - // extension: []byte("extension"), - // includeSignature: false, - // expectError: true, - //}, + { + name: "no extension signature", + extension: []byte("extension"), + includeSignature: false, + expectError: true, + }, { name: "empty extension", includeSignature: true, expectError: false, }, - // TODO: Re-enable once - // https://github.com/tendermint/tendermint/issues/8272 is resolved. - //{ - // name: "no extension and no signature", - // includeSignature: false, - // expectError: true, - //}, + { + name: "no extension and no signature", + includeSignature: false, + expectError: true, + }, } for _, tc := range testCases {