mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-02 01:36:06 +00:00
Make sense of Ghost node Python spec dynamic polymorphism
This commit is contained in:
parent
f7e876471e
commit
1f1328d019
@ -8,73 +8,29 @@
|
||||
# A port of https://github.com/ethereum/research/blob/master/clock_disparity/ghost_node.py
|
||||
# Specs: https://ethresear.ch/t/beacon-chain-casper-ffg-rpj-mini-spec/2760
|
||||
# Part of Casper+Sharding chain v2.1: https://notes.ethereum.org/SCIg8AH5SA-O4C1G1LYZHQ#
|
||||
# Note that implementation is not updated to the latest v2.1 yet
|
||||
|
||||
import
|
||||
tables, deques, # Stdlib
|
||||
nimcrypto, # Nimble packages
|
||||
tables, deques, strutils, # Stdlib
|
||||
nimcrypto, # Nimble packages
|
||||
# ../datatypes, # BeaconBlock is still different from the simulation blocks
|
||||
./networksim # From repo
|
||||
# Local imports
|
||||
./fork_choice_types,
|
||||
./networksim
|
||||
|
||||
import hashes
|
||||
func hash(x: MDigest): Hash =
|
||||
# Allow usage of MDigest in hashtables
|
||||
const bytes = x.type.bits div 8
|
||||
result = x.unsafeAddr.hashData(bytes)
|
||||
###########################################################
|
||||
# Forward declarations
|
||||
|
||||
const
|
||||
NOTARIES = 100 # Committee size in Casper v2.1
|
||||
SLOT_SIZE = 6 # Slot duration in Casper v2.1
|
||||
EPOCH_LENGTH = 25 # Cycle length inCasper v2.
|
||||
method on_receive(self: Node, obj: BlockOrSig, reprocess = false) {.base.} =
|
||||
raise newException(ValueError, "Not implemented error. Please implement in child types")
|
||||
|
||||
# TODO, clear up if reference semantics are needed
|
||||
# for the tables, Block and Sig
|
||||
###########################################################
|
||||
|
||||
type
|
||||
Block = ref object
|
||||
contents: array[32, byte]
|
||||
parent_hash: MDigest[256]
|
||||
hash: MDigest[256]
|
||||
height: int # slot in Casper v2.1 spec
|
||||
proposer: int64
|
||||
slot: int64
|
||||
|
||||
func min_timestamp(self: Block): int64 =
|
||||
SLOT_SIZE * self.slot
|
||||
|
||||
let Genesis = Block()
|
||||
|
||||
type
|
||||
Sig = object
|
||||
# TODO: unsure if this is still relevant in Casper v2.1
|
||||
proposer: int64 # the validator that creates a block
|
||||
targets: seq[MDigest[256]] # the hash of blocks proposed
|
||||
slot: int64 # slot number
|
||||
timestamp: int64 # ts in the ref implementation
|
||||
hash: MDigest[384] # The signature (BLS12-384)
|
||||
|
||||
type
|
||||
Node = ref object
|
||||
|
||||
blocks: TableRef[MDigest[256], Block]
|
||||
sigs: TableRef[MDigest[384], Sig]
|
||||
main_chain: seq[MDigest[256]]
|
||||
timequeue: seq[Block]
|
||||
parentqueue: TableRef[MDigest[256], Node]
|
||||
children: TableRef[MDigest[256], seq[MDigest[256]]]
|
||||
scores: TableRef[MDigest[256], int]
|
||||
scores_at_height: TableRef[MDigest[256], int] # Should be slot not height in v2.1
|
||||
justified: TableRef[MDigest[256], bool]
|
||||
finalized: TableRef[MDigest[256], bool]
|
||||
timestamp: int64
|
||||
id: int64
|
||||
network: NetworkSimulator
|
||||
used_parents: TableRef[MDigest[256], Node]
|
||||
processed: TableRef[MDigest[256], Block]
|
||||
sleepy: bool
|
||||
careless: bool
|
||||
first_round: bool
|
||||
last_made_block: int64
|
||||
last_made_sig: int64
|
||||
proc broadcast(self: Node, x: Block) =
|
||||
if self.sleepy and self.timestamp != 0:
|
||||
return
|
||||
self.network.broadcast(self, x)
|
||||
self.on_receive(x)
|
||||
|
||||
proc log(self: Node, words: string, lvl = 3, all = false) =
|
||||
if (self.id == 0 or all) and lvl >= 2:
|
||||
@ -86,7 +42,11 @@ func add_to_timequeue(self: Node, obj: Block) =
|
||||
inc i
|
||||
self.timequeue.insert(obj, i)
|
||||
|
||||
func add_to_multiset[K, V](self: Node, multiset: var TableRef[K, V], k: K, v: V) =
|
||||
func add_to_multiset[K, V](
|
||||
self: Node,
|
||||
multiset: TableRef[K, seq[V]],
|
||||
k: K,
|
||||
v: V or seq[V]) =
|
||||
if k notin multiset:
|
||||
multiset[k] = @[]
|
||||
multiset[k].add v
|
||||
@ -120,3 +80,78 @@ func recalculate_head(self: Node) =
|
||||
self.change_head(self.main_chain, self.blocks[new_head])
|
||||
else:
|
||||
return
|
||||
|
||||
proc process_children(self: Node, h: MDigest[256]) =
|
||||
if h in self.parentqueue:
|
||||
for b in self.parentqueue[h]:
|
||||
self.on_receive(b, reprocess = true)
|
||||
self.parentqueue.del h
|
||||
|
||||
func get_common_ancestor(self: Node, hash_a, hash_b: MDigest[256]): Block =
|
||||
var (a, b) = (self.blocks[hash_a], self.blocks[hash_b])
|
||||
while b.height > a.height:
|
||||
b = self.blocks[b.parent_hash]
|
||||
while a.height > b.height:
|
||||
a = self.blocks[a.parent_hash]
|
||||
while a.hash != b.hash:
|
||||
a = self.blocks[a.parent_hash]
|
||||
b = self.blocks[b.parent_hash]
|
||||
return a
|
||||
|
||||
func is_descendant(self: Node, hash_a, hash_b: MDigest[256]): bool =
|
||||
let a = self.blocks[hash_a]
|
||||
var b = self.blocks[hash_b]
|
||||
while b.height > a.height:
|
||||
b = self.blocks[b.parent_hash]
|
||||
return a.hash == b.hash
|
||||
|
||||
proc have_ancestry(self: Node, h: MDigest[256]): bool =
|
||||
let h = BlockHash(raw: h)
|
||||
while h.raw != Genesis.hash:
|
||||
if h notin self.processed:
|
||||
return false
|
||||
let wip = self.processed[h]
|
||||
if wip is Block:
|
||||
h.raw = Block(wip).parent_hash
|
||||
return true
|
||||
|
||||
proc on_receive(self: Node, blck: Block, reprocess = false) =
|
||||
block: # Common part of on_receive
|
||||
let hash = BlockHash(raw: blck.hash)
|
||||
if hash in self.processed and not reprocess:
|
||||
return
|
||||
self.processed[hash] = blck
|
||||
|
||||
# parent not yet received
|
||||
if blck.parent_hash notin self.blocks:
|
||||
self.add_to_multiset(self.parentqueue, blck.parent_hash, blck)
|
||||
return
|
||||
# Too early
|
||||
if blck.min_timestamp > self.timestamp:
|
||||
self.add_to_timequeue(blck)
|
||||
return
|
||||
# Add the block
|
||||
self.log("Processing beacon block &" % blck.hash.data[0 .. ^4].toHex(false))
|
||||
self.blocks[blck.hash] = blck
|
||||
# Is the block building on the head? Then add it to the head!
|
||||
if blck.parent_hash == self.main_chain[^1] or self.careless:
|
||||
self.main_chain.add(blck.hash)
|
||||
# Add child record
|
||||
self.add_to_multiset(self.children, blck.parent_hash, blck.hash)
|
||||
# Final steps
|
||||
self.process_children(blck.hash)
|
||||
self.network.broadcast(self, blck)
|
||||
|
||||
proc on_receive(self: Node, sig: Sig, reprocess = false) =
|
||||
block: # Common part of on_receive
|
||||
let hash = SigHash(raw: sig.hash)
|
||||
if hash in self.processed and not reprocess:
|
||||
return
|
||||
self.processed[hash] = sig
|
||||
|
||||
if sig.targets[0] notin self.blocks:
|
||||
self.add_to_multiset(self.parentqueue, sig.targets[0], sig)
|
||||
return
|
||||
|
||||
# Get common ancestor
|
||||
|
||||
|
113
beacon_chain/fork_choice_rule/fork_choice_types.nim
Normal file
113
beacon_chain/fork_choice_rule/fork_choice_types.nim
Normal file
@ -0,0 +1,113 @@
|
||||
# beacon_chain
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
# A port of https://github.com/ethereum/research/blob/master/clock_disparity/ghost_node.py
|
||||
# Specs: https://ethresear.ch/t/beacon-chain-casper-ffg-rpj-mini-spec/2760
|
||||
# Part of Casper+Sharding chain v2.1: https://notes.ethereum.org/SCIg8AH5SA-O4C1G1LYZHQ#
|
||||
# Note that implementation is not updated to the latest v2.1 yet
|
||||
|
||||
import
|
||||
tables, deques, strutils, hashes, # Stdlib
|
||||
nimcrypto # Nimble packages
|
||||
|
||||
const
|
||||
NOTARIES* = 100 # Committee size in Casper v2.1
|
||||
SLOT_SIZE* = 6 # Slot duration in Casper v2.1
|
||||
EPOCH_LENGTH* = 25 # Cycle length inCasper v2.
|
||||
|
||||
# TODO, clear up if reference semantics are needed
|
||||
# for the tables. I.e. what's their maximum size.
|
||||
|
||||
type
|
||||
BlockOrSig* = ref object of RootObj
|
||||
# For some reason, Block and Sig have to be stored
|
||||
# in an heterogenous container.
|
||||
# So we use inheritance to erase types
|
||||
|
||||
BlockOrSigHash* = ref object of RootObj
|
||||
BlockHash* = ref object of BlockOrSigHash
|
||||
raw*: MDigest[256]
|
||||
SigHash* = ref object of BlockOrSigHash
|
||||
raw*: MDigest[384]
|
||||
|
||||
Block* = ref object of BlockOrSig
|
||||
contents*: array[32, byte]
|
||||
parent_hash*: MDigest[256]
|
||||
hash*: MDigest[256]
|
||||
height*: int # slot in Casper v2.1 spec
|
||||
proposer*: int64
|
||||
slot*: int64
|
||||
##########################################
|
||||
|
||||
func min_timestamp*(self: Block): int64 =
|
||||
SLOT_SIZE * self.slot
|
||||
|
||||
let Genesis* = Block()
|
||||
|
||||
##########################################
|
||||
|
||||
func hash*(x: MDigest): Hash =
|
||||
## Allow usage of MDigest in hashtables
|
||||
# We just keep the first 64 bits of the digest
|
||||
const bytes = x.type.bits div 8
|
||||
const nb_ints = bytes div sizeof(int) # Hash is a distinct int
|
||||
|
||||
result = cast[array[nb_ints, Hash]](x)[0]
|
||||
# Alternatively hash for real
|
||||
# result = x.unsafeAddr.hashData(bytes)
|
||||
|
||||
method hash*(x: BlockOrSigHash): Hash {.base.}=
|
||||
raise newException(ValueError, "Not implemented error. Please implement in child types")
|
||||
|
||||
method hash*(x: BlockHash): Hash =
|
||||
## Allow usage of Blockhash in tables
|
||||
x.raw.hash
|
||||
|
||||
method hash*(x: SigHash): Hash =
|
||||
## Allow usage of Sighash in tables
|
||||
x.raw.hash
|
||||
|
||||
#########################################
|
||||
|
||||
type
|
||||
NetworkSimulator* = ref object
|
||||
agents*: seq[int]
|
||||
latency_distribution_sample*: proc (): int
|
||||
time*: int64
|
||||
objqueue*: TableRef[int64, seq[(Node, Block)]]
|
||||
peers*: TableRef[int, seq[Node]]
|
||||
reliability*: float
|
||||
|
||||
Sig* = ref object of BlockOrSig
|
||||
# TODO: unsure if this is still relevant in Casper v2.1
|
||||
proposer*: int64 # the validator that creates a block
|
||||
targets*: seq[MDigest[256]] # the hash of blocks proposed
|
||||
slot*: int64 # slot number
|
||||
timestamp*: int64 # ts in the ref implementation
|
||||
hash*: MDigest[384] # The signature (BLS12-384)
|
||||
|
||||
Node* = ref object
|
||||
blocks*: TableRef[MDigest[256], Block]
|
||||
sigs*: TableRef[MDigest[384], Sig]
|
||||
main_chain*: seq[MDigest[256]]
|
||||
timequeue*: seq[Block]
|
||||
parentqueue*: TableRef[MDigest[256], seq[BlockOrSig]]
|
||||
children*: TableRef[MDigest[256], seq[MDigest[256]]]
|
||||
scores*: TableRef[MDigest[256], int]
|
||||
scores_at_height*: TableRef[MDigest[256], int] # Should be slot not height in v2.1
|
||||
justified*: TableRef[MDigest[256], bool]
|
||||
finalized*: TableRef[MDigest[256], bool]
|
||||
timestamp*: int64
|
||||
id*: int
|
||||
network*: NetworkSimulator
|
||||
used_parents*: TableRef[MDigest[256], Node]
|
||||
processed*: TableRef[BlockOrSigHash, BlockOrSig]
|
||||
sleepy*: bool
|
||||
careless*: bool
|
||||
first_round*: bool
|
||||
last_made_block*: int64
|
||||
last_made_sig*: int64
|
@ -9,13 +9,13 @@
|
||||
# Specs: https://ethresear.ch/t/beacon-chain-casper-ffg-rpj-mini-spec/2760
|
||||
# Part of Casper+Sharding chain v2.1: https://notes.ethereum.org/SCIg8AH5SA-O4C1G1LYZHQ#
|
||||
|
||||
import tables
|
||||
import
|
||||
tables,
|
||||
./fork_choice_types
|
||||
|
||||
type
|
||||
NetworkSimulator* = ref object
|
||||
agents*: seq[int]
|
||||
latency_distribution_sample*: seq[int]
|
||||
time*: int64
|
||||
objqueue*: Table[int, int]
|
||||
peers*: Table[int, int]
|
||||
reliability*: float
|
||||
func broadcast*(self: NetworkSimulator, sender: Node, obj: Block) =
|
||||
for p in self.peers[sender.id]:
|
||||
let recv_time = self.time + self.latency_distribution_sample()
|
||||
if recv_time notin self.objqueue:
|
||||
self.objqueue[recv_time] = @[]
|
||||
self.objqueue[recv_time].add (p, obj)
|
||||
|
Loading…
x
Reference in New Issue
Block a user