-
Notifications
You must be signed in to change notification settings - Fork 2
/
LibraBFT.txt
134 lines (108 loc) · 3.49 KB
/
LibraBFT.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
type BlockId
type Block = struct {
parent: BlockId,
round: uint,
}
function Hash(Block): BlockId
const blocks: [BlockId]Block
axiom forall id: BlockId. Hash(blocks[id]) == id
const h: uint // number of honest replicas
const f: uint // number of faulty replicas
axiom h > 2f
const root: BlockId // root of block tree
axiom blocks[root].round == 0 && blocks[root].parent == root
type HonestReplicaId = 1..h // ids of honest replicas
type HonestQuorum = {q: Set<HonestReplicaId> | size(q) >= h - f} // all possible quorums of honest nodes
// primitive function that returns true iff id' can reach id by following parent link 0 or more times
function Reaches(id': BlockId, id: BlockId): bool;
function Consistent(id': BlockId, id: BlockId) : bool {
Reaches(id', id) || Reaches(id, id')
}
//// global model variables
// all collected votes sent by honest replicas
var voteStore: [BlockId]Set<HonestReplicaId>
// id of the latest globally-committed block
var globallyCommitted: BlockId
//// per-replica variables
// monotonically increasing round of the last voted block
var lastVoteRound: uint
// monotonically increasing round of the preferred block
var preferredBlockRound: uint
//// procedures
procedure HasQuorum(blockId: BlockId) : bool
reads voteStore
{
var numEquivocators: uint
// numEquivocators is the number of faulty replicas who voted for blockId
assume numEquivocators <= f
size(voteStore[blockId]) + numEquivocators >= h
}
procedure Initialize()
{
voteStore := (lambda id: BlockId. if id = root then HonestReplicaId else {})
globallyCommitted := root
lastVoteRound := (lambda r: HonestReplicaId. 0)
preferredBlockRound := (lambda r: HonestReplicaId. 0)
}
//// start things off
procedure Main() {
var r: HonestReplicaId
var blockId: BlockId
// propose or commit nondeterministically
if * {
async OnReceiveProposal(r, blockId)
} else {
async TryCommit(r, blockId)
}
// keep going nondeterministically
if * {
async Main()
}
}
//// event handler at a replica to process proposal
procedure OnReceiveProposal(r: HonestReplicaId, newBlockId: BlockId)
reads voteStore, lastVoteRound, preferredBlockRound
writes voteStore, lastVoteRound, preferredBlockRound
{
var newBlock, b'', b': Block
var id'': BlockId
newBlock := blocks[newBlockId]
id'' := newBlock.parent
if !HasQuorum(id'') {
return
}
b'' := blocks[id'']
// possibly update preferred block
b' := blocks[b''.parent]
if b'.round > preferredBlockRound[r] {
preferredBlockRound[r] := b'.round
}
// possibly vote
if newBlock.round > lastVoteRound[r] && b''.round >= preferredBlockRound[r] {
lastVoteRound[r] := newBlock.round
voteStore[newBlockId] := voteStore[newBlockId] + r
}
}
//// event handler at a replica to commit a block
procedure TryCommit(r: HonestReplicaId, id'': BlockId)
reads voteStore, globallyCommitted
writes globallyCommitted
{
var id', id: BlockId
var b'', b', b: Block
if !HasQuorum(id'') {
return
}
b'' := blocks[id'']
id' := b''.parent
b' := blocks[id']
id := b'.parent
b := blocks[id]
// Invariant: (exists q: HonestQuorum. (forall x: HonestReplicaId. x in q ==> preferredBlockRound[x] >= b.round))
if b''.round == b'.round + 1 && b'.round == b.round + 1 {
assert Consistent(id, globallyCommitted)
if Reaches(id, globallyCommitted) {
globallyCommitted := id
}
}
}