Skip to content

Commit

Permalink
Merge pull request #78 from renproject/fix/bad-base-blocks
Browse files Browse the repository at this point in the history
Fix using bad base block
  • Loading branch information
loongy authored Apr 1, 2020
2 parents c8a2acf + 2ac41b2 commit 16f7091
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 4 deletions.
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

0 comments on commit 16f7091

Please sign in to comment.