Implement signature and block fork choide rule

This commit is contained in:
mratsim 2018-09-04 16:22:10 +02:00
parent 1f1328d019
commit 1cb28aecc8
3 changed files with 81 additions and 13 deletions

View File

@ -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)

View File

@ -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

View File

@ -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: