We may sign hashable objects, such as votes, messages, etc.
record Signed (A : Type) : Type where constructor _signed-by_ field datum : A node : Pid signature : ⦃ _ : Digestable A ⦄ → Signature signature = sign (node ∙sk) datum SignedShare = Signed
BlockId = Hash record ThresholdSig (A : Type) : Type where field payload : A shares : List Pid .uniqueShares : Unique shares .quorum : IsMajority shares -- If we follow the paper to the letter shares should be a list of (A × Pid) -- such that all elements have payload as first component. -- Morally a Share = Pid × A
QC = ThresholdSig (BlockId × Round) -- missing property: let (bid , r) = qc .payload. -- then ∀ b → blockId b ≡ bid → b ∙round ≡ r -- This condition was added to the connects-to predicate. QCs = List QCHow to digest a QC:
qcPayload : QC → PayloadQC qcPayload qc = let bid , r = qc .payload in (bid , r , qc .shares) qcPayload-inj : Injective≡ qcPayload qcPayload-inj eq with refl , refl ← Prod.,-injective eq = refl instance Digestable-QC : Digestable QC Digestable-QC = λ where .digest → digest ∘ qcPayload .digest-inj → qcPayload-inj ∘ digest-inj rqcPayload : Round × QC → ℕ × PayloadQC rqcPayload = map₂ qcPayload rqcPayload-inj : Injective≡ rqcPayload rqcPayload-inj eq with refl , refl ← Prod.,-injective eq = refl instance Digestable-ℕ×QC : Digestable (Round × QC) Digestable-ℕ×QC = λ where .digest → digest ∘ rqcPayload .digest-inj → rqcPayload-inj ∘ digest-inj
Quorum certificates are ordered by round.
The following function returns the higher of two QCs.
qc ≤qc qc′ = qc ∙round ≤ qc′ ∙round qc <qc qc′ = qc ∙round < qc′ ∙round _≤qc?_ = Decidable² _≤qc_ ∋ dec² _<qc?_ = Decidable² _<qc_ ∋ dec² qc ⊔qc qc′ = if ¿ qc ≤qc qc′ ¿ᵇ then qc′ else qc
In every qc there is an honest vote share.
qc⇒hp : ∀ (qc : QC) → ───────────────────────────────── ∃ λ p → p ∈ qc .shares × Honest p qc⇒hp qc = satisfied′ $ honestFromMajority (getUnique qc) (getQuorum qc)
-- TimeoutEvidence comes from timeout messages TimeoutEvidence = Signed (Round × QC)
record TC : Type where field roundTC : Round tes : List TimeoutEvidence -- NB: should we only keep the highest QC? -- possibly a compressed TC' after gathering timeouts .quorumTC : IsMajority tes .uniqueTC : UniqueBy node tes -- ^ each piece of TimeoutEvidence should be signed by a different replica. .allRound : All (λ te → te ∙round ≡ roundTC) tes .qcBound : All (λ te → te ∙qcTE ∙round < roundTC) tes TCs = List TCHow to digest a TC:
PayloadTE = Pid × PayloadQC opaque tePayload : TimeoutEvidence → PayloadTE tePayload ((r , qc) signed-by p) = p , qcPayload qc tePayloads-inj : ∀ {tes tes′} → ∙ All (λ te → te ∙round ≡ r) tes ∙ All (λ te → te ∙round ≡ r) tes′ ∙ map tePayload tes ≡ map tePayload tes′ ────────────────────────────────────── tes ≡ tes′ tePayloads-inj {tes = []} [] [] refl = refl tePayloads-inj {tes = _ ∷ _} (refl ∷ r≡) (refl ∷ r≡′) tes≡ with refl , tes≡ ← L.∷-injective tes≡ = cong₂ _∷_ refl $ tePayloads-inj r≡ r≡′ tes≡ tcPayload : TC → PayloadTC tcPayload tc = tc .roundTC , map tePayload (tc .tes) tcPayload-inj : Injective≡ tcPayload tcPayload-inj {tc}{tc′} tc≡ with refl , tes≡ ← Prod.,-injective tc≡ with refl ← tePayloads-inj (getAllRound tc) (getAllRound tc′) tes≡ = refl instance Digestable-TC : Digestable TC Digestable-TC = λ where .digest → digest ∘ tcPayload .digest-inj → tcPayload-inj ∘ digest-inj
record Block : Type where constructor ⟨_,_,_,_⟩ field blockQC : QC blockTC : Maybe TC round : Round txs : List Transaction SBlock = Signed Block
Chain = List Block genesis : Chain genesis = []
genesisId = genesis ♯ qc₀ : QC qc₀ = record { payload = genesisId , 0 ; shares = allPids ; uniqueShares = L.Unique.tabulate⁺ {f = id} id ; quorum = allPidsMajority } instance HasBlockId-Chain = HasBlockId Chain ∋ λ where ._∙blockId [] → genesisId ._∙blockId (b ∷ _) → b ∙blockId HasRound-Chain = HasRound Chain ∋ λ where ._∙round [] → 0 ._∙round (b ∷ _) → b ∙round
highestQC : TC → QC highestQC tc = maxNE (qcs⁺ tc)
ValidBlock : Block → Type ValidBlock b = b ∙round > b .blockQC ∙round record _-connects-to-_ (b : Block) (ch : Chain) : Type where constructor connects∶ field idMatch : (b .blockQC) ∙blockId ≡ ch ∙blockId roundMatch : (b .blockQC) ∙round ≡ ch ∙round roundAdvances : ValidBlock b -- TODO: move to Block open _-connects-to-_ public