Implement signature and block fork choide rule
This commit is contained in:
parent
1f1328d019
commit
1cb28aecc8
|
@ -11,12 +11,12 @@
|
||||||
# Note that implementation is not updated to the latest v2.1 yet
|
# Note that implementation is not updated to the latest v2.1 yet
|
||||||
|
|
||||||
import
|
import
|
||||||
tables, deques, strutils, # Stdlib
|
# Stdlib
|
||||||
nimcrypto, # Nimble packages
|
tables, deques, strutils, endians, strformat,
|
||||||
# ../datatypes, # BeaconBlock is still different from the simulation blocks
|
# Nimble packages
|
||||||
|
nimcrypto,
|
||||||
# Local imports
|
# Local imports
|
||||||
./fork_choice_types,
|
./fork_choice_types, ./networksim
|
||||||
./networksim
|
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
|
@ -115,7 +115,7 @@ proc have_ancestry(self: Node, h: MDigest[256]): bool =
|
||||||
h.raw = Block(wip).parent_hash
|
h.raw = Block(wip).parent_hash
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc on_receive(self: Node, blck: Block, reprocess = false) =
|
method on_receive(self: Node, blck: Block, reprocess = false) =
|
||||||
block: # Common part of on_receive
|
block: # Common part of on_receive
|
||||||
let hash = BlockHash(raw: blck.hash)
|
let hash = BlockHash(raw: blck.hash)
|
||||||
if hash in self.processed and not reprocess:
|
if hash in self.processed and not reprocess:
|
||||||
|
@ -131,7 +131,7 @@ proc on_receive(self: Node, blck: Block, reprocess = false) =
|
||||||
self.add_to_timequeue(blck)
|
self.add_to_timequeue(blck)
|
||||||
return
|
return
|
||||||
# Add the block
|
# Add the block
|
||||||
self.log("Processing beacon block &" % blck.hash.data[0 .. ^4].toHex(false))
|
self.log "Processing beacon block &" % blck.hash.data[0 .. ^4].toHex(false)
|
||||||
self.blocks[blck.hash] = blck
|
self.blocks[blck.hash] = blck
|
||||||
# Is the block building on the head? Then add it to the head!
|
# Is the block building on the head? Then add it to the head!
|
||||||
if blck.parent_hash == self.main_chain[^1] or self.careless:
|
if blck.parent_hash == self.main_chain[^1] or self.careless:
|
||||||
|
@ -142,7 +142,7 @@ proc on_receive(self: Node, blck: Block, reprocess = false) =
|
||||||
self.process_children(blck.hash)
|
self.process_children(blck.hash)
|
||||||
self.network.broadcast(self, blck)
|
self.network.broadcast(self, blck)
|
||||||
|
|
||||||
proc on_receive(self: Node, sig: Sig, reprocess = false) =
|
method on_receive(self: Node, sig: Sig, reprocess = false) =
|
||||||
block: # Common part of on_receive
|
block: # Common part of on_receive
|
||||||
let hash = SigHash(raw: sig.hash)
|
let hash = SigHash(raw: sig.hash)
|
||||||
if hash in self.processed and not reprocess:
|
if hash in self.processed and not reprocess:
|
||||||
|
@ -152,6 +152,74 @@ proc on_receive(self: Node, sig: Sig, reprocess = false) =
|
||||||
if sig.targets[0] notin self.blocks:
|
if sig.targets[0] notin self.blocks:
|
||||||
self.add_to_multiset(self.parentqueue, sig.targets[0], sig)
|
self.add_to_multiset(self.parentqueue, sig.targets[0], sig)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get common ancestor
|
# Get common ancestor
|
||||||
|
let anc = self.get_common_ancestor(self.main_chain[^1], sig.targets[0])
|
||||||
|
let max_score = block:
|
||||||
|
var max = 0
|
||||||
|
for i in anc.height + 1 ..< self.main_chain.len:
|
||||||
|
max = max(max, self.scores.getOrDefault(self.main_chain[i], 0))
|
||||||
|
max
|
||||||
|
# Process scoring
|
||||||
|
var max_newchain_score = 0
|
||||||
|
for i in countdown(sig.targets.len - 1, 0):
|
||||||
|
let c = sig.targets[i]
|
||||||
|
|
||||||
|
let slot = sig.slot - 1 - i
|
||||||
|
var slot_key: array[4, byte]
|
||||||
|
bigEndian32(slot_key.addr, slot.unsafeAddr)
|
||||||
|
doAssert self.blocks[c].slot <= slot
|
||||||
|
|
||||||
|
# If a parent and child block have non-consecutive slots, then the parent
|
||||||
|
# block is also considered to be the canonical block at all of the intermediate
|
||||||
|
# slot numbers. We store the scores for the block at each height separately
|
||||||
|
var key: array[36, byte]
|
||||||
|
key[0 ..< 4] = slot_key
|
||||||
|
key[4 ..< 36] = c.data
|
||||||
|
self.scores_at_height[key] = self.scores_at_height.getOrDefault(key, 0) + 1
|
||||||
|
|
||||||
|
# For fork choice rule purposes, the score of a block is the highst score
|
||||||
|
# that it has at any height
|
||||||
|
self.scores[c] = max(self.scores.getOrDefault(c, 0), self.scores_at_height[key])
|
||||||
|
|
||||||
|
# If 2/3 of notaries vote for a block, it is justified
|
||||||
|
if self.scores_at_height[key] == NOTARIES * 2 div 3: # Shouldn't that be >= ?
|
||||||
|
self.justified[c] = true
|
||||||
|
var c2 = c
|
||||||
|
self.log &"Justified: {slot} {($c)[0 ..< 8]}"
|
||||||
|
|
||||||
|
# If EPOCH_LENGTH+1 blocks are justified in a row, the oldest is
|
||||||
|
# considered finalized
|
||||||
|
|
||||||
|
var finalize = true
|
||||||
|
for slot2 in countdown(slot-1, max(slot - EPOCH_LENGTH * 1, 0) - 1):
|
||||||
|
if slot2 < self.blocks[c2].slot:
|
||||||
|
c2 = self.blocks[c2].parent_hash
|
||||||
|
|
||||||
|
var slot_key2: array[4, byte]
|
||||||
|
bigEndian32(slot_key2.addr, slot2.unsafeAddr)
|
||||||
|
var key2: array[36, byte]
|
||||||
|
key[0 ..< 4] = slot_key2
|
||||||
|
key[4 ..< 36] = c2.data
|
||||||
|
|
||||||
|
if self.scores_at_height.getOrDefault(key2, 0) < NOTARIES * 2 div 3:
|
||||||
|
finalize = false
|
||||||
|
self.log &"Not quite finalized: stopped at {slot2} needed {max(slot - EPOCH_LENGTH, 0)}"
|
||||||
|
break
|
||||||
|
|
||||||
|
if slot2 < slot - EPOCH_LENGTH - 1 and finalize and c2 notin self.finalized:
|
||||||
|
self.log &"Finalized: {self.blocks[c2].slot} {($c)[0 ..< 8]}"
|
||||||
|
self.finalized[c2] = true
|
||||||
|
|
||||||
|
# Find the maximum score of a block on the chain that this sig is weighing on
|
||||||
|
if self.blocks[c].slot > anc.slot:
|
||||||
|
max_newchain_score = max(max_newchain_score, self.scores[c])
|
||||||
|
|
||||||
|
# If it's higher, switch over the canonical chain
|
||||||
|
if max_newchain_score > max_score:
|
||||||
|
self.main_chain = self.mainchain[0 ..< anc.height + 1]
|
||||||
|
self.recalculate_head()
|
||||||
|
|
||||||
|
self.sigs[sig.hash] = sig
|
||||||
|
|
||||||
|
# Rebroadcast
|
||||||
|
self.network.broadcast(self, sig)
|
||||||
|
|
|
@ -78,7 +78,7 @@ type
|
||||||
agents*: seq[int]
|
agents*: seq[int]
|
||||||
latency_distribution_sample*: proc (): int
|
latency_distribution_sample*: proc (): int
|
||||||
time*: int64
|
time*: int64
|
||||||
objqueue*: TableRef[int64, seq[(Node, Block)]]
|
objqueue*: TableRef[int64, seq[(Node, BlockOrSig)]]
|
||||||
peers*: TableRef[int, seq[Node]]
|
peers*: TableRef[int, seq[Node]]
|
||||||
reliability*: float
|
reliability*: float
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ type
|
||||||
# TODO: unsure if this is still relevant in Casper v2.1
|
# TODO: unsure if this is still relevant in Casper v2.1
|
||||||
proposer*: int64 # the validator that creates a block
|
proposer*: int64 # the validator that creates a block
|
||||||
targets*: seq[MDigest[256]] # the hash of blocks proposed
|
targets*: seq[MDigest[256]] # the hash of blocks proposed
|
||||||
slot*: int64 # slot number
|
slot*: int32 # slot number
|
||||||
timestamp*: int64 # ts in the ref implementation
|
timestamp*: int64 # ts in the ref implementation
|
||||||
hash*: MDigest[384] # The signature (BLS12-384)
|
hash*: MDigest[384] # The signature (BLS12-384)
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ type
|
||||||
parentqueue*: TableRef[MDigest[256], seq[BlockOrSig]]
|
parentqueue*: TableRef[MDigest[256], seq[BlockOrSig]]
|
||||||
children*: TableRef[MDigest[256], seq[MDigest[256]]]
|
children*: TableRef[MDigest[256], seq[MDigest[256]]]
|
||||||
scores*: TableRef[MDigest[256], int]
|
scores*: TableRef[MDigest[256], int]
|
||||||
scores_at_height*: TableRef[MDigest[256], int] # Should be slot not height in v2.1
|
scores_at_height*: TableRef[array[36, byte], int] # Should be slot not height in v2.1
|
||||||
justified*: TableRef[MDigest[256], bool]
|
justified*: TableRef[MDigest[256], bool]
|
||||||
finalized*: TableRef[MDigest[256], bool]
|
finalized*: TableRef[MDigest[256], bool]
|
||||||
timestamp*: int64
|
timestamp*: int64
|
||||||
|
|
|
@ -13,7 +13,7 @@ import
|
||||||
tables,
|
tables,
|
||||||
./fork_choice_types
|
./fork_choice_types
|
||||||
|
|
||||||
func broadcast*(self: NetworkSimulator, sender: Node, obj: Block) =
|
func broadcast*(self: NetworkSimulator, sender: Node, obj: BlockOrSig) =
|
||||||
for p in self.peers[sender.id]:
|
for p in self.peers[sender.id]:
|
||||||
let recv_time = self.time + self.latency_distribution_sample()
|
let recv_time = self.time + self.latency_distribution_sample()
|
||||||
if recv_time notin self.objqueue:
|
if recv_time notin self.objqueue:
|
||||||
|
|
Loading…
Reference in New Issue