mirror of
https://github.com/logos-storage/nim-mysticeti.git
synced 2026-01-05 23:23:08 +00:00
make Block a type dependency
This commit is contained in:
parent
aa4f390a1a
commit
de854cbb5f
@ -36,19 +36,8 @@ export committee.`$`
|
|||||||
|
|
||||||
import ./mysticeti/blocks
|
import ./mysticeti/blocks
|
||||||
|
|
||||||
export blocks.Block
|
|
||||||
export blocks.BlockId
|
export blocks.BlockId
|
||||||
export blocks.author
|
export blocks.new
|
||||||
export blocks.round
|
export blocks.SignedBlock
|
||||||
export blocks.parents
|
export blocks.blck
|
||||||
export blocks.id
|
export blocks.signer
|
||||||
|
|
||||||
import ./mysticeti/basics/immutableseq
|
|
||||||
|
|
||||||
export immutableseq
|
|
||||||
|
|
||||||
import ./mysticeti/blocks/signed
|
|
||||||
|
|
||||||
export signed.SignedBlock
|
|
||||||
export signed.blck
|
|
||||||
export signed.signer
|
|
||||||
|
|||||||
@ -11,7 +11,3 @@ export results
|
|||||||
import ./dependencies
|
import ./dependencies
|
||||||
|
|
||||||
export dependencies
|
export dependencies
|
||||||
|
|
||||||
import ./basics/immutableseq
|
|
||||||
|
|
||||||
export immutableseq
|
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
import ./blocks/blck
|
|
||||||
import ./blocks/blockid
|
import ./blocks/blockid
|
||||||
import ./blocks/signed
|
import ./blocks/signed
|
||||||
|
|
||||||
export blck
|
|
||||||
export blockid
|
export blockid
|
||||||
export signed
|
export signed
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -1,21 +1,21 @@
|
|||||||
import ../basics
|
import ../basics
|
||||||
import ./blck
|
|
||||||
import ./blockid
|
import ./blockid
|
||||||
|
|
||||||
type SignedBlock*[Dependencies] = object
|
type SignedBlock*[Dependencies] = object
|
||||||
blck: Block[Dependencies]
|
blck: Dependencies.Block
|
||||||
signature: Dependencies.Signature
|
signature: Dependencies.Signature
|
||||||
|
|
||||||
func init*(
|
func init*[Dependencies](
|
||||||
_: type SignedBlock,
|
_: type SignedBlock[Dependencies];
|
||||||
blck: Block,
|
blck: Dependencies.Block,
|
||||||
signature: Block.Dependencies.Signature
|
signature: Dependencies.Signature
|
||||||
): auto =
|
): auto =
|
||||||
SignedBlock[Block.Dependencies](blck: blck, signature: signature)
|
SignedBlock[Dependencies](blck: blck, signature: signature)
|
||||||
|
|
||||||
func blck*(signed: SignedBlock): auto =
|
func blck*(signed: SignedBlock): auto =
|
||||||
signed.blck
|
signed.blck
|
||||||
|
|
||||||
func signer*(signed: SignedBlock): auto =
|
func signer*(signed: SignedBlock): auto =
|
||||||
mixin signer
|
mixin signer
|
||||||
|
mixin id
|
||||||
signed.signature.signer(signed.blck.id.hash)
|
signed.signature.signer(signed.blck.id.hash)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
type Dependencies*[
|
type Dependencies*[
|
||||||
Transaction,
|
Block,
|
||||||
Serialization,
|
|
||||||
Hash,
|
Hash,
|
||||||
Identifier,
|
Identifier,
|
||||||
Signature
|
Signature
|
||||||
|
|||||||
@ -43,13 +43,12 @@ func primaryProposer*(validator: Validator): CommitteeMember =
|
|||||||
func nextRound*(validator: Validator) =
|
func nextRound*(validator: Validator) =
|
||||||
validator.rounds.addNewRound()
|
validator.rounds.addNewRound()
|
||||||
|
|
||||||
func skips(blck: Block, round: uint64, author: CommitteeMember): bool =
|
func updateSkipped(validator: Validator, supporter: Validator.Dependencies.Block) =
|
||||||
for parent in blck.parents:
|
func skips(blck: Validator.Dependencies.Block, round: uint64, author: CommitteeMember): bool =
|
||||||
if parent.round == round and parent.author == author:
|
for parent in blck.parents:
|
||||||
return false
|
if parent.round == round and parent.author == author:
|
||||||
true
|
return false
|
||||||
|
true
|
||||||
func updateSkipped(validator: Validator, supporter: Block) =
|
|
||||||
if round =? validator.rounds.latest.find(supporter.round) and
|
if round =? validator.rounds.latest.find(supporter.round) and
|
||||||
previous =? round.previous:
|
previous =? round.previous:
|
||||||
for proposer in previous.proposers:
|
for proposer in previous.proposers:
|
||||||
@ -59,7 +58,8 @@ func updateSkipped(validator: Validator, supporter: Block) =
|
|||||||
let stake = validator.committee.stake(author)
|
let stake = validator.committee.stake(author)
|
||||||
slot.skipBy(author, stake)
|
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
|
without certifying =? validator.rounds.latest.find(certificate.round) and
|
||||||
voting =? certifying.previous and
|
voting =? certifying.previous and
|
||||||
proposing =? voting.previous:
|
proposing =? voting.previous:
|
||||||
@ -83,6 +83,7 @@ func addBlock(validator: Validator, signedBlock: SignedBlock) =
|
|||||||
validator.updateCertified(signedBlock.blck)
|
validator.updateCertified(signedBlock.blck)
|
||||||
|
|
||||||
func parentBlocks*(validator: Validator): auto =
|
func parentBlocks*(validator: Validator): auto =
|
||||||
|
mixin id
|
||||||
var parents: seq[BlockId[Validator.Dependencies.Hash]]
|
var parents: seq[BlockId[Validator.Dependencies.Hash]]
|
||||||
if previous =? validator.rounds.latest.previous:
|
if previous =? validator.rounds.latest.previous:
|
||||||
for slot in previous.slots:
|
for slot in previous.slots:
|
||||||
@ -91,6 +92,7 @@ func parentBlocks*(validator: Validator): auto =
|
|||||||
parents
|
parents
|
||||||
|
|
||||||
func check*(validator: Validator, signed: SignedBlock): auto =
|
func check*(validator: Validator, signed: SignedBlock): auto =
|
||||||
|
mixin id
|
||||||
type BlockCheck = checks.BlockCheck[SignedBlock.Dependencies]
|
type BlockCheck = checks.BlockCheck[SignedBlock.Dependencies]
|
||||||
type BlockId = blocks.BlockId[SignedBlock.Dependencies.Hash]
|
type BlockId = blocks.BlockId[SignedBlock.Dependencies.Hash]
|
||||||
without member =? validator.committee.membership(signed.signer):
|
without member =? validator.committee.membership(signed.signer):
|
||||||
@ -141,7 +143,7 @@ func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
|
|||||||
return
|
return
|
||||||
without anchorProposal =? anchor.proposal:
|
without anchorProposal =? anchor.proposal:
|
||||||
return
|
return
|
||||||
var todo = anchorProposal.blck.parents.copy
|
var todo = anchorProposal.blck.parents
|
||||||
while todo.len > 0:
|
while todo.len > 0:
|
||||||
let parent = todo.pop()
|
let parent = todo.pop()
|
||||||
if parent.round < round.number + 2:
|
if parent.round < round.number + 2:
|
||||||
@ -152,7 +154,7 @@ func updateIndirect(validator: Validator, slot: ProposerSlot, round: Round) =
|
|||||||
return
|
return
|
||||||
without parentBlock =? round.find(parent):
|
without parentBlock =? round.find(parent):
|
||||||
raiseAssert "parent block not found"
|
raiseAssert "parent block not found"
|
||||||
todo.add(parentBlock.blck.parents.copy)
|
todo.add(parentBlock.blck.parents)
|
||||||
slot.skip()
|
slot.skip()
|
||||||
|
|
||||||
iterator committed*(validator: Validator): auto =
|
iterator committed*(validator: Validator): auto =
|
||||||
|
|||||||
@ -65,6 +65,7 @@ func find*(round: Round, number: uint64): ?Round =
|
|||||||
current = previous
|
current = previous
|
||||||
|
|
||||||
func find*(round: Round, blockId: BlockId): auto =
|
func find*(round: Round, blockId: BlockId): auto =
|
||||||
|
mixin id
|
||||||
if found =? round.find(blockId.round):
|
if found =? round.find(blockId.round):
|
||||||
let slot = found[blockId.author]
|
let slot = found[blockId.author]
|
||||||
for proposal in slot.proposals:
|
for proposal in slot.proposals:
|
||||||
|
|||||||
@ -69,6 +69,7 @@ func certifyBy*(proposal: Proposal, certificate: BlockId, stake: Stake) =
|
|||||||
proposal.slot.status = SlotStatus.commit
|
proposal.slot.status = SlotStatus.commit
|
||||||
|
|
||||||
func certify*(proposal, anchor: Proposal) =
|
func certify*(proposal, anchor: Proposal) =
|
||||||
|
mixin id
|
||||||
assert proposal.slot.status == SlotStatus.undecided
|
assert proposal.slot.status == SlotStatus.undecided
|
||||||
assert anchor.slot.status == SlotStatus.commit
|
assert anchor.slot.status == SlotStatus.commit
|
||||||
assert anchor.certifiedBy.stake > 2/3
|
assert anchor.certifiedBy.stake > 2/3
|
||||||
|
|||||||
@ -16,24 +16,14 @@ proc example*(T: type BlockId): T =
|
|||||||
let hash = T.Hash.example
|
let hash = T.Hash.example
|
||||||
BlockId.new(author, round, hash)
|
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*(
|
proc example*(
|
||||||
T: type SignedBlock,
|
T: type SignedBlock,
|
||||||
author = CommitteeMember.example,
|
author = CommitteeMember.example,
|
||||||
round = uint64.example
|
round = uint64.example
|
||||||
): T =
|
): 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
|
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] =
|
proc example*[T](_: type seq[T], length=0..10): seq[T] =
|
||||||
let size = rand(length)
|
let size = rand(length)
|
||||||
@ -54,3 +44,12 @@ proc example*(T: type MockIdentifier): T =
|
|||||||
|
|
||||||
proc example*(T: type MockSignature): T =
|
proc example*(T: type MockSignature): T =
|
||||||
MockIdentity.example.sign(MockHash.example)
|
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)
|
||||||
|
|||||||
@ -2,16 +2,15 @@ import mysticeti/dependencies
|
|||||||
import ./mocks/signing
|
import ./mocks/signing
|
||||||
import ./mocks/hashing
|
import ./mocks/hashing
|
||||||
import ./mocks/transaction
|
import ./mocks/transaction
|
||||||
import ./mocks/serialization
|
import ./mocks/blck
|
||||||
|
|
||||||
export signing
|
export signing
|
||||||
export hashing
|
export hashing
|
||||||
export transaction
|
export transaction
|
||||||
export serialization
|
export blck
|
||||||
|
|
||||||
type MockDependencies* = Dependencies[
|
type MockDependencies* = Dependencies[
|
||||||
MockTransaction,
|
MockBlock,
|
||||||
MockSerialization,
|
|
||||||
MockHash,
|
MockHash,
|
||||||
MockIdentifier,
|
MockIdentifier,
|
||||||
MockSignature
|
MockSignature
|
||||||
|
|||||||
33
tests/mysticeti/mocks/blck.nim
Normal file
33
tests/mysticeti/mocks/blck.nim
Normal 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
|
||||||
@ -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)
|
|
||||||
@ -5,8 +5,8 @@ import mysticeti/blocks
|
|||||||
type Validator = mysticeti.Validator[MockDependencies]
|
type Validator = mysticeti.Validator[MockDependencies]
|
||||||
type Committee = mysticeti.Committee[MockDependencies]
|
type Committee = mysticeti.Committee[MockDependencies]
|
||||||
type Identity = MockIdentity
|
type Identity = MockIdentity
|
||||||
type Transaction = MockDependencies.Transaction
|
type Transaction = MockTransaction
|
||||||
type Block = blocks.Block[MockDependencies]
|
type Block = MockBlock
|
||||||
type SignedBlock = blocks.SignedBlock[MockDependencies]
|
type SignedBlock = blocks.SignedBlock[MockDependencies]
|
||||||
|
|
||||||
type NetworkSimulator* = object
|
type NetworkSimulator* = object
|
||||||
|
|||||||
@ -4,34 +4,12 @@ import mysticeti/blocks
|
|||||||
|
|
||||||
suite "Blocks":
|
suite "Blocks":
|
||||||
|
|
||||||
type Block = mysticeti.Block[MockDependencies]
|
|
||||||
type BlockId = mysticeti.BlockId[MockDependencies.Hash]
|
|
||||||
type Identity = MockIdentity
|
type Identity = MockIdentity
|
||||||
type Transaction = MockDependencies.Transaction
|
type SignedBlock = mysticeti.SignedBlock[MockDependencies]
|
||||||
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))
|
|
||||||
|
|
||||||
test "blocks can be signed":
|
test "blocks can be signed":
|
||||||
let signer = Identity.init
|
let signer = Identity.init
|
||||||
let blck = Block.example
|
let blck = MockBlock.example
|
||||||
let signature = signer.sign(blck.id.hash)
|
let signature = signer.sign(blck.id.hash)
|
||||||
let signed = SignedBlock.init(blck, signature)
|
let signed = SignedBlock.init(blck, signature)
|
||||||
check signed.blck == blck
|
check signed.blck == blck
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import mysticeti/validator/round
|
|||||||
suite "Validator Round":
|
suite "Validator Round":
|
||||||
|
|
||||||
type Round = round.Round[MockDependencies]
|
type Round = round.Round[MockDependencies]
|
||||||
type Block = mysticeti.Block[MockDependencies]
|
type Block = MockBlock
|
||||||
type SignedBlock = mysticeti.SignedBlock[MockDependencies]
|
type SignedBlock = mysticeti.SignedBlock[MockDependencies]
|
||||||
|
|
||||||
test "rounds have a number":
|
test "rounds have a number":
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import mysticeti/blocks
|
|||||||
|
|
||||||
suite "Validator Network":
|
suite "Validator Network":
|
||||||
|
|
||||||
type Transaction = MockDependencies.Transaction
|
type Transaction = MockTransaction
|
||||||
type Block = blocks.Block[MockDependencies]
|
type Block = MockBlock
|
||||||
|
type SignedBlock = blocks.SignedBlock[MockDependencies]
|
||||||
type BlockId = blocks.BlockId[MockDependencies.Hash]
|
type BlockId = blocks.BlockId[MockDependencies.Hash]
|
||||||
type Hash = MockDependencies.Hash
|
type Hash = MockDependencies.Hash
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user