make Block a type dependency

This commit is contained in:
Mark Spanbroek 2024-12-09 18:07:43 +01:00
parent aa4f390a1a
commit de854cbb5f
18 changed files with 81 additions and 191 deletions

View File

@ -36,19 +36,8 @@ export committee.`$`
import ./mysticeti/blocks
export blocks.Block
export blocks.BlockId
export blocks.author
export blocks.round
export blocks.parents
export blocks.id
import ./mysticeti/basics/immutableseq
export immutableseq
import ./mysticeti/blocks/signed
export signed.SignedBlock
export signed.blck
export signed.signer
export blocks.new
export blocks.SignedBlock
export blocks.blck
export blocks.signer

View File

@ -11,7 +11,3 @@ export results
import ./dependencies
export dependencies
import ./basics/immutableseq
export immutableseq

View File

@ -1,27 +0,0 @@
type ImmutableSeq*[Element] = ref object
## Encapsulates a sequence, so that it no longer can be
## modified, and can be passed by reference to avoid copying.
elements: seq[Element]
func immutable*[Element](sequence: seq[Element]): ImmutableSeq[Element] =
ImmutableSeq[Element](elements: sequence)
func copy*(sequence: ImmutableSeq): auto =
sequence.elements
iterator items*(sequence: ImmutableSeq): auto =
for element in sequence.elements:
yield element
func len*(sequence: ImmutableSeq): int =
sequence.elements.len
func `[]`*(sequence: ImmutableSeq, index: int): auto =
sequence.elements[index]
func contains*[Element](sequence: ImmutableSeq[Element], element: Element): bool =
sequence.elements.contains(element)
func `==`*(a, b: ImmutableSeq): bool =
a.elements == b.elements

View File

@ -1,7 +1,5 @@
import ./blocks/blck
import ./blocks/blockid
import ./blocks/signed
export blck
export blockid
export signed

View File

@ -1,47 +0,0 @@
import ../basics
import ../committee
import ./blockid
type
Block*[Dependencies] = ref object
author: CommitteeMember
round: uint64
parents: ImmutableSeq[BlockId[Dependencies.Hash]]
transactions: ImmutableSeq[Dependencies.Transaction]
id: ?BlockId[Dependencies.Hash]
func new*[Dependencies](
_: type Block[Dependencies];
author: CommitteeMember,
round: uint64,
parents: seq[BlockId[Dependencies.Hash]],
transactions: seq[Dependencies.Transaction]
): auto =
Block[Dependencies](
author: author,
round: round,
parents: parents.immutable,
transactions: transactions.immutable
)
func author*(blck: Block): auto =
blck.author
func round*(blck: Block): uint64 =
blck.round
func parents*(blck: Block): auto =
blck.parents
func transactions*(blck: Block): auto =
blck.transactions
func id*(blck: Block): auto =
without var id =? blck.id:
type Dependencies = Block.Dependencies
mixin hash
let blockBytes = Dependencies.Serialization.toBytes(blck)
let blockHash = Dependencies.Hash.hash(blockBytes)
id = BlockId.new(blck.author, blck.round, blockHash)
blck.id = some id
id

View File

@ -1,21 +1,21 @@
import ../basics
import ./blck
import ./blockid
type SignedBlock*[Dependencies] = object
blck: Block[Dependencies]
blck: Dependencies.Block
signature: Dependencies.Signature
func init*(
_: type SignedBlock,
blck: Block,
signature: Block.Dependencies.Signature
func init*[Dependencies](
_: type SignedBlock[Dependencies];
blck: Dependencies.Block,
signature: Dependencies.Signature
): auto =
SignedBlock[Block.Dependencies](blck: blck, signature: signature)
SignedBlock[Dependencies](blck: blck, signature: signature)
func blck*(signed: SignedBlock): auto =
signed.blck
func signer*(signed: SignedBlock): auto =
mixin signer
mixin id
signed.signature.signer(signed.blck.id.hash)

View File

@ -1,6 +1,5 @@
type Dependencies*[
Transaction,
Serialization,
Block,
Hash,
Identifier,
Signature

View File

@ -43,13 +43,12 @@ func primaryProposer*(validator: Validator): CommitteeMember =
func nextRound*(validator: Validator) =
validator.rounds.addNewRound()
func skips(blck: Block, round: uint64, author: CommitteeMember): bool =
for parent in blck.parents:
if parent.round == round and parent.author == author:
return false
true
func updateSkipped(validator: Validator, supporter: Block) =
func updateSkipped(validator: Validator, supporter: Validator.Dependencies.Block) =
func skips(blck: Validator.Dependencies.Block, round: uint64, author: CommitteeMember): bool =
for parent in blck.parents:
if parent.round == round and parent.author == author:
return false
true
if round =? validator.rounds.latest.find(supporter.round) and
previous =? round.previous:
for proposer in previous.proposers:
@ -59,7 +58,8 @@ func updateSkipped(validator: Validator, supporter: Block) =
let stake = validator.committee.stake(author)
slot.skipBy(author, stake)
func updateCertified(validator: Validator, certificate: Block) =
func updateCertified(validator: Validator, certificate: Validator.Dependencies.Block) =
mixin id
without certifying =? validator.rounds.latest.find(certificate.round) and
voting =? certifying.previous and
proposing =? voting.previous:
@ -83,6 +83,7 @@ func addBlock(validator: Validator, signedBlock: SignedBlock) =
validator.updateCertified(signedBlock.blck)
func parentBlocks*(validator: Validator): auto =
mixin id
var parents: seq[BlockId[Validator.Dependencies.Hash]]
if previous =? validator.rounds.latest.previous:
for slot in previous.slots:
@ -91,6 +92,7 @@ func parentBlocks*(validator: Validator): auto =
parents
func check*(validator: Validator, signed: SignedBlock): auto =
mixin id
type BlockCheck = checks.BlockCheck[SignedBlock.Dependencies]
type BlockId = blocks.BlockId[SignedBlock.Dependencies.Hash]
without member =? validator.committee.membership(signed.signer):
@ -141,7 +143,7 @@ func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
return
without anchorProposal =? anchor.proposal:
return
var todo = anchorProposal.blck.parents.copy
var todo = anchorProposal.blck.parents
while todo.len > 0:
let parent = todo.pop()
if parent.round < round.number + 2:
@ -152,7 +154,7 @@ func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
return
without parentBlock =? round.find(parent):
raiseAssert "parent block not found"
todo.add(parentBlock.blck.parents.copy)
todo.add(parentBlock.blck.parents)
slot.skip()
iterator committed*(validator: Validator): auto =

View File

@ -65,6 +65,7 @@ func find*(round: Round, number: uint64): ?Round =
current = previous
func find*(round: Round, blockId: BlockId): auto =
mixin id
if found =? round.find(blockId.round):
let slot = found[blockId.author]
for proposal in slot.proposals:

View File

@ -69,6 +69,7 @@ func certifyBy*(proposal: Proposal, certificate: BlockId, stake: Stake) =
proposal.slot.status = SlotStatus.commit
func certify*(proposal, anchor: Proposal) =
mixin id
assert proposal.slot.status == SlotStatus.undecided
assert anchor.slot.status == SlotStatus.commit
assert anchor.certifiedBy.stake > 2/3

View File

@ -16,24 +16,14 @@ proc example*(T: type BlockId): T =
let hash = T.Hash.example
BlockId.new(author, round, hash)
proc example*(
T: type Block,
author = CommitteeMember.example,
round = uint64.example
): T =
type Transaction = T.Dependencies.Transaction
let parents = seq[BlockId[T.Dependencies.Hash]].example
let transactions = seq[Transaction].example
T.new(author, round, parents, transactions)
proc example*(
T: type SignedBlock,
author = CommitteeMember.example,
round = uint64.example
): T =
let blck = Block[T.Dependencies].example(author = author, round = round)
let blck = T.Dependencies.Block.example(author = author, round = round)
let signature = T.Dependencies.Signature.example
SignedBlock.init(blck, signature)
SignedBlock[T.Dependencies].init(blck, signature)
proc example*[T](_: type seq[T], length=0..10): seq[T] =
let size = rand(length)
@ -54,3 +44,12 @@ proc example*(T: type MockIdentifier): T =
proc example*(T: type MockSignature): T =
MockIdentity.example.sign(MockHash.example)
proc example*(
_: type MockBlock,
author = CommitteeMember.example,
round = uint64.example
): MockBlock =
let parents = seq[BlockId[MockHash]].example
let transactions = seq[MockTransaction].example
MockBlock.new(author, round, parents, transactions)

View File

@ -2,16 +2,15 @@ import mysticeti/dependencies
import ./mocks/signing
import ./mocks/hashing
import ./mocks/transaction
import ./mocks/serialization
import ./mocks/blck
export signing
export hashing
export transaction
export serialization
export blck
type MockDependencies* = Dependencies[
MockTransaction,
MockSerialization,
MockBlock,
MockHash,
MockIdentifier,
MockSignature

View File

@ -0,0 +1,33 @@
import mysticeti
import mysticeti/basics
import ./hashing
import ./transaction
type MockBlock* = ref object
author*: CommitteeMember
round*: uint64
parents*: seq[BlockId[MockHash]]
transactions*: seq[MockTransaction]
id: ?BlockId[MockHash]
func new*(
_: type MockBlock,
author: CommitteeMember,
round: uint64,
parents: seq[BlockId[MockHash]],
transactions: seq[MockTransaction]
): auto =
MockBlock(
author: author,
round: round,
parents: parents,
transactions: transactions
)
func id*(blck: MockBlock): auto =
without var id =? blck.id:
let blockBytes = cast[seq[byte]]($blck[])
let blockHash = MockHash.hash(blockBytes)
id = BlockId.new(blck.author, blck.round, blockHash)
blck.id = some id
id

View File

@ -1,32 +0,0 @@
import std/json
import mysticeti
import ./transaction
type MockSerialization* = object
proc `%`*(member: CommitteeMember): JsonNode =
%member.int
proc `%`*(id: BlockId): JsonNode =
%*{
"author": id.author,
"round": id.round,
"hash": $id.hash
}
proc `%`*(transaction: MockTransaction): JsonNode =
%*{
"nonce": transaction.nonce
}
proc `%`*(blck: Block): JsonNode =
%*{
"author": blck.author,
"round": blck.round,
"parents": blck.parents,
"transactions": blck.transactions
}
func toBytes*(_: type MockSerialization, blck: Block): seq[byte] =
let json = %blck
cast[seq[byte]]($json)

View File

@ -5,8 +5,8 @@ import mysticeti/blocks
type Validator = mysticeti.Validator[MockDependencies]
type Committee = mysticeti.Committee[MockDependencies]
type Identity = MockIdentity
type Transaction = MockDependencies.Transaction
type Block = blocks.Block[MockDependencies]
type Transaction = MockTransaction
type Block = MockBlock
type SignedBlock = blocks.SignedBlock[MockDependencies]
type NetworkSimulator* = object

View File

@ -4,34 +4,12 @@ import mysticeti/blocks
suite "Blocks":
type Block = mysticeti.Block[MockDependencies]
type BlockId = mysticeti.BlockId[MockDependencies.Hash]
type Identity = MockIdentity
type Transaction = MockDependencies.Transaction
type Hash = MockDependencies.Hash
type Serialization = MockDependencies.Serialization
test "blocks have an author, a round, parents and transactions":
let author = CommitteeMember.example
let round = uint64.example
let parents = seq[BlockId].example
let transactions = seq[Transaction].example
let blck = Block.new(author, round, parents, transactions)
check blck.author == author
check blck.round == round
check blck.parents == parents.immutable
check blck.transactions == transactions.immutable
test "blocks have an id consisting of author, round and hash":
let blck = Block.example
let id = blck.id
check id.author == blck.author
check id.round == blck.round
check id.hash == Hash.hash(Serialization.toBytes(blck))
type SignedBlock = mysticeti.SignedBlock[MockDependencies]
test "blocks can be signed":
let signer = Identity.init
let blck = Block.example
let blck = MockBlock.example
let signature = signer.sign(blck.id.hash)
let signed = SignedBlock.init(blck, signature)
check signed.blck == blck

View File

@ -7,7 +7,7 @@ import mysticeti/validator/round
suite "Validator Round":
type Round = round.Round[MockDependencies]
type Block = mysticeti.Block[MockDependencies]
type Block = MockBlock
type SignedBlock = mysticeti.SignedBlock[MockDependencies]
test "rounds have a number":

View File

@ -6,8 +6,9 @@ import mysticeti/blocks
suite "Validator Network":
type Transaction = MockDependencies.Transaction
type Block = blocks.Block[MockDependencies]
type Transaction = MockTransaction
type Block = MockBlock
type SignedBlock = blocks.SignedBlock[MockDependencies]
type BlockId = blocks.BlockId[MockDependencies.Hash]
type Hash = MockDependencies.Hash