Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix using bad base block #78

Merged
merged 1 commit into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Blockchain interface {
InsertBlockAtHeight(block.Height, block.Block)
BlockAtHeight(block.Height) (block.Block, bool)
BlockExistsAtHeight(block.Height) bool
LatestBaseBlock() block.Block
}

// A SaveRestorer defines a storage interface for the State.
Expand Down Expand Up @@ -756,10 +757,7 @@ func (p *Process) syncLatestCommit(latestCommit LatestCommit) {

// Validate the commits
signatories := map[id.Signatory]struct{}{}
baseBlock, ok := p.blockchain.BlockAtHeight(0)
if !ok {
panic("no genesis block")
}
baseBlock := p.blockchain.LatestBaseBlock()
for _, sig := range baseBlock.Header().Signatories() {
signatories[sig] = struct{}{}
}
Expand Down
5 changes: 5 additions & 0 deletions replica/rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type BlockIterator interface {
// NextBlock returns the `block.Txs`, `block.Plan` and the parent
// `block.State` for the given `block.Height`.
NextBlock(block.Kind, block.Height, Shard) (block.Txs, block.Plan, block.State)

// BaseBlocksInRange must return an upper bound estimate for the number of
// base blocks between two blocks (identified by their block hash). This is
// used to prevent forking by old signatories.
BaseBlocksInRange(begin, end id.Hash) int
}

type Validator interface {
Expand Down
17 changes: 17 additions & 0 deletions replica/replica.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,23 @@ func (replica *Replica) HandleMessage(m Message) {
return
}
}
if m.Message.Type() == process.ProposeMessageType {
if m.Message.Height() > replica.p.CurrentHeight()+1 {
// If the Propose is not at the next height, then we need to make
// sure that no base blocks have been missed. Otherwise, reject the
// Propose, and wait until the appropriate one has been seen.
baseBlockHash := replica.blockStorage.LatestBaseBlock(m.Shard).Hash()
blockHash := m.Message.BlockHash()
numBlocks := replica.rebaser.blockIterator.BaseBlocksInRange(baseBlockHash, blockHash)
if numBlocks > 0 {
// We have missed a base block, so we reject the Propose. This
// assumes that the first Propose after the base block will
// eventually be seen by the Process, as per the underlying
// network assumptions.
return
}
}
}

// Check that the Message sender is from our Shard (this can be a moderately
// expensive operation, so we cache the result until a new `block.Base` is
Expand Down
4 changes: 4 additions & 0 deletions replica/replica_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func (m mockBlockIterator) NextBlock(kind block.Kind, height block.Height, shard
return RandomBytesSlice(), RandomBytesSlice(), RandomBytesSlice()
}

func (m mockBlockIterator) BaseBlocksInRange(begin, end id.Hash) int {
return 0 // mockBlockIterator does not support rebasing.
}

type mockValidator struct {
valid error
}
Expand Down
11 changes: 11 additions & 0 deletions testutil/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,17 @@ func (bc *MockBlockchain) BlockAtHeight(height block.Height) (block.Block, bool)
return block, ok
}

func (bc *MockBlockchain) LatestBaseBlock() block.Block {
bc.mu.RLock()
defer bc.mu.RUnlock()

block, ok := bc.blocks[0]
if !ok {
panic("no genesis block")
}
return block
}

func (bc *MockBlockchain) StateAtHeight(height block.Height) (block.State, bool) {
bc.mu.RLock()
defer bc.mu.RUnlock()
Expand Down
4 changes: 4 additions & 0 deletions testutil/replica/replica.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func (m *MockBlockIterator) NextBlock(kind block.Kind, height block.Height, shar
}
}

func (m *MockBlockIterator) BaseBlocksInRange(begin, end id.Hash) int {
return 0 // MockBlockIterator does not support rebasing.
}

type MockValidator struct {
store *MockPersistentStorage
}
Expand Down