Blocks

Signatures on objects

Signed Objects

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

Threshold signatures

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

Quorum Certificates

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 QC
How 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 , reflProd.,-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 , reflProd.,-injective eq = refl

instance
  Digestable-ℕ×QC : Digestable (Round × QC)
  Digestable-ℕ×QC = λ where
    .digest      digest  rqcPayload
    .digest-inj  rqcPayload-inj  digest-inj

QC order

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

QC Property

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)

Timeout Certificates

-- 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 TC
How 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 refltePayloads-inj (getAllRound tc) (getAllRound tc′) tes≡
    = refl

instance
  Digestable-TC : Digestable TC
  Digestable-TC = λ where
    .digest      digest  tcPayload
    .digest-inj  tcPayload-inj  digest-inj

Blocks

record Block : Type where
  constructor ⟨_,_,_,_⟩
  field
    blockQC : QC
    blockTC : Maybe TC
    round   : Round
    txs     : List Transaction

SBlock = Signed Block

Genesis and Genesis QC

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