combine generic types for signing and hashing

This commit is contained in:
Mark Spanbroek 2024-11-06 13:45:37 +01:00
parent fcf32043a1
commit c600c5a2ef
24 changed files with 117 additions and 96 deletions

View File

@ -1,3 +1,7 @@
import ./mysticeti/dependencies
export dependencies.Dependencies
import ./mysticeti/validator import ./mysticeti/validator
export validator.Validator export validator.Validator

View File

@ -7,3 +7,7 @@ import pkg/questionable/results
export questionable export questionable
export results export results
import ./dependencies
export dependencies

View File

@ -4,20 +4,20 @@ import ./blockid
import ./transaction import ./transaction
type type
Block*[Hashing] = object Block*[Dependencies] = object
author: CommitteeMember author: CommitteeMember
round: uint64 round: uint64
parents: seq[BlockId[Hashing]] parents: seq[BlockId[Dependencies]]
transactions: seq[Transaction] transactions: seq[Transaction]
func new*[Hashing]( func new*[Dependencies](
_: type Block[Hashing]; _: type Block[Dependencies];
author: CommitteeMember, author: CommitteeMember,
round: uint64, round: uint64,
parents: seq[BlockId[Hashing]], parents: seq[BlockId[Dependencies]],
transactions: seq[Transaction] transactions: seq[Transaction]
): auto = ): auto =
Block[Hashing]( Block[Dependencies](
author: author, author: author,
round: round, round: round,
parents: parents, parents: parents,
@ -40,8 +40,8 @@ func toBytes*(blck: Block): seq[byte] =
cast[seq[byte]]($blck) # TODO: proper serialization cast[seq[byte]]($blck) # TODO: proper serialization
func id*(blck: Block): auto = func id*(blck: Block): auto =
BlockId.new( BlockId[Block.Dependencies].new(
blck.author, blck.author,
blck.round, blck.round,
Block.Hashing.hash(blck.toBytes) Hash[Block.Dependencies].hash(blck.toBytes)
) )

View File

@ -1,18 +1,18 @@
import ../committee import ../committee
import ../hashing import ../hashing
type BlockId*[Hashing] = object type BlockId*[Dependencies] = object
author: CommitteeMember author: CommitteeMember
round: uint64 round: uint64
hash: Hash[Hashing] hash: Hash[Dependencies]
func new*( func new*(
_: type BlockId, T: type BlockId,
author: CommitteeMember, author: CommitteeMember,
round: uint64, round: uint64,
hash: Hash hash: Hash
): auto = ): auto =
BlockId[Hash.Hashing]( T(
author: author, author: author,
round: round, round: round,
hash: hash hash: hash

View File

@ -1,12 +1,13 @@
import ../basics
import ../signing import ../signing
import ./blck import ./blck
type SignedBlock*[Signing, Hashing] = object type SignedBlock*[Dependencies] = object
blck: Block[Hashing] blck: Block[Dependencies]
signature: Signature[Signing] signature: Signature[Dependencies]
func new*(_: type SignedBlock, blck: Block, signature: Signature): auto = func new*(_: type SignedBlock, blck: Block, signature: Signature): auto =
SignedBlock[Signature.Signing, Block.Hashing](blck: blck, signature: signature) SignedBlock[Block.Dependencies](blck: blck, signature: signature)
func blck*(signed: SignedBlock): auto = func blck*(signed: SignedBlock): auto =
signed.blck signed.blck

View File

@ -3,13 +3,13 @@ import ../signing
import ./members import ./members
type type
Committee*[Signing] = ref object Committee*[Dependencies] = ref object
members: seq[Identifier[Signing]] members: seq[Identifier[Dependencies]]
stakes: seq[Stake] stakes: seq[Stake]
Stake* = float64 Stake* = float64
func new*(_: type Committee, stakes: openArray[(Identifier, Stake)]): auto = func new*(T: type Committee, stakes: openArray[(Identifier, Stake)]): auto =
var committee = Committee[Identifier.Signing]() var committee = T()
for (member, stake) in stakes: for (member, stake) in stakes:
committee.members.add(member) committee.members.add(member)
committee.stakes.add(stake) committee.stakes.add(stake)

View File

@ -0,0 +1 @@
type Dependencies*[Signing, Hashing] = object

View File

@ -1,11 +1,11 @@
type type
Hash*[Hashing] = object Hash*[Dependencies] = object
value: Hashing.Hash value: Dependencies.Hashing.Hash
Hashing*[Hash] = object Hashing*[Hash] = object
func hash*(T: type Hashing, bytes: openArray[byte]): auto = func hash*(T: type Hash, bytes: openArray[byte]): auto =
mixin hash mixin hash
Hash[T](value: T.Hash.hash(bytes)) T(value: T.Dependencies.Hashing.Hash.hash(bytes))
func `$`*(hash: Hash): string = func `$`*(hash: Hash): string =
$hash.value $hash.value

View File

@ -1,27 +1,27 @@
type type
Identity*[Signing] = object Identity*[Dependencies] = object
value: Signing.Identity value: Dependencies.Signing.Identity
Identifier*[Signing] = object Identifier*[Dependencies] = object
value: Signing.Identifier value: Dependencies.Signing.Identifier
Signature*[Signing] = object Signature*[Dependencies] = object
value: Signing.Signature value: Dependencies.Signing.Signature
Signing*[Identity, Identifier, Signature] = object Signing*[Identity, Identifier, Signature] = object
proc init*[Signing](_: type Identity[Signing]): Identity[Signing] = proc init*(T: type Identity): T =
mixin init mixin init
Identity[Signing](value: Signing.Identity.init()) T(value: T.Dependencies.Signing.Identity.init())
func identifier*(identity: Identity): auto = func identifier*(identity: Identity): auto =
mixin identifier mixin identifier
Identifier[Identity.Signing](value: identity.value.identifier) Identifier[Identity.Dependencies](value: identity.value.identifier)
func sign*(identity: Identity, bytes: openArray[byte]): auto = func sign*(identity: Identity, bytes: openArray[byte]): auto =
mixin sign mixin sign
Signature[Identity.Signing](value: identity.value.sign(bytes)) Signature[Identity.Dependencies](value: identity.value.sign(bytes))
func signer*(signature: Signature, bytes: openArray[byte]): auto = func signer*(signature: Signature, bytes: openArray[byte]): auto =
mixin signer mixin signer
Identifier[Signature.Signing](value: signature.value.signer(bytes)) Identifier[Signature.Dependencies](value: signature.value.signer(bytes))
func `$`*(identity: Identity): string = func `$`*(identity: Identity): string =
$identity.value $identity.value

View File

@ -9,11 +9,11 @@ import ./validator/checks
export slots export slots
export checks export checks
type Validator*[Signing, Hashing] = ref object type Validator*[Dependencies] = ref object
identity: Identity[Signing] identity: Identity[Dependencies]
committee: Committee[Signing] committee: Committee[Dependencies]
membership: CommitteeMember membership: CommitteeMember
rounds: Rounds[Signing, Hashing] rounds: Rounds[Dependencies]
func new*(T: type Validator; identity: Identity, committee: Committee): ?!T = func new*(T: type Validator; identity: Identity, committee: Committee): ?!T =
without membership =? committee.membership(identity.identifier): without membership =? committee.membership(identity.identifier):
@ -22,7 +22,7 @@ func new*(T: type Validator; identity: Identity, committee: Committee): ?!T =
identity: identity, identity: identity,
committee: committee, committee: committee,
membership: membership, membership: membership,
rounds: Rounds[T.Signing, T.Hashing].init(committee.size) rounds: Rounds[T.Dependencies].init(committee.size)
) )
func identifier*(validator: Validator): auto = func identifier*(validator: Validator): auto =
@ -77,11 +77,12 @@ func addBlock(validator: Validator, signedBlock: SignedBlock) =
validator.updateCertified(signedBlock.blck) validator.updateCertified(signedBlock.blck)
proc propose*(validator: Validator, transactions: seq[Transaction]): auto = proc propose*(validator: Validator, transactions: seq[Transaction]): auto =
type SignedBlock = blocks.SignedBlock[Validator.Signing, Validator.Hashing] type Block = blocks.Block[Validator.Dependencies]
type SignedBlock = blocks.SignedBlock[Validator.Dependencies]
let round = validator.rounds.latest let round = validator.rounds.latest
if round[validator.membership].proposals.len > 0: if round[validator.membership].proposals.len > 0:
return SignedBlock.failure "already proposed this round" return SignedBlock.failure "already proposed this round"
var parents: seq[BlockId[Validator.Hashing]] var parents: seq[BlockId[Validator.Dependencies]]
var parentStake: Stake var parentStake: Stake
if previous =? round.previous: if previous =? round.previous:
for slot in previous.slots: for slot in previous.slots:
@ -103,8 +104,8 @@ proc propose*(validator: Validator, transactions: seq[Transaction]): auto =
success signedBlock success signedBlock
func check*(validator: Validator, signed: SignedBlock): auto = func check*(validator: Validator, signed: SignedBlock): auto =
type BlockCheck = checks.BlockCheck[SignedBlock.Signing, SignedBlock.Hashing] type BlockCheck = checks.BlockCheck[SignedBlock.Dependencies]
type BlockId = blocks.BlockId[SignedBlock.Hashing] type BlockId = blocks.BlockId[SignedBlock.Dependencies]
without member =? validator.committee.membership(signed.signer): without member =? validator.committee.membership(signed.signer):
return BlockCheck.invalid("block is not signed by a committee member") return BlockCheck.invalid("block is not signed by a committee member")
if member != signed.blck.author: if member != signed.blck.author:

View File

@ -1,3 +1,4 @@
import ../basics
import ../blocks import ../blocks
type type
@ -5,15 +6,15 @@ type
invalid invalid
incomplete incomplete
correct correct
BlockCheck*[Signing, Hashing] = object BlockCheck*[Dependencies] = object
case verdict: BlockVerdict case verdict: BlockVerdict
of invalid: of invalid:
reason: string reason: string
of incomplete: of incomplete:
missing: seq[BlockId[Hashing]] missing: seq[BlockId[Dependencies]]
of correct: of correct:
blck: CorrectBlock[Signing, Hashing] blck: CorrectBlock[Dependencies]
CorrectBlock*[Signing, Hashing] = distinct SignedBlock[Signing, Hashing] CorrectBlock*[Dependencies] = distinct SignedBlock[Dependencies]
func invalid*(T: type BlockCheck, reason: string): T = func invalid*(T: type BlockCheck, reason: string): T =
T(verdict: BlockVerdict.invalid, reason: reason) T(verdict: BlockVerdict.invalid, reason: reason)
@ -22,7 +23,7 @@ func incomplete*(T: type BlockCheck; missing: seq[BlockId]): T =
T(verdict: BlockVerdict.incomplete, missing: missing) T(verdict: BlockVerdict.incomplete, missing: missing)
func correct*(T: type BlockCheck, signedBlock: SignedBlock): T = func correct*(T: type BlockCheck, signedBlock: SignedBlock): T =
let blck = CorrectBlock[SignedBlock.Signing, SignedBlock.Hashing](signedBlock) let blck = CorrectBlock[SignedBlock.Dependencies](signedBlock)
T(verdict: BlockVerdict.correct, blck: blck) T(verdict: BlockVerdict.correct, blck: blck)
func verdict*(check: BlockCheck): BlockVerdict = func verdict*(check: BlockCheck): BlockVerdict =
@ -38,7 +39,7 @@ func blck*(check: BlockCheck): auto =
check.blck check.blck
func signedBlock*(correct: CorrectBlock): auto = func signedBlock*(correct: CorrectBlock): auto =
SignedBlock[CorrectBlock.Signing, CorrectBlock.Hashing](correct) SignedBlock[CorrectBlock.Dependencies](correct)
func blck*(correct: CorrectBlock): auto = func blck*(correct: CorrectBlock): auto =
correct.signedBlock.blck correct.signedBlock.blck

View File

@ -3,14 +3,14 @@ import ../blocks
import ../committee import ../committee
import ./slots import ./slots
type Round*[Signing, Hashing] = ref object type Round*[Dependencies] = ref object
number: uint64 number: uint64
previous, next: ?Round[Signing, Hashing] previous, next: ?Round[Dependencies]
slots: seq[ProposerSlot[Signing, Hashing]] slots: seq[ProposerSlot[Dependencies]]
func new*(T: type Round, number: uint64, slots: int): T = func new*(T: type Round, number: uint64, slots: int): T =
assert slots > 0 assert slots > 0
type Slot = ProposerSlot[T.Signing, T.Hashing] type Slot = ProposerSlot[T.Dependencies]
let slots = newSeqWith(slots, Slot.new()) let slots = newSeqWith(slots, Slot.new())
T(number: number, slots: slots) T(number: number, slots: slots)

View File

@ -3,11 +3,11 @@ import ./round
export round export round
type Rounds*[Signing, Hashing] = object type Rounds*[Dependencies] = object
oldest, latest: Round[Signing, Hashing] oldest, latest: Round[Dependencies]
func init*(T: type Rounds, slots: int, start: uint64 = 0): T = func init*(T: type Rounds, slots: int, start: uint64 = 0): T =
let round = Round[T.Signing, T.Hashing].new(start, slots) let round = Round[T.Dependencies].new(start, slots)
T(oldest: round, latest: round) T(oldest: round, latest: round)
func oldest*(rounds: Rounds): auto = func oldest*(rounds: Rounds): auto =
@ -17,7 +17,7 @@ func latest*(rounds: Rounds): auto =
rounds.latest rounds.latest
func addNewRound*(rounds: var Rounds) = func addNewRound*(rounds: var Rounds) =
rounds.latest = Round[Rounds.Signing, Rounds.Hashing].new(rounds.latest) rounds.latest = Round[Rounds.Dependencies].new(rounds.latest)
func removeOldestRound*(rounds: var Rounds) = func removeOldestRound*(rounds: var Rounds) =
assert rounds.oldest.next.isSome assert rounds.oldest.next.isSome

View File

@ -3,15 +3,15 @@ import ../blocks
import ../committee import ../committee
type type
ProposerSlot*[Signing, Hashing] = ref object ProposerSlot*[Dependencies] = ref object
proposals: seq[Proposal[Signing, Hashing]] proposals: seq[Proposal[Dependencies]]
skippedBy: Voting skippedBy: Voting
status: SlotStatus status: SlotStatus
Proposal*[Signing, Hashing] = ref object Proposal*[Dependencies] = ref object
slot: ProposerSlot[Signing, Hashing] slot: ProposerSlot[Dependencies]
signedBlock: SignedBlock[Signing, Hashing] signedBlock: SignedBlock[Dependencies]
certifiedBy: Voting certifiedBy: Voting
certificates: seq[BlockId[Hashing]] certificates: seq[BlockId[Dependencies]]
SlotStatus* {.pure.} = enum SlotStatus* {.pure.} = enum
undecided undecided
skip skip
@ -40,7 +40,7 @@ func certificates*(proposal: Proposal): auto =
proposal.certificates proposal.certificates
func addProposal*(slot: ProposerSlot, signedBlock: SignedBlock) = func addProposal*(slot: ProposerSlot, signedBlock: SignedBlock) =
let proposal = Proposal[SignedBlock.Signing, SignedBlock.Hashing]( let proposal = Proposal[ProposerSlot.Dependencies](
slot: slot, slot: slot,
signedBlock: signedBlock signedBlock: signedBlock
) )

View File

@ -1,10 +1,12 @@
import ../basics import ../basics
import mysticeti
import mysticeti/committee import mysticeti/committee
import mysticeti/signing import mysticeti/signing
suite "Committee": suite "Committee":
type Identifier = signing.Identifier[MockSigning] type Identifier = signing.Identifier[MockDependencies]
type Committee = committee.Committee[MockDependencies]
test "committee has numbered members": test "committee has numbered members":
let identifiers = array[4, Identifier].example let identifiers = array[4, Identifier].example

View File

@ -14,26 +14,26 @@ proc example*(T: type Identity): T =
T.init() T.init()
proc example*(T: type Identifier): T = proc example*(T: type Identifier): T =
Identity[T.Signing].example.identifier Identity[T.Dependencies].example.identifier
proc example*(T: type CommitteeMember): T = proc example*(T: type CommitteeMember): T =
CommitteeMember(int.example) CommitteeMember(int.example)
proc example*(T: type Hash): T = proc example*(T: type Hash): T =
T.Hashing.hash(seq[byte].example) T.hash(seq[byte].example)
proc example*(T: type BlockId): T = proc example*(T: type BlockId): T =
let author = CommitteeMember.example let author = CommitteeMember.example
let round = uint64.example let round = uint64.example
let hash = Hash[T.Hashing].example let hash = Hash[T.Dependencies].example
BlockId.new(author, round, hash) T.new(author, round, hash)
proc example*( proc example*(
T: type Block, T: type Block,
author = CommitteeMember.example, author = CommitteeMember.example,
round = uint64.example round = uint64.example
): T = ): T =
let parents = seq[BlockId[T.Hashing]].example let parents = seq[BlockId[T.Dependencies]].example
let transactions = seq[Transaction].example let transactions = seq[Transaction].example
T.new(author, round, parents, transactions) T.new(author, round, parents, transactions)
@ -42,8 +42,8 @@ proc example*(
author = CommitteeMember.example, author = CommitteeMember.example,
round = uint64.example round = uint64.example
): T = ): T =
let identity = Identity[T.Signing].example let identity = Identity[T.Dependencies].example
let blck = Block[T.Hashing].example(author = author, round = round) let blck = Block[T.Dependencies].example(author = author, round = round)
identity.sign(blck) identity.sign(blck)
proc example*[T](_: type seq[T], length=0..10): seq[T] = proc example*[T](_: type seq[T], length=0..10): seq[T] =

View File

@ -1,5 +1,8 @@
import mysticeti/dependencies
import ./mocks/signing import ./mocks/signing
import ./mocks/hashing import ./mocks/hashing
export signing export signing
export hashing export hashing
type MockDependencies* = Dependencies[MockSigning, MockHashing]

View File

@ -2,9 +2,10 @@ import ./basics
import mysticeti import mysticeti
import mysticeti/blocks import mysticeti/blocks
type Validator* = mysticeti.Validator[MockSigning, MockHashing] type Validator* = mysticeti.Validator[MockDependencies]
type Identity* = mysticeti.Identity[MockSigning] type Committee* = mysticeti.Committee[MockDependencies]
type SignedBlock* = blocks.SignedBlock[MockSigning, MockHashing] type Identity* = mysticeti.Identity[MockDependencies]
type SignedBlock* = blocks.SignedBlock[MockDependencies]
type NetworkSimulator* = object type NetworkSimulator* = object
identities: seq[Identity] identities: seq[Identity]

View File

@ -5,9 +5,10 @@ import mysticeti/hashing
suite "Blocks": suite "Blocks":
type Block = mysticeti.Block[MockHashing] type Block = mysticeti.Block[MockDependencies]
type BlockId = mysticeti.BlockId[MockHashing] type BlockId = mysticeti.BlockId[MockDependencies]
type Identity = mysticeti.Identity[MockSigning] type Identity = mysticeti.Identity[MockDependencies]
type Hash = hashing.Hash[MockDependencies]
test "blocks have an author, a round, parents and transactions": test "blocks have an author, a round, parents and transactions":
let author = CommitteeMember.example let author = CommitteeMember.example
@ -25,7 +26,7 @@ suite "Blocks":
let id = blck.id let id = blck.id
check id.author == blck.author check id.author == blck.author
check id.round == blck.round check id.round == blck.round
check id.hash == Block.Hashing.hash(blck.toBytes) check id.hash == Hash.hash(blck.toBytes)
test "blocks can be signed": test "blocks can be signed":
let signer = Identity.init let signer = Identity.init

View File

@ -6,9 +6,9 @@ import mysticeti/validator/round
suite "Validator Round": suite "Validator Round":
type Round = round.Round[MockSigning, MockHashing] type Round = round.Round[MockDependencies]
type Block = mysticeti.Block[MockHashing] type Block = mysticeti.Block[MockDependencies]
type SignedBlock = mysticeti.SignedBlock[MockSigning, MockHashing] type SignedBlock = mysticeti.SignedBlock[MockDependencies]
test "rounds have a number": test "rounds have a number":
check Round.new(0, 1).number == 0 check Round.new(0, 1).number == 0

View File

@ -3,8 +3,8 @@ import mysticeti/validator/rounds
suite "List of Validator Rounds": suite "List of Validator Rounds":
type Rounds = rounds.Rounds[MockSigning, MockHashing] type Rounds = rounds.Rounds[MockDependencies]
type Round = rounds.Round[MockSigning, MockHashing] type Round = rounds.Round[MockDependencies]
test "has a single round initially": test "has a single round initially":
let rounds = Rounds.init(slots = 4) let rounds = Rounds.init(slots = 4)

View File

@ -4,10 +4,10 @@ import mysticeti/validator/slots
suite "Proposer Slots": suite "Proposer Slots":
type BlockId = mysticeti.BlockId[MockHashing] type BlockId = mysticeti.BlockId[MockDependencies]
type SignedBlock = mysticeti.SignedBlock[MockSigning, MockHashing] type SignedBlock = mysticeti.SignedBlock[MockDependencies]
type Proposal = slots.Proposal[MockSigning, MockHashing] type Proposal = slots.Proposal[MockDependencies]
type ProposerSlot = slots.ProposerSlot[MockSigning, MockHashing] type ProposerSlot = slots.ProposerSlot[MockDependencies]
var slot: ProposerSlot var slot: ProposerSlot

View File

@ -1,8 +1,9 @@
import ../basics import ../basics
import mysticeti import mysticeti
type Validator = mysticeti.Validator[MockSigning, MockHashing] type Validator = mysticeti.Validator[MockDependencies]
type Identity = mysticeti.Identity[MockSigning] type Identity = mysticeti.Identity[MockDependencies]
type Committee = mysticeti.Committee[MockDependencies]
suite "Validator": suite "Validator":

View File

@ -7,11 +7,12 @@ import mysticeti/hashing
suite "Validator Network": suite "Validator Network":
type Validator = mysticeti.Validator[MockSigning, MockHashing] type Validator = mysticeti.Validator[MockDependencies]
type Identity = mysticeti.Identity[MockSigning] type Committee = mysticeti.Committee[MockDependencies]
type Block = blocks.Block[MockHashing] type Identity = mysticeti.Identity[MockDependencies]
type BlockId = blocks.BlockId[MockHashing] type Block = blocks.Block[MockDependencies]
type Hash = hashing.Hash[MockHashing] type BlockId = blocks.BlockId[MockDependencies]
type Hash = hashing.Hash[MockDependencies]
var simulator: NetworkSimulator var simulator: NetworkSimulator