Merge pull request #826 from status-im/devel
Testnet0 release 2020-03-23
This commit is contained in:
commit
0ed657e953
|
@ -22,7 +22,8 @@
|
|||
|
||||
import
|
||||
options,
|
||||
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator],
|
||||
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator,
|
||||
state_transition_block],
|
||||
./attestation_pool, ./beacon_node_types, ./ssz
|
||||
|
||||
# TODO gossipsub validation lives somewhere, maybe here
|
||||
|
@ -33,13 +34,6 @@ const
|
|||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#configuration
|
||||
ATTESTATION_PROPAGATION_SLOT_RANGE = 32
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/validator/0_beacon-chain-validator.md#aggregation-selection
|
||||
func get_slot_signature(state: BeaconState, slot: Slot, privkey: ValidatorPrivKey):
|
||||
ValidatorSig =
|
||||
let domain =
|
||||
get_domain(state, DOMAIN_BEACON_ATTESTER, compute_epoch_at_slot(slot))
|
||||
bls_sign(privkey, hash_tree_root(slot).data, domain)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregation-selection
|
||||
func is_aggregator(state: BeaconState, slot: Slot, index: uint64,
|
||||
slot_signature: ValidatorSig): bool =
|
||||
|
@ -58,7 +52,7 @@ proc aggregate_attestations*(
|
|||
|
||||
let
|
||||
slot = state.slot - 2
|
||||
slot_signature = get_slot_signature(state, slot, privkey)
|
||||
slot_signature = get_slot_signature(state.fork, slot, privkey)
|
||||
|
||||
if slot < 0:
|
||||
return none(AggregateAndProof)
|
||||
|
|
|
@ -266,10 +266,12 @@ proc add*(pool: var AttestationPool, attestation: Attestation) =
|
|||
pool.addResolved(blck, attestation)
|
||||
|
||||
proc getAttestationsForBlock*(
|
||||
pool: AttestationPool, state: BeaconState,
|
||||
newBlockSlot: Slot): seq[Attestation] =
|
||||
pool: AttestationPool, state: BeaconState): seq[Attestation] =
|
||||
## Retrieve attestations that may be added to a new block at the slot of the
|
||||
## given state
|
||||
logScope: pcs = "retrieve_attestation"
|
||||
|
||||
let newBlockSlot = state.slot
|
||||
if newBlockSlot < (GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
debug "Too early for attestations",
|
||||
newBlockSlot = shortLog(newBlockSlot),
|
||||
|
@ -327,8 +329,7 @@ proc getAttestationsForBlock*(
|
|||
# TODO we're checking signatures here every time which is very slow - this
|
||||
# is needed because validate does nothing for now and we don't want
|
||||
# to include a broken attestation
|
||||
if not check_attestation(
|
||||
state, attestation, {nextSlot}, cache):
|
||||
if not check_attestation(state, attestation, {}, cache):
|
||||
continue
|
||||
|
||||
for v in a.validations[1..^1]:
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import
|
||||
# Standard library
|
||||
os, net, tables, random, strutils, times, sequtils,
|
||||
os, tables, random, strutils, times, sequtils,
|
||||
|
||||
# Nimble packages
|
||||
stew/[objects, bitseqs, byteutils],
|
||||
chronos, chronicles, confutils, metrics,
|
||||
json_serialization/std/[options, sets], serialization/errors,
|
||||
stew/[objects, bitseqs, byteutils], stew/shims/macros,
|
||||
chronos, chronicles, confutils, metrics, json_rpc/[rpcserver, jsonmarshal],
|
||||
json_serialization/std/[options, sets, net], serialization/errors,
|
||||
kvstore, kvstore_sqlite3,
|
||||
eth/p2p/enode, eth/[keys, async_utils], eth/p2p/discoveryv5/enr,
|
||||
eth/p2p/enode, eth/[keys, async_utils], eth/p2p/discoveryv5/[protocol, enr],
|
||||
|
||||
# Local modules
|
||||
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network],
|
||||
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network,
|
||||
state_transition_block], spec/presets/custom,
|
||||
conf, time, state_transition, beacon_chain_db, validator_pool, extras,
|
||||
attestation_pool, block_pool, eth2_network, eth2_discovery,
|
||||
beacon_node_types, mainchain_monitor, version, ssz, ssz/dynamic_navigator,
|
||||
|
@ -23,6 +24,10 @@ const
|
|||
|
||||
type
|
||||
KeyPair = eth2_network.KeyPair
|
||||
RpcServer = RpcHttpServer
|
||||
|
||||
template init(T: type RpcHttpServer, ip: IpAddress, port: Port): T =
|
||||
newRpcHttpServer([initTAddress(ip, port)])
|
||||
|
||||
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#interop-metrics
|
||||
declareGauge beacon_slot,
|
||||
|
@ -60,6 +65,7 @@ type
|
|||
attestationPool: AttestationPool
|
||||
mainchainMonitor: MainchainMonitor
|
||||
beaconClock: BeaconClock
|
||||
rpcServer: RpcServer
|
||||
|
||||
proc onBeaconBlock*(node: BeaconNode, signedBlock: SignedBeaconBlock) {.gcsafe.}
|
||||
proc updateHead(node: BeaconNode): BlockRef
|
||||
|
@ -202,6 +208,11 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
|||
addressFile = string(conf.dataDir) / "beacon_node.address"
|
||||
network.saveConnectionAddressFile(addressFile)
|
||||
|
||||
let rpcServer = if conf.rpcEnabled:
|
||||
RpcServer.init(conf.rpcAddress, conf.rpcPort)
|
||||
else:
|
||||
nil
|
||||
|
||||
var res = BeaconNode(
|
||||
nickname: nickname,
|
||||
network: network,
|
||||
|
@ -217,6 +228,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
|||
attestationPool: AttestationPool.init(blockPool),
|
||||
mainchainMonitor: mainchainMonitor,
|
||||
beaconClock: BeaconClock.init(blockPool.headState.data.data),
|
||||
rpcServer: rpcServer,
|
||||
)
|
||||
|
||||
# TODO sync is called when a remote peer is connected - is that the right
|
||||
|
@ -351,7 +363,7 @@ proc sendAttestation(node: BeaconNode,
|
|||
node.network.broadcast(
|
||||
getAttestationTopic(attestationData.index), attestation)
|
||||
|
||||
if node.config.dump:
|
||||
if node.config.dumpEnabled:
|
||||
SSZ.saveFile(
|
||||
node.config.dumpDir / "att-" & $attestationData.slot & "-" &
|
||||
$attestationData.index & "-" & validator.pubKey.shortLog &
|
||||
|
@ -381,8 +393,8 @@ proc proposeBlock(node: BeaconNode,
|
|||
cat = "fastforward"
|
||||
return head
|
||||
|
||||
# Advance state to the slot immediately preceding the one we're creating a
|
||||
# block for - potentially we will be processing empty slots along the way.
|
||||
# Advance state to the slot that we're proposing for - this is the equivalent
|
||||
# of running `process_slots` up to the slot of the new block.
|
||||
let (nroot, nblck) = node.blockPool.withState(
|
||||
node.blockPool.tmpState, head.atSlot(slot)):
|
||||
let (eth1data, deposits) =
|
||||
|
@ -394,45 +406,27 @@ proc proposeBlock(node: BeaconNode,
|
|||
(node.mainchainMonitor.eth1Data,
|
||||
node.mainchainMonitor.getPendingDeposits())
|
||||
|
||||
# To create a block, we'll first apply a partial block to the state, skipping
|
||||
# some validations.
|
||||
let
|
||||
fork = state.fork
|
||||
blockBody = BeaconBlockBody(
|
||||
randao_reveal: validator.genRandaoReveal(fork, slot),
|
||||
eth1_data: eth1data,
|
||||
attestations:
|
||||
node.attestationPool.getAttestationsForBlock(state, slot),
|
||||
deposits: deposits)
|
||||
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let proposer_index = get_beacon_proposer_index(state, cache)
|
||||
if proposer_index.isNone:
|
||||
doAssert false, "proposeBlock: missing proposer index"
|
||||
let message = makeBeaconBlock(
|
||||
state,
|
||||
head.root,
|
||||
validator.genRandaoReveal(state.fork, slot),
|
||||
eth1data,
|
||||
Eth2Digest(),
|
||||
node.attestationPool.getAttestationsForBlock(state),
|
||||
deposits)
|
||||
|
||||
if not message.isSome():
|
||||
return head # already logged elsewhere!
|
||||
var
|
||||
newBlock = SignedBeaconBlock(
|
||||
message: BeaconBlock(
|
||||
slot: slot,
|
||||
proposer_index: proposer_index.get.uint64,
|
||||
parent_root: head.root,
|
||||
body: blockBody))
|
||||
tmpState = hashedState
|
||||
discard state_transition(tmpState, newBlock, {skipStateRootValidation})
|
||||
# TODO only enable in fast-fail debugging situations
|
||||
# otherwise, bad attestations can bring down network
|
||||
# doAssert ok # TODO: err, could this fail somehow?
|
||||
|
||||
newBlock.message.state_root = tmpState.root
|
||||
message: message.get()
|
||||
)
|
||||
|
||||
let blockRoot = hash_tree_root(newBlock.message)
|
||||
|
||||
# Careful, state no longer valid after here..
|
||||
# We use the fork from the pre-newBlock state which should be fine because
|
||||
# fork carries two epochs, so even if it's a fork block, the right thing
|
||||
# will happen here
|
||||
# Careful, state no longer valid after here because of the await..
|
||||
newBlock.signature =
|
||||
await validator.signBlockProposal(fork, slot, blockRoot)
|
||||
await validator.signBlockProposal(state.fork, slot, blockRoot)
|
||||
|
||||
(blockRoot, newBlock)
|
||||
|
||||
|
@ -450,14 +444,16 @@ proc proposeBlock(node: BeaconNode,
|
|||
validator = shortLog(validator),
|
||||
cat = "consensus"
|
||||
|
||||
if node.config.dump:
|
||||
if node.config.dumpEnabled:
|
||||
SSZ.saveFile(
|
||||
node.config.dumpDir / "block-" & $newBlock.message.slot & "-" &
|
||||
shortLog(newBlockRef.root) & ".ssz", newBlock)
|
||||
SSZ.saveFile(
|
||||
node.config.dumpDir / "state-" & $tmpState.data.slot & "-" &
|
||||
shortLog(newBlockRef.root) & "-" & shortLog(tmpState.root) & ".ssz",
|
||||
tmpState.data)
|
||||
node.blockPool.withState(
|
||||
node.blockPool.tmpState, newBlockRef.atSlot(newBlockRef.slot)):
|
||||
SSZ.saveFile(
|
||||
node.config.dumpDir / "state-" & $state.slot & "-" &
|
||||
shortLog(newBlockRef.root) & "-" & shortLog(root()) & ".ssz",
|
||||
state())
|
||||
|
||||
node.network.broadcast(topicBeaconBlocks, newBlock)
|
||||
|
||||
|
@ -860,7 +856,113 @@ proc onSecond(node: BeaconNode, moment: Moment) {.async.} =
|
|||
addTimer(nextSecond) do (p: pointer):
|
||||
asyncCheck node.onSecond(nextSecond)
|
||||
|
||||
# TODO: Should we move these to other modules?
|
||||
# This would require moving around other type definitions
|
||||
proc installValidatorApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
||||
discard
|
||||
|
||||
func slotOrZero(time: BeaconTime): Slot =
|
||||
let exSlot = time.toSlot
|
||||
if exSlot.afterGenesis: exSlot.slot
|
||||
else: Slot(0)
|
||||
|
||||
proc currentSlot(node: BeaconNode): Slot =
|
||||
node.beaconClock.now.slotOrZero
|
||||
|
||||
proc connectedPeersCount(node: BeaconNode): int =
|
||||
libp2p_peers.value.int
|
||||
|
||||
proc fromJson(n: JsonNode; argName: string; result: var Slot) =
|
||||
var i: int
|
||||
fromJson(n, argName, i)
|
||||
result = Slot(i)
|
||||
|
||||
proc installBeaconApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
||||
rpcServer.rpc("getBeaconHead") do () -> Slot:
|
||||
return node.currentSlot
|
||||
|
||||
template requireOneOf(x, y: distinct Option) =
|
||||
if x.isNone xor y.isNone:
|
||||
raise newException(CatchableError,
|
||||
"Please specify one of " & astToStr(x) & " or " & astToStr(y))
|
||||
|
||||
template jsonResult(x: auto): auto =
|
||||
StringOfJson(Json.encode(x))
|
||||
|
||||
rpcServer.rpc("getBeaconBlock") do (slot: Option[Slot],
|
||||
root: Option[Eth2Digest]) -> StringOfJson:
|
||||
requireOneOf(slot, root)
|
||||
var blockHash: Eth2Digest
|
||||
if root.isSome:
|
||||
blockHash = root.get
|
||||
else:
|
||||
let foundRef = node.blockPool.getBlockByPreciseSlot(slot.get)
|
||||
if foundRef != nil:
|
||||
blockHash = foundRef.root
|
||||
else:
|
||||
return StringOfJson("null")
|
||||
|
||||
let dbBlock = node.db.getBlock(blockHash)
|
||||
if dbBlock.isSome:
|
||||
return jsonResult(dbBlock.get)
|
||||
else:
|
||||
return StringOfJson("null")
|
||||
|
||||
rpcServer.rpc("getBeaconState") do (slot: Option[Slot],
|
||||
root: Option[Eth2Digest]) -> StringOfJson:
|
||||
requireOneOf(slot, root)
|
||||
if slot.isSome:
|
||||
let blk = node.blockPool.head.blck.atSlot(slot.get)
|
||||
var tmpState: StateData
|
||||
node.blockPool.withState(tmpState, blk):
|
||||
return jsonResult(state)
|
||||
else:
|
||||
let state = node.db.getState(root.get)
|
||||
if state.isSome:
|
||||
return jsonResult(state.get)
|
||||
else:
|
||||
return StringOfJson("null")
|
||||
|
||||
rpcServer.rpc("getNetworkPeerId") do () -> string:
|
||||
when networkBackend != libp2p:
|
||||
raise newException(CatchableError, "Unsupported operation")
|
||||
else:
|
||||
return $publicKey(node.network)
|
||||
|
||||
rpcServer.rpc("getNetworkPeers") do () -> seq[string]:
|
||||
when networkBackend != libp2p:
|
||||
if true:
|
||||
raise newException(CatchableError, "Unsupported operation")
|
||||
|
||||
for peerId, peer in node.network.peerPool:
|
||||
result.add $peerId
|
||||
|
||||
rpcServer.rpc("getNetworkEnr") do () -> string:
|
||||
return $node.network.discovery.localNode.record
|
||||
|
||||
proc installDebugApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
||||
rpcServer.rpc("getSpecPreset") do () -> JsonNode:
|
||||
var res = newJObject()
|
||||
genCode:
|
||||
for setting in BeaconChainConstants:
|
||||
let
|
||||
settingSym = ident($setting)
|
||||
settingKey = newLit(toLowerAscii($setting))
|
||||
yield quote do:
|
||||
res[`settingKey`] = %`settingSym`
|
||||
|
||||
return res
|
||||
|
||||
proc installRpcHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
||||
rpcServer.installValidatorApiHandlers(node)
|
||||
rpcServer.installBeaconApiHandlers(node)
|
||||
rpcServer.installDebugApiHandlers(node)
|
||||
|
||||
proc run*(node: BeaconNode) =
|
||||
if node.rpcServer != nil:
|
||||
node.rpcServer.installRpcHandlers(node)
|
||||
node.rpcServer.start()
|
||||
|
||||
waitFor node.network.subscribe(topicBeaconBlocks) do (signedBlock: SignedBeaconBlock):
|
||||
onBeaconBlock(node, signedBlock)
|
||||
|
||||
|
@ -970,11 +1072,6 @@ when hasPrompt:
|
|||
else:
|
||||
p[].writeLine("Unknown command: " & cmd)
|
||||
|
||||
proc slotOrZero(time: BeaconTime): Slot =
|
||||
let exSlot = time.toSlot
|
||||
if exSlot.afterGenesis: exSlot.slot
|
||||
else: Slot(0)
|
||||
|
||||
proc initPrompt(node: BeaconNode) =
|
||||
if isatty(stdout) and node.config.statusBarEnabled:
|
||||
enableTrueColors()
|
||||
|
@ -997,7 +1094,7 @@ when hasPrompt:
|
|||
# arbitrary expression that is resolvable through this API.
|
||||
case expr.toLowerAscii
|
||||
of "connected_peers":
|
||||
$(libp2p_peers.value.int)
|
||||
$(node.connectedPeersCount)
|
||||
|
||||
of "last_finalized_epoch":
|
||||
var head = node.blockPool.finalizedHead
|
||||
|
@ -1014,7 +1111,7 @@ when hasPrompt:
|
|||
$SLOTS_PER_EPOCH
|
||||
|
||||
of "slot":
|
||||
$node.beaconClock.now.slotOrZero
|
||||
$node.currentSlot
|
||||
|
||||
of "slot_trailing_digits":
|
||||
var slotStr = $node.beaconClock.now.slotOrZero
|
||||
|
@ -1130,9 +1227,9 @@ when isMainModule:
|
|||
let
|
||||
networkKeys = getPersistentNetKeys(config)
|
||||
bootstrapAddress = enode.Address(
|
||||
ip: parseIpAddress(config.bootstrapAddress),
|
||||
tcpPort: Port config.bootstrapPort,
|
||||
udpPort: Port config.bootstrapPort)
|
||||
ip: config.bootstrapAddress,
|
||||
tcpPort: config.bootstrapPort,
|
||||
udpPort: config.bootstrapPort)
|
||||
|
||||
bootstrapEnr = enr.Record.init(
|
||||
1, # sequence number
|
||||
|
@ -1166,11 +1263,11 @@ when isMainModule:
|
|||
initPrompt(node)
|
||||
|
||||
when useInsecureFeatures:
|
||||
if config.metricsServer:
|
||||
let metricsAddress = config.metricsServerAddress
|
||||
if config.metricsEnabled:
|
||||
let metricsAddress = config.metricsAddress
|
||||
info "Starting metrics HTTP server",
|
||||
address = metricsAddress, port = config.metricsServerPort
|
||||
metrics.startHttpServer(metricsAddress, Port(config.metricsServerPort))
|
||||
address = metricsAddress, port = config.metricsPort
|
||||
metrics.startHttpServer($metricsAddress, config.metricsPort)
|
||||
|
||||
if node.nickname != "":
|
||||
dynamicLogScope(node = node.nickname): node.start()
|
||||
|
|
|
@ -514,6 +514,17 @@ proc getBlockRange*(pool: BlockPool, headBlock: Eth2Digest,
|
|||
trace "getBlockRange result", position = result, blockSlot = b.slot
|
||||
skip skipStep
|
||||
|
||||
func getBlockBySlot*(pool: BlockPool, slot: Slot): BlockRef =
|
||||
## Retrieves the first block in the current canonical chain
|
||||
## with slot number less or equal to `slot`.
|
||||
pool.head.blck.findAncestorBySlot(slot).blck
|
||||
|
||||
func getBlockByPreciseSlot*(pool: BlockPool, slot: Slot): BlockRef =
|
||||
## Retrieves a block from the canonical chain with a slot
|
||||
## number equal to `slot`.
|
||||
let found = pool.getBlockBySlot(slot)
|
||||
if found.slot != slot: found else: nil
|
||||
|
||||
proc get*(pool: BlockPool, blck: BlockRef): BlockData =
|
||||
## Retrieve the associated block body of a block reference
|
||||
doAssert (not blck.isNil), "Trying to get nil BlockRef"
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import
|
||||
os, options, strformat, strutils,
|
||||
chronicles, confutils, json_serialization,
|
||||
confutils/defs, chronicles/options as chroniclesOptions,
|
||||
confutils/defs, confutils/std/net,
|
||||
chronicles/options as chroniclesOptions,
|
||||
spec/[crypto]
|
||||
|
||||
export
|
||||
defs, enabledLogLevel
|
||||
|
||||
const
|
||||
DEFAULT_NETWORK* {.strdefine.} = "testnet0"
|
||||
defs, enabledLogLevel, parseCmdArg, completeCmdArg
|
||||
|
||||
type
|
||||
ValidatorKeyPath* = TypedInputFile[ValidatorPrivKey, Txt, "privkey"]
|
||||
|
@ -62,19 +60,6 @@ type
|
|||
desc: "Address of the deposit contract."
|
||||
name: "deposit-contract" }: string
|
||||
|
||||
statusBarEnabled* {.
|
||||
defaultValue: true
|
||||
desc: "Display a status bar at the bottom of the terminal screen."
|
||||
name: "status-bar" }: bool
|
||||
|
||||
statusBarContents* {.
|
||||
defaultValue: "peers: $connected_peers; " &
|
||||
"epoch: $epoch, slot: $epoch_slot/$slots_per_epoch ($slot); " &
|
||||
"finalized epoch: $last_finalized_epoch |" &
|
||||
"ETH: $attached_validators_balance"
|
||||
desc: "Textual template for the contents of the status bar."
|
||||
name: "status-bar-contents" }: string
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: StartUpCmd
|
||||
|
@ -90,15 +75,20 @@ type
|
|||
desc: "Specifies a line-delimited file of bootsrap Ethereum network addresses."
|
||||
name: "bootstrap-file" }: InputFile
|
||||
|
||||
libp2pAddress* {.
|
||||
defaultValue: defaultListenAddress(config)
|
||||
desc: "Listening address for the Ethereum LibP2P traffic."
|
||||
name: "listen-address"}: IpAddress
|
||||
|
||||
tcpPort* {.
|
||||
defaultValue: defaultPort(config)
|
||||
desc: "TCP listening port."
|
||||
name: "tcp-port" }: int
|
||||
defaultValue: defaultEth2TcpPort
|
||||
desc: "Listening TCP port for Ethereum LibP2P traffic."
|
||||
name: "tcp-port" }: Port
|
||||
|
||||
udpPort* {.
|
||||
defaultValue: defaultPort(config)
|
||||
desc: "UDP listening port."
|
||||
name: "udp-port" }: int
|
||||
defaultValue: defaultEth2TcpPort
|
||||
desc: "Listening UDP port for node discovery."
|
||||
name: "udp-port" }: Port
|
||||
|
||||
maxPeers* {.
|
||||
defaultValue: 10
|
||||
|
@ -137,25 +127,53 @@ type
|
|||
desc: "A positive epoch selects the epoch at which to stop."
|
||||
name: "stop-at-epoch" }: uint64
|
||||
|
||||
metricsServer* {.
|
||||
metricsEnabled* {.
|
||||
defaultValue: false
|
||||
desc: "Enable the metrics server."
|
||||
name: "metrics-server" }: bool
|
||||
name: "metrics" }: bool
|
||||
|
||||
metricsServerAddress* {.
|
||||
defaultValue: "0.0.0.0"
|
||||
metricsAddress* {.
|
||||
defaultValue: defaultAdminListenAddress(config)
|
||||
desc: "Listening address of the metrics server."
|
||||
name: "metrics-server-address" }: string # TODO: use a validated type here
|
||||
name: "metrics-address" }: IpAddress
|
||||
|
||||
metricsServerPort* {.
|
||||
metricsPort* {.
|
||||
defaultValue: 8008
|
||||
desc: "Listening HTTP port of the metrics server."
|
||||
name: "metrics-server-port" }: uint16
|
||||
name: "metrics-port" }: Port
|
||||
|
||||
dump* {.
|
||||
statusBarEnabled* {.
|
||||
defaultValue: true
|
||||
desc: "Display a status bar at the bottom of the terminal screen."
|
||||
name: "status-bar" }: bool
|
||||
|
||||
statusBarContents* {.
|
||||
defaultValue: "peers: $connected_peers; " &
|
||||
"epoch: $epoch, slot: $epoch_slot/$slots_per_epoch ($slot); " &
|
||||
"finalized epoch: $last_finalized_epoch |" &
|
||||
"ETH: $attached_validators_balance"
|
||||
desc: "Textual template for the contents of the status bar."
|
||||
name: "status-bar-contents" }: string
|
||||
|
||||
rpcEnabled* {.
|
||||
defaultValue: false
|
||||
desc: "Enable the JSON-RPC server"
|
||||
name: "rpc" }: bool
|
||||
|
||||
rpcPort* {.
|
||||
defaultValue: defaultEth2RpcPort
|
||||
desc: "HTTP port for the JSON-RPC service."
|
||||
name: "rpc-port" }: Port
|
||||
|
||||
rpcAddress* {.
|
||||
defaultValue: defaultAdminListenAddress(config)
|
||||
desc: "Listening address of the RPC server"
|
||||
name: "rpc-address" }: IpAddress
|
||||
|
||||
dumpEnabled* {.
|
||||
defaultValue: false
|
||||
desc: "Write SSZ dumps of blocks, attestations and states to data dir"
|
||||
.}: bool
|
||||
name: "dump" }: bool
|
||||
|
||||
of createTestnet:
|
||||
validatorsDir* {.
|
||||
|
@ -178,14 +196,14 @@ type
|
|||
name: "last-user-validator" }: uint64
|
||||
|
||||
bootstrapAddress* {.
|
||||
defaultValue: "127.0.0.1"
|
||||
defaultValue: parseIpAddress("127.0.0.1")
|
||||
desc: "The public IP address that will be advertised as a bootstrap node for the testnet."
|
||||
name: "bootstrap-address" }: string
|
||||
name: "bootstrap-address" }: IpAddress
|
||||
|
||||
bootstrapPort* {.
|
||||
defaultValue: defaultPort(config)
|
||||
defaultValue: defaultEth2TcpPort
|
||||
desc: "The TCP/UDP port that will be used by the bootstrap node."
|
||||
name: "bootstrap-port" }: int
|
||||
name: "bootstrap-port" }: Port
|
||||
|
||||
genesisOffset* {.
|
||||
defaultValue: 5
|
||||
|
@ -248,9 +266,6 @@ type
|
|||
argument
|
||||
desc: "REST API path to evaluate" }: string
|
||||
|
||||
proc defaultPort*(config: BeaconNodeConf): int =
|
||||
9000
|
||||
|
||||
proc defaultDataDir*(conf: BeaconNodeConf): string =
|
||||
let dataDir = when defined(windows):
|
||||
"AppData" / "Roaming" / "Nimbus"
|
||||
|
@ -274,6 +289,14 @@ func localValidatorsDir*(conf: BeaconNodeConf): string =
|
|||
func databaseDir*(conf: BeaconNodeConf): string =
|
||||
conf.dataDir / "db"
|
||||
|
||||
func defaultListenAddress*(conf: BeaconNodeConf): IpAddress =
|
||||
# TODO: How should we select between IPv4 and IPv6
|
||||
# Maybe there should be a config option for this.
|
||||
parseIpAddress("0.0.0.0")
|
||||
|
||||
func defaultAdminListenAddress*(conf: BeaconNodeConf): IpAddress =
|
||||
parseIpAddress("127.0.0.1")
|
||||
|
||||
iterator validatorKeys*(conf: BeaconNodeConf): ValidatorPrivKey =
|
||||
for validatorKeyFile in conf.validators:
|
||||
try:
|
||||
|
|
|
@ -25,7 +25,7 @@ proc new*(T: type Eth2DiscoveryProtocol,
|
|||
pk = initPrivateKey(rawPrivKeyBytes)
|
||||
db = DiscoveryDB.init(newMemoryDB())
|
||||
|
||||
newProtocol(pk, db, ip, Port conf.tcpPort, Port conf.udpPort)
|
||||
newProtocol(pk, db, ip, conf.tcpPort, conf.udpPort)
|
||||
|
||||
proc toENode*(a: MultiAddress): Result[ENode, cstring] =
|
||||
if not IPFS.match(a):
|
||||
|
|
|
@ -141,7 +141,7 @@ when networkBackend in [libp2p, libp2pDaemon]:
|
|||
bootstrapNodes: seq[ENode]): Future[Eth2Node] {.async.} =
|
||||
var
|
||||
(extIp, extTcpPort, _) = setupNat(conf)
|
||||
hostAddress = tcpEndPoint(globalListeningAddr, Port conf.tcpPort)
|
||||
hostAddress = tcpEndPoint(conf.libp2pAddress, conf.tcpPort)
|
||||
announcedAddresses = if extIp == globalListeningAddr: @[]
|
||||
else: @[tcpEndPoint(extIp, extTcpPort)]
|
||||
|
||||
|
|
|
@ -16,16 +16,8 @@
|
|||
# improved coverage, and to avoid unnecessary validation when replaying trusted
|
||||
# (previously validated) blocks.
|
||||
|
||||
|
||||
type
|
||||
UpdateFlag* = enum
|
||||
nextSlot ##\
|
||||
## Perform the operation as if the next slot was being processed - this is
|
||||
## useful when using the state to verify data that will go in the next slot,
|
||||
## for example when proposing
|
||||
## TODO need to be careful here, easy to assume that slot number change is
|
||||
## enough, vs advancing the state - however, making a full state copy
|
||||
## is expensive also :/
|
||||
skipMerkleValidation ##\
|
||||
## When processing deposits, skip verifying the Merkle proof trees of each
|
||||
## deposit. This is a holdover from both interop issues with the malformed
|
||||
|
|
|
@ -107,6 +107,9 @@ const
|
|||
template `$`*(peer: Peer): string = id(peer.info)
|
||||
chronicles.formatIt(Peer): $it
|
||||
|
||||
template remote*(peer: Peer): untyped =
|
||||
peer.info.peerId
|
||||
|
||||
# TODO: This exists only as a compatibility layer between the daemon
|
||||
# APIs and the native LibP2P ones. It won't be necessary once the
|
||||
# daemon is removed.
|
||||
|
@ -277,6 +280,9 @@ proc init*(T: type Eth2Node, conf: BeaconNodeConf,
|
|||
if msg.protocolMounter != nil:
|
||||
msg.protocolMounter result
|
||||
|
||||
template publicKey*(node: Eth2Node): keys.PublicKey =
|
||||
node.discovery.privKey.getPublicKey
|
||||
|
||||
template addKnownPeer*(node: Eth2Node, peer: ENode|enr.Record) =
|
||||
node.discovery.addNode peer
|
||||
|
||||
|
@ -348,6 +354,7 @@ proc p2pProtocolBackendImpl*(p: P2PProtocol): Backend =
|
|||
msgName = $msg.ident
|
||||
msgNameLit = newLit msgName
|
||||
MsgRecName = msg.recName
|
||||
MsgStrongRecName = msg.strongRecName
|
||||
codecNameLit = getRequestProtoName(msg.procDef)
|
||||
|
||||
if msg.procDef.body.kind != nnkEmpty and msg.kind == msgRequest:
|
||||
|
@ -385,7 +392,7 @@ proc p2pProtocolBackendImpl*(p: P2PProtocol): Backend =
|
|||
proc thunk(`streamVar`: `P2PStream`,
|
||||
proto: string): Future[void] {.gcsafe.} =
|
||||
return handleIncomingStream(`networkVar`, `streamVar`,
|
||||
`MsgRecName`, `Format`)
|
||||
`MsgStrongRecName`, `Format`)
|
||||
|
||||
mount `networkVar`.switch,
|
||||
LPProtocol(codec: `codecNameLit`, handler: thunk)
|
||||
|
|
|
@ -358,7 +358,7 @@ proc implementSendProcBody(sendProc: SendProc) =
|
|||
|
||||
proc handleIncomingStream(network: Eth2Node, stream: P2PStream,
|
||||
MsgType, Format: distinct type) {.async, gcsafe.} =
|
||||
mixin callUserHandler
|
||||
mixin callUserHandler, RecType
|
||||
const msgName = typetraits.name(MsgType)
|
||||
|
||||
## Uncomment this to enable tracing on all incoming requests
|
||||
|
@ -384,9 +384,10 @@ proc handleIncomingStream(network: Eth2Node, stream: P2PStream,
|
|||
await sendErrorResponse(peer, stream, ServerError, readTimeoutErrorMsg)
|
||||
return
|
||||
|
||||
var msg: MsgType
|
||||
type MsgRec = RecType(MsgType)
|
||||
var msg: MsgRec
|
||||
try:
|
||||
msg = decode(Format, msgBytes, MsgType)
|
||||
msg = decode(Format, msgBytes, MsgRec)
|
||||
except SerializationError as err:
|
||||
await sendErrorResponse(peer, stream, err, msgName, msgBytes)
|
||||
return
|
||||
|
@ -399,7 +400,7 @@ proc handleIncomingStream(network: Eth2Node, stream: P2PStream,
|
|||
raise err
|
||||
|
||||
try:
|
||||
logReceivedMsg(peer, msg)
|
||||
logReceivedMsg(peer, MsgType(msg))
|
||||
await callUserHandler(peer, stream, msg)
|
||||
except CatchableError as err:
|
||||
await sendErrorResponse(peer, stream, ServerError, err.msg)
|
||||
|
|
|
@ -101,6 +101,10 @@ template openStream(node: Eth2Node, peer: Peer, protocolId: string): untyped =
|
|||
|
||||
proc init*(T: type Peer, network: Eth2Node, id: PeerID): Peer {.gcsafe.}
|
||||
|
||||
template remote*(peer: Peer): untyped =
|
||||
# TODO: Can we get a proper address here?
|
||||
peer.id
|
||||
|
||||
proc getPeer*(node: Eth2Node, peerId: PeerID): Peer {.gcsafe.} =
|
||||
result = node.peers.getOrDefault(peerId)
|
||||
if result == nil:
|
||||
|
@ -211,6 +215,7 @@ proc p2pProtocolBackendImpl*(p: P2PProtocol): Backend =
|
|||
msgName = $msg.ident
|
||||
msgNameLit = newLit msgName
|
||||
MsgRecName = msg.recName
|
||||
MsgStrongRecName = msg.strongRecName
|
||||
|
||||
if msg.procDef.body.kind != nnkEmpty and msg.kind == msgRequest:
|
||||
# Request procs need an extra param - the stream where the response
|
||||
|
@ -235,7 +240,7 @@ proc p2pProtocolBackendImpl*(p: P2PProtocol): Backend =
|
|||
proc `thunkName`(`daemonVar`: `DaemonAPI`,
|
||||
`streamVar`: `P2PStream`): Future[void] {.gcsafe.} =
|
||||
return handleIncomingStream(`Eth2Node`(`daemonVar`.userData), `streamVar`,
|
||||
`MsgRecName`, `Format`)
|
||||
`MsgStrongRecName`, `Format`)
|
||||
else:
|
||||
thunkName = newNilLit()
|
||||
|
||||
|
|
|
@ -55,6 +55,10 @@ proc fireNotFullEvent[A, B](pool: PeerPool[A, B],
|
|||
elif item.peerType == PeerType.Outgoing:
|
||||
pool.outNotFullEvent.fire()
|
||||
|
||||
iterator pairs*[A, B](pool: PeerPool[A, B]): (B, A) =
|
||||
for peerId, peerIdx in pool.registry:
|
||||
yield (peerId, pool.storage[peerIdx.data].data)
|
||||
|
||||
proc waitNotEmptyEvent[A, B](pool: PeerPool[A, B],
|
||||
filter: set[PeerType]) {.async.} =
|
||||
if filter == {PeerType.Incoming, PeerType.Outgoing} or filter == {}:
|
||||
|
|
|
@ -444,11 +444,9 @@ proc check_attestation*(
|
|||
## at the current slot. When acting as a proposer, the same rules need to
|
||||
## be followed!
|
||||
|
||||
let stateSlot =
|
||||
if nextSlot in flags: state.slot + 1
|
||||
else: state.slot
|
||||
|
||||
let data = attestation.data
|
||||
let
|
||||
stateSlot = state.slot
|
||||
data = attestation.data
|
||||
|
||||
trace "process_attestation: beginning",
|
||||
attestation=attestation
|
||||
|
|
|
@ -202,9 +202,17 @@ proc newKeyPair*(): tuple[pub: ValidatorPubKey, priv: ValidatorPrivKey] {.noInit
|
|||
# ----------------------------------------------------------------------
|
||||
|
||||
func shortLog*(x: BlsValue): string =
|
||||
($x)[0..7]
|
||||
## Logging for wrapped BLS types
|
||||
## that may contain valid or non-validated data
|
||||
# The prefix must be short
|
||||
# due to the mechanics of the `shortLog` function.
|
||||
if x.kind == Real:
|
||||
x.blsValue.toHex()[0..7]
|
||||
else:
|
||||
"raw: " & x.blob.toHex(lowercase = true)[0..7]
|
||||
|
||||
func shortLog*(x: BlsCurveType): string =
|
||||
## Logging for raw unwrapped BLS types
|
||||
($x)[0..7]
|
||||
|
||||
proc toGaugeValue*(hash: Eth2Digest): int64 =
|
||||
|
@ -220,9 +228,9 @@ func `$`*(x: BlsValue): string =
|
|||
# The prefix must be short
|
||||
# due to the mechanics of the `shortLog` function.
|
||||
if x.kind == Real:
|
||||
"real: 0x" & x.blsValue.toHex()
|
||||
x.blsValue.toHex()
|
||||
else:
|
||||
"raw: 0x" & x.blob.toHex(lowercase = true)
|
||||
"raw: " & x.blob.toHex(lowercase = true)
|
||||
|
||||
func getBytes*(x: BlsValue): auto =
|
||||
if x.kind == Real:
|
||||
|
|
|
@ -82,7 +82,7 @@ type
|
|||
# Domains
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#domain-types
|
||||
DomainType* {.pure.} = enum
|
||||
DomainType* = enum
|
||||
DOMAIN_BEACON_PROPOSER = 0
|
||||
DOMAIN_BEACON_ATTESTER = 1
|
||||
DOMAIN_RANDAO = 2
|
||||
|
@ -510,9 +510,6 @@ proc writeValue*(writer: var JsonWriter, value: ValidatorIndex) =
|
|||
proc readValue*(reader: var JsonReader, value: var ValidatorIndex) =
|
||||
value = ValidatorIndex reader.readValue(uint32)
|
||||
|
||||
proc `%`*(i: uint64): JsonNode =
|
||||
% int(i)
|
||||
|
||||
# `ValidatorIndex` seq handling.
|
||||
proc max*(a: ValidatorIndex, b: int) : auto =
|
||||
max(a.int, b)
|
||||
|
@ -592,6 +589,7 @@ func shortLog*(e: Epoch): uint64 =
|
|||
func shortLog*(v: BeaconBlock): auto =
|
||||
(
|
||||
slot: shortLog(v.slot),
|
||||
proposer_index: v.proposer_index,
|
||||
parent_root: shortLog(v.parent_root),
|
||||
state_root: shortLog(v.state_root),
|
||||
proposer_slashings_len: v.body.proposer_slashings.len(),
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import
|
||||
options,
|
||||
../datatypes
|
||||
|
||||
# https://github.com/ethereum/eth2.0-APIs/blob/master/apis/beacon/basic.md
|
||||
#
|
||||
proc getBeaconHead(): Slot
|
||||
proc getBeaconBlock(slot = none(Slot), root = none(Eth2Digest)): BeaconBlock
|
||||
proc getBeaconState(slot = none(Slot), root = none(Eth2Digest)): BeaconState
|
||||
proc getNetworkPeerId()
|
||||
proc getNetworkPeers()
|
||||
proc getNetworkEnr()
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import
|
||||
options,
|
||||
../datatypes
|
||||
|
||||
# https://github.com/ethereum/eth2.0-APIs/tree/master/apis/validator
|
||||
|
||||
type
|
||||
SyncStatus* = object
|
||||
starting_slot*: Slot
|
||||
current_slot*: Slot
|
||||
highest_slot*: Slot
|
||||
|
||||
SyncingStatusResponse* = object
|
||||
is_syncing*: bool
|
||||
sync_status*: SyncStatus
|
||||
|
||||
ValidatorDuty* = object
|
||||
validator_pubkey: ValidatorPubKey
|
||||
attestation_slot: Slot
|
||||
attestation_shard: uint
|
||||
block_proposal_slot: Slot
|
||||
|
||||
proc getNodeVersion(): string
|
||||
proc getGenesisTime(): uint64
|
||||
proc getSyncingStatus(): SyncingStatusResponse
|
||||
proc getValidator(key: ValidatorPubKey): Validator
|
||||
proc getValidatorDuties(validators: openarray[ValidatorPubKey], epoch: Epoch): seq[ValidatorDuty]
|
||||
proc getBlockForSigning(slot: Slot, randaoReveal: string): BeaconBlock
|
||||
proc postBlock(blk: BeaconBlock)
|
||||
proc getAttestationForSigning(validatorKey: ValidatorPubKey, pocBit: int, slot: Slot, shard: uint): Attestation
|
||||
proc postAttestation(attestation: Attestation)
|
||||
|
||||
# Optional RPCs
|
||||
|
||||
proc getForkId()
|
||||
|
|
@ -36,12 +36,12 @@ func compute_epoch_at_slot*(slot: Slot|uint64): Epoch =
|
|||
template epoch*(slot: Slot): Epoch =
|
||||
compute_epoch_at_slot(slot)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch
|
||||
func compute_start_slot_at_epoch*(epoch: Epoch): Slot =
|
||||
# Return the start slot of ``epoch``.
|
||||
(epoch * SLOTS_PER_EPOCH).Slot
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#is_active_validator
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#is_active_validator
|
||||
func is_active_validator*(validator: Validator, epoch: Epoch): bool =
|
||||
### Check if ``validator`` is active
|
||||
validator.activation_epoch <= epoch and epoch < validator.exit_epoch
|
||||
|
@ -114,13 +114,38 @@ func int_to_bytes4*(x: uint64): array[4, byte] =
|
|||
result[2] = ((x shr 16) and 0xff).byte
|
||||
result[3] = ((x shr 24) and 0xff).byte
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_domain
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_fork_data_root
|
||||
func compute_fork_data_root(current_version: array[4, byte],
|
||||
genesis_validators_root: Eth2Digest): Eth2Digest =
|
||||
# Return the 32-byte fork data root for the ``current_version`` and
|
||||
# ``genesis_validators_root``.
|
||||
# This is used primarily in signature domains to avoid collisions across
|
||||
# forks/chains.
|
||||
hash_tree_root(ForkData(
|
||||
current_version: current_version,
|
||||
genesis_validators_root: genesis_validators_root
|
||||
))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_fork_digest
|
||||
func compute_fork_digest(current_version: array[4, byte],
|
||||
genesis_validators_root: Eth2Digest): array[4, byte] =
|
||||
# Return the 4-byte fork digest for the ``current_version`` and
|
||||
# ``genesis_validators_root``.
|
||||
# This is a digest primarily used for domain separation on the p2p layer.
|
||||
# 4-bytes suffices for practical separation of forks/chains.
|
||||
result[0..3] =
|
||||
compute_fork_data_root(current_version, genesis_validators_root).data[0..3]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_domain
|
||||
func compute_domain*(
|
||||
domain_type: DomainType,
|
||||
fork_version: array[4, byte] = [0'u8, 0, 0, 0]): Domain =
|
||||
fork_version: array[4, byte] = [0'u8, 0, 0, 0],
|
||||
genesis_validators_root: Eth2Digest = ZERO_HASH): Domain =
|
||||
# Return the domain for the ``domain_type`` and ``fork_version``.
|
||||
let fork_data_root =
|
||||
compute_fork_data_root(fork_version, genesis_validators_root)
|
||||
result[0..3] = int_to_bytes4(domain_type.uint64)
|
||||
result[4..7] = fork_version
|
||||
result[4..31] = fork_data_root.data[0..27]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_domain
|
||||
func get_domain*(
|
||||
|
@ -144,7 +169,7 @@ func get_domain*(
|
|||
func get_domain*(state: BeaconState, domain_type: DomainType): Domain =
|
||||
get_domain(state, domain_type, get_current_epoch(state))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_signing_root
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_signing_root
|
||||
func compute_signing_root*(ssz_object: auto, domain: Domain): Eth2Digest =
|
||||
# Return the signing root of an object by calculating the root of the
|
||||
# object-domain tree.
|
||||
|
@ -154,7 +179,7 @@ func compute_signing_root*(ssz_object: auto, domain: Domain): Eth2Digest =
|
|||
)
|
||||
hash_tree_root(domain_wrapped_object)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_seed
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_seed
|
||||
func get_seed*(state: BeaconState, epoch: Epoch, domain_type: DomainType): Eth2Digest =
|
||||
# Return the seed at ``epoch``.
|
||||
|
||||
|
|
|
@ -14,10 +14,15 @@ const
|
|||
topicProposerSlashings* = "/eth2/proposer_slashing/ssz"
|
||||
topicAttesterSlashings* = "/eth2/attester_slashing/ssz"
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#configuration
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/p2p-interface.md#configuration
|
||||
ATTESTATION_SUBNET_COUNT* = 64
|
||||
|
||||
defaultEth2TcpPort* = 9000
|
||||
|
||||
# This is not part of the spec yet!
|
||||
defaultEth2RpcPort* = 9090
|
||||
|
||||
func getAttestationTopic*(committeeIndex: uint64): string =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#broadcast-attestation
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md#broadcast-attestation
|
||||
let topicIndex = committeeIndex mod ATTESTATION_SUBNET_COUNT
|
||||
&"/eth2/index{topicIndex}{topicAttestationSuffix}"
|
||||
&"/eth2/committee_index{topicIndex}{topicAttestationSuffix}"
|
||||
|
|
|
@ -2,7 +2,7 @@ import
|
|||
macros, strutils, tables
|
||||
|
||||
type
|
||||
BeaconChainConstants* = enum
|
||||
BeaconChainConstants* {.pure.} = enum
|
||||
BASE_REWARDS_PER_EPOCH
|
||||
BASE_REWARD_FACTOR
|
||||
BLS_WITHDRAWAL_PREFIX
|
||||
|
|
|
@ -111,9 +111,9 @@ proc process_randao(
|
|||
let signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
|
||||
if skipBLSValidation notin flags:
|
||||
if not blsVerify(proposer.pubkey, signing_root.data, body.randao_reveal):
|
||||
notice "Randao mismatch", proposer_pubkey = proposer.pubkey,
|
||||
notice "Randao mismatch", proposer_pubkey = shortLog(proposer.pubkey),
|
||||
message = epoch,
|
||||
signature = body.randao_reveal,
|
||||
signature = shortLog(body.randao_reveal),
|
||||
slot = state.slot
|
||||
return false
|
||||
|
||||
|
@ -436,3 +436,87 @@ proc process_block*(
|
|||
return false
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
||||
# TODO There's more to do here - the spec has helpers that deal set up some of
|
||||
# the fields in here!
|
||||
proc makeBeaconBlock*(
|
||||
state: BeaconState,
|
||||
parent_root: Eth2Digest,
|
||||
randao_reveal: ValidatorSig,
|
||||
eth1_data: Eth1Data,
|
||||
graffiti: Eth2Digest,
|
||||
attestations: seq[Attestation],
|
||||
deposits: seq[Deposit]): Option[BeaconBlock] =
|
||||
## Create a block for the given state. The last block applied to it must be
|
||||
## the one identified by parent_root and process_slots must be called up to
|
||||
## the slot for which a block is to be created.
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let proposer_index = get_beacon_proposer_index(state, cache)
|
||||
doAssert proposer_index.isSome, "Unable to get proposer index when proposing!"
|
||||
|
||||
# To create a block, we'll first apply a partial block to the state, skipping
|
||||
# some validations.
|
||||
|
||||
var blck = BeaconBlock(
|
||||
slot: state.slot,
|
||||
proposer_index: proposer_index.get().uint64,
|
||||
parent_root: parent_root,
|
||||
body: BeaconBlockBody(
|
||||
randao_reveal: randao_reveal,
|
||||
eth1_data: eth1data,
|
||||
graffiti: graffiti,
|
||||
attestations: attestations,
|
||||
deposits: deposits)
|
||||
)
|
||||
|
||||
var tmpState = state
|
||||
let ok = process_block(tmpState, blck, {skipBlsValidation}, cache)
|
||||
|
||||
if not ok:
|
||||
warn "Unable to apply new block to state", blck = shortLog(blck)
|
||||
return
|
||||
|
||||
blck.state_root = hash_tree_root(tmpState)
|
||||
|
||||
some(blck)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
||||
func get_slot_signature*(
|
||||
fork: Fork, slot: Slot, privkey: ValidatorPrivKey): ValidatorSig =
|
||||
let
|
||||
domain =
|
||||
get_domain(fork, DOMAIN_SELECTION_PROOF, compute_epoch_at_slot(slot))
|
||||
signing_root = compute_signing_root(slot, domain)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
||||
func get_epoch_signature*(
|
||||
fork: Fork, slot: Slot, privkey: ValidatorPrivKey): ValidatorSig =
|
||||
let
|
||||
domain =
|
||||
get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
|
||||
signing_root = compute_signing_root(compute_epoch_at_slot(slot), domain)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
||||
func get_block_signature*(
|
||||
fork: Fork, slot: Slot, root: Eth2Digest, privkey: ValidatorPrivKey): ValidatorSig =
|
||||
let
|
||||
domain =
|
||||
get_domain(fork, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(slot))
|
||||
signing_root = compute_signing_root(root, domain)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
||||
func get_attestation_signature*(
|
||||
fork: Fork, attestation: AttestationData, privkey: ValidatorPrivKey): ValidatorSig =
|
||||
let
|
||||
attestationRoot = hash_tree_root(attestation)
|
||||
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER, attestation.target.epoch)
|
||||
signing_root = compute_signing_root(attestationRoot, domain)
|
||||
|
||||
blsSign(privKey, signing_root.data)
|
||||
|
|
|
@ -71,7 +71,7 @@ func get_total_active_balance*(state: BeaconState): Gwei =
|
|||
state,
|
||||
get_active_validator_indices(state, get_current_epoch(state)))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#helper-functions-1
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#helper-functions-1
|
||||
func get_matching_source_attestations(state: BeaconState, epoch: Epoch):
|
||||
seq[PendingAttestation] =
|
||||
doAssert epoch in [get_current_epoch(state), get_previous_epoch(state)]
|
||||
|
@ -233,7 +233,7 @@ proc process_justification_and_finalization*(
|
|||
checkpoint = shortLog(state.finalized_checkpoint),
|
||||
cat = "finalization"
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||
func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
||||
total_balance: auto): Gwei =
|
||||
# Spec function recalculates total_balance every time, which creates an
|
||||
|
@ -242,7 +242,7 @@ func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
|||
effective_balance * BASE_REWARD_FACTOR div
|
||||
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.2/specs/core/0_beacon-chain.md#rewards-and-penalties-1
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||
func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] {.nbench.}=
|
||||
let
|
||||
|
@ -276,9 +276,11 @@ func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
|||
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
|
||||
for index in eligible_validator_indices:
|
||||
if index in unslashed_attesting_indices:
|
||||
rewards[index] +=
|
||||
get_base_reward(state, index, total_balance) * attesting_balance div
|
||||
total_balance
|
||||
# Factored out from balance totals to avoid uint64 overflow
|
||||
const increment = EFFECTIVE_BALANCE_INCREMENT
|
||||
let reward_numerator = get_base_reward(state, index, total_balance) *
|
||||
(attesting_balance div increment)
|
||||
rewards[index] = reward_numerator div (total_balance div increment)
|
||||
else:
|
||||
penalties[index] += get_base_reward(state, index, total_balance)
|
||||
|
||||
|
@ -290,8 +292,6 @@ func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
|||
matching_source_attestations,
|
||||
get_attesting_indices(state, it.data, it.aggregation_bits, stateCache))
|
||||
|
||||
## TODO if this is still a profiling issue, do higher-level semantic
|
||||
## translation
|
||||
for index in get_unslashed_attesting_indices(
|
||||
state, matching_source_attestations, stateCache):
|
||||
# Translation of attestation = min([...])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import
|
||||
tables,
|
||||
chronos, chronicles,
|
||||
spec/[datatypes, crypto, digest, helpers], ssz,
|
||||
spec/[datatypes, crypto, digest, state_transition_block], ssz,
|
||||
beacon_node_types
|
||||
|
||||
func init*(T: type ValidatorPool): T =
|
||||
|
@ -29,18 +29,12 @@ proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
|
|||
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
|
||||
|
||||
if v.kind == inProcess:
|
||||
# TODO state might become invalid after any async calls - it's fragile to
|
||||
# care about this in here
|
||||
let
|
||||
domain =
|
||||
get_domain(fork, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(slot))
|
||||
# TODO this is an ugly hack to fake a delay and subsequent async reordering
|
||||
# for the purpose of testing the external validator delay - to be
|
||||
# replaced by something more sensible
|
||||
await sleepAsync(chronos.milliseconds(1))
|
||||
|
||||
let signing_root = compute_signing_root(blockRoot, domain)
|
||||
result = blsSign(v.privKey, signing_root.data)
|
||||
result = get_block_signature(fork, slot, blockRoot, v.privKey)
|
||||
else:
|
||||
error "Unimplemented"
|
||||
quit 1
|
||||
|
@ -49,17 +43,12 @@ proc signAttestation*(v: AttachedValidator,
|
|||
attestation: AttestationData,
|
||||
fork: Fork): Future[ValidatorSig] {.async.} =
|
||||
if v.kind == inProcess:
|
||||
let
|
||||
attestationRoot = hash_tree_root(attestation)
|
||||
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER, attestation.target.epoch)
|
||||
|
||||
# TODO this is an ugly hack to fake a delay and subsequent async reordering
|
||||
# for the purpose of testing the external validator delay - to be
|
||||
# replaced by something more sensible
|
||||
await sleepAsync(chronos.milliseconds(1))
|
||||
|
||||
let signing_root = compute_signing_root(attestationRoot, domain)
|
||||
result = blsSign(v.privKey, signing_root.data)
|
||||
result = get_attestation_signature(fork, attestation, v.privKey)
|
||||
else:
|
||||
error "Unimplemented"
|
||||
quit 1
|
||||
|
@ -67,11 +56,7 @@ proc signAttestation*(v: AttachedValidator,
|
|||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#randao-reveal
|
||||
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork, slot: Slot):
|
||||
ValidatorSig =
|
||||
let
|
||||
domain = get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
|
||||
signing_root = compute_signing_root(compute_epoch_at_slot(slot).uint64, domain)
|
||||
|
||||
bls_sign(k, signing_root.data)
|
||||
get_epoch_signature(fork, slot, k)
|
||||
|
||||
func genRandaoReveal*(v: AttachedValidator, fork: Fork, slot: Slot):
|
||||
ValidatorSig =
|
||||
|
|
|
@ -4,7 +4,7 @@ type
|
|||
libp2pDaemon
|
||||
|
||||
const
|
||||
NETWORK_TYPE {.strdefine.} = "libp2p_daemon"
|
||||
NETWORK_TYPE {.strdefine.} = "libp2p"
|
||||
|
||||
networkBackend* = when NETWORK_TYPE == "libp2p": libp2p
|
||||
elif NETWORK_TYPE == "libp2p_daemon": libp2pDaemon
|
||||
|
|
|
@ -121,8 +121,7 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
|||
|
||||
let
|
||||
attestations_idx = state.slot
|
||||
body = BeaconBlockBody(
|
||||
attestations: attestations.getOrDefault(attestations_idx))
|
||||
blockAttestations = attestations.getOrDefault(attestations_idx)
|
||||
|
||||
attestations.del attestations_idx
|
||||
doAssert len(attestations) <=
|
||||
|
@ -134,7 +133,8 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
|||
else: tBlock
|
||||
|
||||
withTimer(timers[t]):
|
||||
signedBlock = addBlock(state, latest_block_root, body, flags)
|
||||
signedBlock = addTestBlock(
|
||||
state, latest_block_root, attestations = blockAttestations, flags = flags)
|
||||
latest_block_root = withTimerRet(timers[tHashBlock]):
|
||||
hash_tree_root(signedBlock.message)
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ cli do (skipGoerliKey {.
|
|||
mode = Verbose
|
||||
execIgnoringExitCode replace(&"""{beaconNodeBinary}
|
||||
--data-dir="{dataDir}"
|
||||
--dump=true
|
||||
--dump
|
||||
--web3-url={web3Url}
|
||||
{bootstrapFileOpt}
|
||||
{logLevelOpt}
|
||||
|
|
|
@ -12,7 +12,8 @@ import
|
|||
# Standard library
|
||||
sets,
|
||||
# Specs
|
||||
../../beacon_chain/spec/[datatypes, beaconstate, helpers, validator, crypto],
|
||||
../../beacon_chain/spec/[datatypes, beaconstate, helpers, validator, crypto,
|
||||
state_transition_block],
|
||||
# Internals
|
||||
../../beacon_chain/[ssz, extras, state_transition],
|
||||
# Mocking procs
|
||||
|
@ -53,20 +54,6 @@ proc mockAttestationData(
|
|||
epoch: target_epoch, root: epoch_boundary_root
|
||||
)
|
||||
|
||||
proc get_attestation_signature(
|
||||
state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
privkey: ValidatorPrivKey
|
||||
): ValidatorSig =
|
||||
let domain = get_domain(
|
||||
state = state,
|
||||
domain_type = DOMAIN_BEACON_ATTESTER,
|
||||
message_epoch = attestation_data.target.epoch
|
||||
)
|
||||
let signing_root = compute_signing_root(attestation_data, domain)
|
||||
|
||||
return blsSign(privkey, signing_root.data)
|
||||
|
||||
proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let participants = get_attesting_indices(
|
||||
|
@ -79,7 +66,7 @@ proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
|
|||
var first_iter = true # Can't do while loop on hashset
|
||||
for validator_index in participants:
|
||||
let sig = get_attestation_signature(
|
||||
state, attestation.data, MockPrivKeys[validator_index]
|
||||
state.fork, attestation.data, MockPrivKeys[validator_index]
|
||||
)
|
||||
if first_iter:
|
||||
attestation.signature = sig
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import
|
||||
options,
|
||||
# Specs
|
||||
../../beacon_chain/spec/[datatypes, crypto, helpers, validator],
|
||||
../../beacon_chain/spec/[datatypes, crypto, validator, state_transition_block],
|
||||
# Internals
|
||||
../../beacon_chain/[ssz, extras, state_transition],
|
||||
# Mock helpers
|
||||
|
@ -27,27 +27,10 @@ proc signMockBlockImpl(
|
|||
|
||||
let privkey = MockPrivKeys[proposer_index]
|
||||
|
||||
|
||||
block:
|
||||
let domain = get_domain(
|
||||
state,
|
||||
DOMAIN_RANDAO,
|
||||
message_epoch = block_slot.compute_epoch_at_slot(),
|
||||
)
|
||||
let signing_root = compute_signing_root(
|
||||
block_slot.compute_epoch_at_slot(),
|
||||
domain
|
||||
)
|
||||
signedBlock.message.body.randao_reveal = bls_sign(privkey, signing_root.data)
|
||||
|
||||
block:
|
||||
let domain = get_domain(
|
||||
state,
|
||||
DOMAIN_BEACON_PROPOSER,
|
||||
message_epoch = block_slot.compute_epoch_at_slot(),
|
||||
)
|
||||
let signing_root = compute_signing_root(signedBlock.message, domain)
|
||||
signedBlock.signature = bls_sign(privkey, signing_root.data)
|
||||
signedBlock.message.body.randao_reveal = get_epoch_signature(
|
||||
state.fork, block_slot, privkey)
|
||||
signedBlock.signature = get_block_signature(
|
||||
state.fork, block_slot, hash_tree_root(signedBlock.message), privkey)
|
||||
|
||||
proc signMockBlock*(
|
||||
state: BeaconState,
|
||||
|
|
|
@ -64,9 +64,12 @@ cd "$DATA_DIR" && $NODE_BIN \
|
|||
--state-snapshot=$SNAPSHOT_FILE \
|
||||
$DEPOSIT_WEB3_URL_ARG \
|
||||
--deposit-contract=$DEPOSIT_CONTRACT_ADDRESS \
|
||||
--verify-finalization=on \
|
||||
--metrics-server=on \
|
||||
--metrics-server-address="127.0.0.1" \
|
||||
--metrics-server-port="$(( $BASE_METRICS_PORT + $NODE_ID ))" \
|
||||
--verify-finalization \
|
||||
--rpc \
|
||||
--rpc-address="127.0.0.1" \
|
||||
--rpc-port="$(( $BASE_RPC_PORT + $NODE_ID ))" \
|
||||
--metrics \
|
||||
--metrics-address="127.0.0.1" \
|
||||
--metrics-port="$(( $BASE_METRICS_PORT + $NODE_ID ))" \
|
||||
"$@"
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ DEPLOY_DEPOSIT_CONTRACT_BIN="${SIMULATION_DIR}/deploy_deposit_contract"
|
|||
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-${MASTER_NODE}/beacon_node.address"
|
||||
|
||||
BASE_P2P_PORT=30000
|
||||
BASE_RPC_PORT=7000
|
||||
BASE_METRICS_PORT=8008
|
||||
# Set DEPOSIT_WEB3_URL_ARG to empty to get genesis state from file, not using web3
|
||||
# DEPOSIT_WEB3_URL_ARG=--web3-url=ws://localhost:8545
|
||||
|
|
|
@ -35,17 +35,16 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
var cache = get_empty_per_epoch_cache()
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
beacon_committee = get_beacon_committee(state.data.data,
|
||||
state.data.data.slot, 0, cache)
|
||||
beacon_committee = get_beacon_committee(
|
||||
state.data.data, state.data.data.slot, 0, cache)
|
||||
attestation = makeAttestation(
|
||||
state.data.data, state.blck.root, beacon_committee[0], cache)
|
||||
|
||||
pool.add(attestation)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
|
||||
|
||||
let attestations = pool.getAttestationsForBlock(
|
||||
state.data.data, state.data.data.slot + 1)
|
||||
let attestations = pool.getAttestationsForBlock(state.data.data)
|
||||
|
||||
check:
|
||||
attestations.len == 1
|
||||
|
@ -54,8 +53,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
var cache = get_empty_per_epoch_cache()
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
bc0 = get_beacon_committee(state.data.data,
|
||||
state.data.data.slot, 0, cache)
|
||||
bc0 = get_beacon_committee(
|
||||
state.data.data, state.data.data.slot, 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, bc0[0], cache)
|
||||
|
||||
|
@ -71,10 +70,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
pool.add(attestation1)
|
||||
pool.add(attestation0)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
|
||||
|
||||
let attestations = pool.getAttestationsForBlock(
|
||||
state.data.data, state.data.data.slot + 1)
|
||||
let attestations = pool.getAttestationsForBlock(state.data.data)
|
||||
|
||||
check:
|
||||
attestations.len == 1
|
||||
|
@ -83,8 +81,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
var cache = get_empty_per_epoch_cache()
|
||||
let
|
||||
# Create an attestation for slot 1!
|
||||
bc0 = get_beacon_committee(state.data.data,
|
||||
state.data.data.slot, 0, cache)
|
||||
bc0 = get_beacon_committee(
|
||||
state.data.data, state.data.data.slot, 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, bc0[0], cache)
|
||||
attestation1 = makeAttestation(
|
||||
|
@ -93,10 +91,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
pool.add(attestation0)
|
||||
pool.add(attestation1)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
|
||||
|
||||
let attestations = pool.getAttestationsForBlock(
|
||||
state.data.data, state.data.data.slot + 1)
|
||||
let attestations = pool.getAttestationsForBlock(state.data.data)
|
||||
|
||||
check:
|
||||
attestations.len == 1
|
||||
|
@ -106,8 +103,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
|
||||
var
|
||||
# Create an attestation for slot 1!
|
||||
bc0 = get_beacon_committee(state.data.data,
|
||||
state.data.data.slot, 0, cache)
|
||||
bc0 = get_beacon_committee(
|
||||
state.data.data, state.data.data.slot, 0, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, state.blck.root, bc0[0], cache)
|
||||
attestation1 = makeAttestation(
|
||||
|
@ -118,10 +115,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
pool.add(attestation0)
|
||||
pool.add(attestation1)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
|
||||
|
||||
let attestations = pool.getAttestationsForBlock(
|
||||
state.data.data, state.data.data.slot + 1)
|
||||
let attestations = pool.getAttestationsForBlock(state.data.data)
|
||||
|
||||
check:
|
||||
attestations.len == 1
|
||||
|
@ -142,17 +138,16 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
pool.add(attestation1)
|
||||
pool.add(attestation0)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot + 1)
|
||||
|
||||
let attestations = pool.getAttestationsForBlock(
|
||||
state.data.data, state.data.data.slot + 1)
|
||||
let attestations = pool.getAttestationsForBlock(state.data.data)
|
||||
|
||||
check:
|
||||
attestations.len == 1
|
||||
|
||||
timedTest "Fork choice returns latest block with no attestations":
|
||||
let
|
||||
b1 = addBlock(state.data.data, blockPool.tail.root, BeaconBlockBody())
|
||||
b1 = addTestBlock(state.data.data, blockPool.tail.root)
|
||||
b1Root = hash_tree_root(b1.message)
|
||||
b1Add = blockPool.add(b1Root, b1)
|
||||
head = pool.selectHead()
|
||||
|
@ -161,7 +156,7 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
head == b1Add
|
||||
|
||||
let
|
||||
b2 = addBlock(state.data.data, b1Root, BeaconBlockBody())
|
||||
b2 = addTestBlock(state.data.data, b1Root)
|
||||
b2Root = hash_tree_root(b2.message)
|
||||
b2Add = blockPool.add(b2Root, b2)
|
||||
head2 = pool.selectHead()
|
||||
|
@ -172,7 +167,7 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
timedTest "Fork choice returns block with attestation":
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let
|
||||
b10 = makeBlock(state.data.data, blockPool.tail.root, BeaconBlockBody())
|
||||
b10 = makeTestBlock(state.data.data, blockPool.tail.root)
|
||||
b10Root = hash_tree_root(b10.message)
|
||||
b10Add = blockPool.add(b10Root, b10)
|
||||
head = pool.selectHead()
|
||||
|
@ -181,16 +176,15 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
head == b10Add
|
||||
|
||||
let
|
||||
b11 = makeBlock(state.data.data, blockPool.tail.root, BeaconBlockBody(
|
||||
graffiti: Eth2Digest(data: [1'u8, 0, 0, 0 ,0 ,0 ,0 ,0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
))
|
||||
b11 = makeTestBlock(state.data.data, blockPool.tail.root,
|
||||
graffiti = Eth2Digest(data: [1'u8, 0, 0, 0 ,0 ,0 ,0 ,0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
)
|
||||
b11Root = hash_tree_root(b11.message)
|
||||
b11Add = blockPool.add(b11Root, b11)
|
||||
|
||||
bc1 = get_beacon_committee(state.data.data,
|
||||
state.data.data.slot, 1, cache)
|
||||
attestation0 = makeAttestation(
|
||||
state.data.data, b10Root, bc1[0], cache)
|
||||
bc1 = get_beacon_committee(
|
||||
state.data.data, state.data.data.slot, 1, cache)
|
||||
attestation0 = makeAttestation(state.data.data, b10Root, bc1[0], cache)
|
||||
|
||||
pool.add(attestation0)
|
||||
|
||||
|
@ -201,10 +195,8 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
head2 == b10Add
|
||||
|
||||
let
|
||||
attestation1 = makeAttestation(
|
||||
state.data.data, b11Root, bc1[1], cache)
|
||||
attestation2 = makeAttestation(
|
||||
state.data.data, b11Root, bc1[2], cache)
|
||||
attestation1 = makeAttestation(state.data.data, b11Root, bc1[1], cache)
|
||||
attestation2 = makeAttestation(state.data.data, b11Root, bc1[2], cache)
|
||||
pool.add(attestation1)
|
||||
|
||||
let head3 = pool.selectHead()
|
||||
|
|
|
@ -89,9 +89,9 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
db = makeTestDB(SLOTS_PER_EPOCH)
|
||||
pool = BlockPool.init(db)
|
||||
state = pool.loadTailState().data.data
|
||||
b1 = addBlock(state, pool.tail.root, BeaconBlockBody())
|
||||
b1 = addTestBlock(state, pool.tail.root)
|
||||
b1Root = hash_tree_root(b1.message)
|
||||
b2 = addBlock(state, b1Root, BeaconBlockBody())
|
||||
b2 = addTestBlock(state, b1Root)
|
||||
b2Root {.used.} = hash_tree_root(b2.message)
|
||||
|
||||
timedTest "getRef returns nil for missing blocks":
|
||||
|
@ -246,8 +246,7 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
block:
|
||||
# Create a fork that will not be taken
|
||||
var
|
||||
blck = makeBlock(pool.headState.data.data, pool.head.blck.root,
|
||||
BeaconBlockBody())
|
||||
blck = makeTestBlock(pool.headState.data.data, pool.head.blck.root)
|
||||
discard pool.add(hash_tree_root(blck.message), blck)
|
||||
|
||||
for i in 0 ..< (SLOTS_PER_EPOCH * 6):
|
||||
|
@ -258,11 +257,11 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
|||
pool.heads.len == 2
|
||||
var
|
||||
cache = get_empty_per_epoch_cache()
|
||||
blck = makeBlock(pool.headState.data.data, pool.head.blck.root,
|
||||
BeaconBlockBody(
|
||||
attestations: makeFullAttestations(
|
||||
pool.headState.data.data, pool.head.blck.root,
|
||||
pool.headState.data.data.slot, cache, {})))
|
||||
blck = makeTestBlock(
|
||||
pool.headState.data.data, pool.head.blck.root,
|
||||
attestations = makeFullAttestations(
|
||||
pool.headState.data.data, pool.head.blck.root,
|
||||
pool.headState.data.data.slot, cache, {}))
|
||||
let added = pool.add(hash_tree_root(blck.message), blck)
|
||||
pool.updateHead(added)
|
||||
|
||||
|
|
|
@ -7,23 +7,23 @@ import
|
|||
suiteReport "Honest validator":
|
||||
timedTest "Attestation topics":
|
||||
check:
|
||||
getAttestationTopic(0) == "/eth2/index0_beacon_attestation/ssz"
|
||||
getAttestationTopic(9) == "/eth2/index9_beacon_attestation/ssz"
|
||||
getAttestationTopic(10) == "/eth2/index10_beacon_attestation/ssz"
|
||||
getAttestationTopic(11) == "/eth2/index11_beacon_attestation/ssz"
|
||||
getAttestationTopic(14) == "/eth2/index14_beacon_attestation/ssz"
|
||||
getAttestationTopic(22) == "/eth2/index22_beacon_attestation/ssz"
|
||||
getAttestationTopic(34) == "/eth2/index34_beacon_attestation/ssz"
|
||||
getAttestationTopic(46) == "/eth2/index46_beacon_attestation/ssz"
|
||||
getAttestationTopic(60) == "/eth2/index60_beacon_attestation/ssz"
|
||||
getAttestationTopic(63) == "/eth2/index63_beacon_attestation/ssz"
|
||||
getAttestationTopic(200) == "/eth2/index8_beacon_attestation/ssz"
|
||||
getAttestationTopic(400) == "/eth2/index16_beacon_attestation/ssz"
|
||||
getAttestationTopic(469) == "/eth2/index21_beacon_attestation/ssz"
|
||||
getAttestationTopic(550) == "/eth2/index38_beacon_attestation/ssz"
|
||||
getAttestationTopic(600) == "/eth2/index24_beacon_attestation/ssz"
|
||||
getAttestationTopic(613) == "/eth2/index37_beacon_attestation/ssz"
|
||||
getAttestationTopic(733) == "/eth2/index29_beacon_attestation/ssz"
|
||||
getAttestationTopic(775) == "/eth2/index7_beacon_attestation/ssz"
|
||||
getAttestationTopic(888) == "/eth2/index56_beacon_attestation/ssz"
|
||||
getAttestationTopic(995) == "/eth2/index35_beacon_attestation/ssz"
|
||||
getAttestationTopic(0) == "/eth2/committee_index0_beacon_attestation/ssz"
|
||||
getAttestationTopic(9) == "/eth2/committee_index9_beacon_attestation/ssz"
|
||||
getAttestationTopic(10) == "/eth2/committee_index10_beacon_attestation/ssz"
|
||||
getAttestationTopic(11) == "/eth2/committee_index11_beacon_attestation/ssz"
|
||||
getAttestationTopic(14) == "/eth2/committee_index14_beacon_attestation/ssz"
|
||||
getAttestationTopic(22) == "/eth2/committee_index22_beacon_attestation/ssz"
|
||||
getAttestationTopic(34) == "/eth2/committee_index34_beacon_attestation/ssz"
|
||||
getAttestationTopic(46) == "/eth2/committee_index46_beacon_attestation/ssz"
|
||||
getAttestationTopic(60) == "/eth2/committee_index60_beacon_attestation/ssz"
|
||||
getAttestationTopic(63) == "/eth2/committee_index63_beacon_attestation/ssz"
|
||||
getAttestationTopic(200) == "/eth2/committee_index8_beacon_attestation/ssz"
|
||||
getAttestationTopic(400) == "/eth2/committee_index16_beacon_attestation/ssz"
|
||||
getAttestationTopic(469) == "/eth2/committee_index21_beacon_attestation/ssz"
|
||||
getAttestationTopic(550) == "/eth2/committee_index38_beacon_attestation/ssz"
|
||||
getAttestationTopic(600) == "/eth2/committee_index24_beacon_attestation/ssz"
|
||||
getAttestationTopic(613) == "/eth2/committee_index37_beacon_attestation/ssz"
|
||||
getAttestationTopic(733) == "/eth2/committee_index29_beacon_attestation/ssz"
|
||||
getAttestationTopic(775) == "/eth2/committee_index7_beacon_attestation/ssz"
|
||||
getAttestationTopic(888) == "/eth2/committee_index56_beacon_attestation/ssz"
|
||||
getAttestationTopic(995) == "/eth2/committee_index35_beacon_attestation/ssz"
|
||||
|
|
|
@ -38,7 +38,7 @@ suiteReport "Block processing" & preset():
|
|||
var
|
||||
state = genesisState
|
||||
previous_block_root = hash_tree_root(genesisBlock.message)
|
||||
new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
||||
new_block = makeTestBlock(state, previous_block_root)
|
||||
|
||||
let block_ok = state_transition(state, new_block, {})
|
||||
|
||||
|
@ -62,7 +62,7 @@ suiteReport "Block processing" & preset():
|
|||
previous_block_root = genesisRoot
|
||||
|
||||
for i in 1..SLOTS_PER_EPOCH.int:
|
||||
var new_block = makeBlock(state, previous_block_root, BeaconBlockBody())
|
||||
let new_block = makeTestBlock(state, previous_block_root)
|
||||
|
||||
let block_ok = state_transition(state, new_block, {})
|
||||
|
||||
|
@ -95,9 +95,9 @@ suiteReport "Block processing" & preset():
|
|||
process_slots(state, GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY + 1)
|
||||
|
||||
let
|
||||
new_block = makeBlock(state, previous_block_root, BeaconBlockBody(
|
||||
attestations: @[attestation]
|
||||
))
|
||||
new_block = makeTestBlock(state, previous_block_root,
|
||||
attestations = @[attestation]
|
||||
)
|
||||
discard state_transition(state, new_block, {})
|
||||
|
||||
check:
|
||||
|
|
|
@ -11,7 +11,7 @@ import
|
|||
../beacon_chain/[beacon_chain_db, block_pool, extras, ssz, state_transition,
|
||||
validator_pool],
|
||||
../beacon_chain/spec/[beaconstate, crypto, datatypes, digest,
|
||||
helpers, validator]
|
||||
helpers, validator, state_transition_block]
|
||||
|
||||
func makeFakeValidatorPrivKey(i: int): ValidatorPrivKey =
|
||||
# 0 is not a valid BLS private key - 1000 helps interop with rust BLS library,
|
||||
|
@ -62,81 +62,70 @@ func makeInitialDeposits*(
|
|||
for i in 0..<n.int:
|
||||
result.add makeDeposit(i, flags)
|
||||
|
||||
proc addBlock*(
|
||||
state: var BeaconState, previous_block_root: Eth2Digest,
|
||||
body: BeaconBlockBody, flags: UpdateFlags = {}): SignedBeaconBlock =
|
||||
proc addTestBlock*(
|
||||
state: var BeaconState,
|
||||
parent_root: Eth2Digest,
|
||||
eth1_data = Eth1Data(),
|
||||
attestations = newSeq[Attestation](),
|
||||
deposits = newSeq[Deposit](),
|
||||
graffiti = Eth2Digest(),
|
||||
flags: set[UpdateFlag] = {}): SignedBeaconBlock =
|
||||
# Create and add a block to state - state will advance by one slot!
|
||||
# This is the equivalent of running
|
||||
# updateState(state, prev_block, makeBlock(...), {skipBlsValidation})
|
||||
# but avoids some slow block copies
|
||||
# FIXME update comment - updateState no longer exists, could be `state_transition`
|
||||
# but different number of parameters
|
||||
|
||||
state.slot += 1
|
||||
process_slots(state, state.slot + 1)
|
||||
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let proposer_index = get_beacon_proposer_index(state, cache)
|
||||
state.slot -= 1
|
||||
|
||||
let
|
||||
# Index from the new state, but registry from the old state.. hmm...
|
||||
# In tests, let this throw
|
||||
proposer = state.validators[proposer_index.get]
|
||||
privKey = hackPrivKey(proposer)
|
||||
randao_reveal =
|
||||
if skipBlsValidation notin flags:
|
||||
privKey.genRandaoReveal(state.fork, state.slot)
|
||||
else:
|
||||
ValidatorSig()
|
||||
|
||||
# TODO ugly hack; API needs rethinking
|
||||
var new_body = body
|
||||
if skipBlsValidation notin flags:
|
||||
new_body.randao_reveal = privKey.genRandaoReveal(state.fork, state.slot + 1)
|
||||
let
|
||||
message = makeBeaconBlock(
|
||||
state,
|
||||
parent_root,
|
||||
randao_reveal,
|
||||
eth1_data,
|
||||
graffiti,
|
||||
attestations,
|
||||
deposits)
|
||||
|
||||
new_body.eth1_data = Eth1Data()
|
||||
doAssert message.isSome(), "Should have created a valid block!"
|
||||
|
||||
var
|
||||
# In order to reuse the state transition function, we first create a dummy
|
||||
# block that has some fields set, and use that to generate the state as it
|
||||
# would look with the new block applied.
|
||||
new_block = SignedBeaconBlock(
|
||||
message: BeaconBlock(
|
||||
slot: state.slot + 1,
|
||||
proposer_index: proposer_index.get.uint64,
|
||||
parent_root: previous_block_root,
|
||||
state_root: Eth2Digest(), # we need the new state first
|
||||
body: new_body
|
||||
)
|
||||
message: message.get()
|
||||
)
|
||||
|
||||
let block_ok = state_transition(state, new_block, {skipBlsValidation, skipStateRootValidation})
|
||||
doAssert block_ok
|
||||
|
||||
# Ok, we have the new state as it would look with the block applied - now we
|
||||
# can set the state root in order to be able to create a valid signature
|
||||
new_block.message.state_root = hash_tree_root(state)
|
||||
|
||||
doAssert privKey.pubKey() == proposer.pubkey,
|
||||
"signature key should be derived from private key! - wrong privkey?"
|
||||
|
||||
if skipBLSValidation notin flags:
|
||||
let domain = get_domain(state, DOMAIN_BEACON_PROPOSER,
|
||||
compute_epoch_at_slot(new_block.message.slot))
|
||||
let signing_root = compute_signing_root(new_block.message, domain)
|
||||
# We have a signature - put it in the block and we should be done!
|
||||
new_block.signature = bls_sign(privKey, signing_root.data)
|
||||
|
||||
doAssert bls_verify(
|
||||
proposer.pubkey,
|
||||
signing_root.data, new_block.signature),
|
||||
"we just signed this message - it should pass verification!"
|
||||
let ok = process_block(state, new_block.message, flags, cache)
|
||||
|
||||
doAssert ok, "adding block after producing it should work"
|
||||
new_block
|
||||
|
||||
proc makeBlock*(
|
||||
state: BeaconState, previous_block_root: Eth2Digest,
|
||||
body: BeaconBlockBody): SignedBeaconBlock =
|
||||
proc makeTestBlock*(
|
||||
state: BeaconState,
|
||||
parent_root: Eth2Digest,
|
||||
eth1_data = Eth1Data(),
|
||||
attestations = newSeq[Attestation](),
|
||||
deposits = newSeq[Deposit](),
|
||||
graffiti = Eth2Digest(),
|
||||
flags: set[UpdateFlag] = {}): SignedBeaconBlock =
|
||||
# Create a block for `state.slot + 1` - like a block proposer would do!
|
||||
# It's a bit awkward - in order to produce a block for N+1, we need to
|
||||
# calculate what the state will look like after that block has been applied,
|
||||
# because the block includes the state root.
|
||||
var next_state = state
|
||||
addBlock(next_state, previous_block_root, body)
|
||||
var tmpState = state
|
||||
addTestBlock(
|
||||
tmpState, parent_root, eth1_data, attestations, deposits, graffiti, flags)
|
||||
|
||||
proc makeAttestation*(
|
||||
state: BeaconState, beacon_block_root: Eth2Digest,
|
||||
|
@ -158,11 +147,9 @@ proc makeAttestation*(
|
|||
aggregation_bits.setBit sac_index
|
||||
|
||||
let
|
||||
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
|
||||
signing_root = compute_signing_root(data, domain)
|
||||
sig =
|
||||
if skipBLSValidation notin flags:
|
||||
bls_sign(hackPrivKey(validator), signing_root.data)
|
||||
get_attestation_signature(state.fork, data, hackPrivKey(validator))
|
||||
else:
|
||||
ValidatorSig()
|
||||
|
||||
|
@ -209,27 +196,24 @@ proc makeFullAttestations*(
|
|||
let
|
||||
committee = get_beacon_committee(state, slot, index, cache)
|
||||
data = makeAttestationData(state, slot, index, beacon_block_root)
|
||||
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, data.target.epoch)
|
||||
signing_root = compute_signing_root(data, domain)
|
||||
|
||||
doAssert committee.len() >= 1
|
||||
# Initial attestation
|
||||
var attestation = Attestation(
|
||||
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
|
||||
data: data,
|
||||
signature: bls_sign(
|
||||
hackPrivKey(state.validators[committee[0]]),
|
||||
signing_root.data
|
||||
)
|
||||
signature: get_attestation_signature(
|
||||
state.fork, data,
|
||||
hackPrivKey(state.validators[committee[0]]))
|
||||
)
|
||||
# Aggregate the remainder
|
||||
attestation.aggregation_bits.setBit 0
|
||||
for j in 1 ..< committee.len():
|
||||
attestation.aggregation_bits.setBit j
|
||||
if skipBLSValidation notin flags:
|
||||
attestation.signature.aggregate(bls_sign(
|
||||
hackPrivKey(state.validators[committee[j]]),
|
||||
signing_root.data
|
||||
attestation.signature.aggregate(get_attestation_signature(
|
||||
state.fork, data,
|
||||
hackPrivKey(state.validators[committee[j]])
|
||||
))
|
||||
|
||||
result.add attestation
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit e4de3a90019a5f621c265729ac9071769a061a79
|
||||
Subproject commit 8da0e30c526ab1c6c825e16546fc5db972c5408d
|
|
@ -1 +1 @@
|
|||
Subproject commit b3b5854003ebb10b99efa451d4391ef050f5951e
|
||||
Subproject commit 9c442bf65b52a4c857cc6e51efe901352e8b6ebf
|
|
@ -1 +1 @@
|
|||
Subproject commit 4d1d2577e9a6634f567899b927b6dc9021624588
|
||||
Subproject commit 6fbaeb61cab889f74870372bc00cc99371e16794
|
|
@ -1 +1 @@
|
|||
Subproject commit 88b79e230005d8301c3ae950abdbf8ad55e37f19
|
||||
Subproject commit 6350b72b5eda69f7ccfa57a94fd420509dbf6f49
|
|
@ -1 +1 @@
|
|||
Subproject commit b1a34f478efe6d1212cfccecaf4dd8bf89fc2b1f
|
||||
Subproject commit 0a3e4a764b718d13fc330f228fce60ed265cfde2
|
Loading…
Reference in New Issue