REST cleanups (#3255)
* REST cleanups * reject out-of-range committee requests * print all hex values as lower-case * allow requesting state information by head state root * turn `DomainType` into array (follow spec) * `uint_to_bytesXX` -> `uint_to_bytes` (follow spec) * fix wrong dependent root in `/eth/v1/validator/duties/proposer/` * update documentation - `--subscribe-all-subnets` is no longer needed when using the REST interface with validator clients * more fixes * common helpers for dependent block * remove test rules obsoleted by more strict epoch tests * fix trailing commas * Update docs/the_nimbus_book/src/rest-api.md * Update docs/the_nimbus_book/src/rest-api.md Co-authored-by: sacha <sacha@status.im>
This commit is contained in:
parent
25bb927e62
commit
6f7e0e3393
|
@ -57,7 +57,6 @@ type
|
|||
## Slot time for this BlockSlot which may differ from blck.slot when time
|
||||
## has advanced without blocks
|
||||
|
||||
|
||||
template root*(blck: BlockRef): Eth2Digest = blck.bid.root
|
||||
template slot*(blck: BlockRef): Slot = blck.bid.slot
|
||||
|
||||
|
@ -213,6 +212,20 @@ func isProposed*(bsi: BlockSlotId): bool =
|
|||
## slot)
|
||||
bsi.bid.isProposed(bsi.slot)
|
||||
|
||||
func dependentBlock*(head, tail: BlockRef, epoch: Epoch): BlockRef =
|
||||
## The block that determined the proposer shuffling in the given epoch
|
||||
let dependentSlot =
|
||||
if epoch >= Epoch(1): epoch.compute_start_slot_at_epoch() - 1
|
||||
else: Slot(0)
|
||||
let res = head.atSlot(dependentSlot)
|
||||
if isNil(res.blck): tail
|
||||
else: res.blck
|
||||
|
||||
func prevDependentBlock*(head, tail: BlockRef, epoch: Epoch): BlockRef =
|
||||
## The block that determined the attester shuffling in the given epoch
|
||||
if epoch >= 1: head.dependentBlock(tail, epoch - 1)
|
||||
else: head.dependentBlock(tail, epoch)
|
||||
|
||||
func shortLog*(v: BlockId): string =
|
||||
# epoch:root when logging epoch, root:slot when logging slot!
|
||||
shortLog(v.root) & ":" & $v.slot
|
||||
|
|
|
@ -11,7 +11,7 @@ import
|
|||
# Standard library
|
||||
std/[options, sets, tables, hashes],
|
||||
# Status libraries
|
||||
stew/endians2, chronicles,
|
||||
chronicles,
|
||||
# Internals
|
||||
../spec/[signatures_batch, forks, helpers],
|
||||
../spec/datatypes/[phase0, altair, bellatrix],
|
||||
|
|
|
@ -1274,9 +1274,9 @@ proc pruneStateCachesDAG*(dag: ChainDAGRef) =
|
|||
epochRefPruneDur = epochRefPruneTick - statePruneTick
|
||||
|
||||
proc updateHead*(
|
||||
dag: ChainDAGRef,
|
||||
newHead: BlockRef,
|
||||
quarantine: var Quarantine) =
|
||||
dag: ChainDAGRef,
|
||||
newHead: BlockRef,
|
||||
quarantine: var Quarantine) =
|
||||
## Update what we consider to be the current head, as given by the fork
|
||||
## choice.
|
||||
##
|
||||
|
@ -1362,25 +1362,15 @@ proc updateHead*(
|
|||
dag.headState.data, finalized_checkpoint))
|
||||
|
||||
if not(isNil(dag.onHeadChanged)):
|
||||
let currentEpoch = epoch(newHead.slot)
|
||||
let
|
||||
currentDutyDepRoot =
|
||||
if currentEpoch > dag.tail.slot.epoch:
|
||||
dag.head.atSlot(
|
||||
compute_start_slot_at_epoch(currentEpoch) - 1).blck.root
|
||||
else:
|
||||
dag.tail.root
|
||||
previousDutyDepRoot =
|
||||
if currentEpoch > dag.tail.slot.epoch + 1:
|
||||
dag.head.atSlot(
|
||||
compute_start_slot_at_epoch(currentEpoch - 1) - 1).blck.root
|
||||
else:
|
||||
dag.tail.root
|
||||
currentEpoch = epoch(newHead.slot)
|
||||
depBlock = dag.head.dependentBlock(dag.tail, currentEpoch)
|
||||
prevDepBlock = dag.head.prevDependentBlock(dag.tail, currentEpoch)
|
||||
epochTransition = (finalizedHead != dag.finalizedHead)
|
||||
let data = HeadChangeInfoObject.init(dag.head.slot, dag.head.root,
|
||||
getStateRoot(dag.headState.data),
|
||||
epochTransition, previousDutyDepRoot,
|
||||
currentDutyDepRoot)
|
||||
epochTransition, depBlock.root,
|
||||
prevDepBlock.root)
|
||||
dag.onHeadChanged(data)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#additional-metrics
|
||||
|
|
|
@ -9,7 +9,6 @@ import
|
|||
std/[typetraits, sequtils, strutils, sets],
|
||||
stew/[results, base10],
|
||||
chronicles,
|
||||
nimcrypto/utils as ncrutils,
|
||||
./rest_utils,
|
||||
../beacon_node, ../networking/eth2_network,
|
||||
../consensus_object_pools/[blockchain_dag, exit_pool, spec_cache],
|
||||
|
@ -121,14 +120,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -144,14 +142,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -177,14 +174,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -209,14 +205,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -338,14 +333,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -407,14 +401,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -501,14 +494,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
@ -519,8 +511,18 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
|
||||
$repoch.error())
|
||||
let res = repoch.get()
|
||||
if res > MaxEpoch:
|
||||
return RestApiResponse.jsonError(Http400, EpochOverflowValueError)
|
||||
|
||||
if res > bslot.slot.epoch + MIN_SEED_LOOKAHEAD:
|
||||
return RestApiResponse.jsonError(
|
||||
Http400, InvalidEpochValueError,
|
||||
"Requested epoch more than 1 epoch past state epoch")
|
||||
|
||||
if res + EPOCHS_PER_HISTORICAL_VECTOR <
|
||||
bslot.slot.epoch + MIN_SEED_LOOKAHEAD:
|
||||
return RestApiResponse.jsonError(
|
||||
Http400, InvalidEpochValueError,
|
||||
"Requested epoch earlier than what committees can be computed for")
|
||||
|
||||
some(res)
|
||||
else:
|
||||
none[Epoch]()
|
||||
|
@ -540,7 +542,24 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if rslot.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
|
||||
$rslot.error())
|
||||
some(rslot.get())
|
||||
let res = rslot.get()
|
||||
if vepoch.isSome():
|
||||
if res.epoch != vepoch.get():
|
||||
return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
|
||||
"Slot does not match requested epoch")
|
||||
else:
|
||||
if res.epoch > bslot.slot.epoch + 1:
|
||||
return RestApiResponse.jsonError(
|
||||
Http400, InvalidEpochValueError,
|
||||
"Requested slot more than 1 epoch past state epoch")
|
||||
|
||||
if res.epoch + EPOCHS_PER_HISTORICAL_VECTOR <
|
||||
bslot.slot.epoch + MIN_SEED_LOOKAHEAD:
|
||||
return RestApiResponse.jsonError(
|
||||
Http400, InvalidEpochValueError,
|
||||
"Requested slot earlier than what committees can be computed for")
|
||||
|
||||
some(res)
|
||||
else:
|
||||
none[Slot]()
|
||||
node.withStateForBlockSlot(bslot):
|
||||
|
@ -590,13 +609,15 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if state_id.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
|
||||
$state_id.error())
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
let sid = state_id.get()
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
|
||||
let bres = node.getBlockSlot(sid)
|
||||
if bres.isErr():
|
||||
if sid.kind == StateQueryKind.Root:
|
||||
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
|
||||
# in current version of database.
|
||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||
$bres.error())
|
||||
bres.get()
|
||||
|
||||
let qepoch =
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import stew/[endians2, base10], chronicles,
|
||||
nimcrypto/utils as ncrutils
|
||||
import stew/[byteutils, base10], chronicles
|
||||
import ".."/beacon_node,
|
||||
".."/eth1/eth1_monitor,
|
||||
".."/spec/forks,
|
||||
|
@ -170,38 +169,31 @@ proc installConfigApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
# JUSTIFICATION_BITS_LENGTH
|
||||
# ENDIANNESS
|
||||
BLS_WITHDRAWAL_PREFIX:
|
||||
"0x" & ncrutils.toHex([BLS_WITHDRAWAL_PREFIX]),
|
||||
to0xHex([BLS_WITHDRAWAL_PREFIX]),
|
||||
ETH1_ADDRESS_WITHDRAWAL_PREFIX:
|
||||
"0x" & ncrutils.toHex([ETH1_ADDRESS_WITHDRAWAL_PREFIX]),
|
||||
to0xHex([ETH1_ADDRESS_WITHDRAWAL_PREFIX]),
|
||||
DOMAIN_BEACON_PROPOSER:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_BEACON_PROPOSER).toBytesLE()),
|
||||
to0xHex(DOMAIN_BEACON_PROPOSER.data),
|
||||
DOMAIN_BEACON_ATTESTER:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_BEACON_ATTESTER).toBytesLE()),
|
||||
to0xHex(DOMAIN_BEACON_ATTESTER.data),
|
||||
DOMAIN_RANDAO:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_RANDAO).toBytesLE()),
|
||||
to0xHex(DOMAIN_RANDAO.data),
|
||||
DOMAIN_DEPOSIT:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_DEPOSIT).toBytesLE()),
|
||||
to0xHex(DOMAIN_DEPOSIT.data),
|
||||
DOMAIN_VOLUNTARY_EXIT:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_VOLUNTARY_EXIT).toBytesLE()),
|
||||
to0xHex(DOMAIN_VOLUNTARY_EXIT.data),
|
||||
DOMAIN_SELECTION_PROOF:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_SELECTION_PROOF).toBytesLE()),
|
||||
to0xHex(DOMAIN_SELECTION_PROOF.data),
|
||||
DOMAIN_AGGREGATE_AND_PROOF:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_AGGREGATE_AND_PROOF).toBytesLE()),
|
||||
to0xHex(DOMAIN_AGGREGATE_AND_PROOF.data),
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/beacon-chain.md#constants
|
||||
TIMELY_SOURCE_FLAG_INDEX:
|
||||
"0x" & ncrutils.toHex([byte(TIMELY_SOURCE_FLAG_INDEX)]),
|
||||
to0xHex([byte(TIMELY_SOURCE_FLAG_INDEX)]),
|
||||
TIMELY_TARGET_FLAG_INDEX:
|
||||
"0x" & ncrutils.toHex([byte(TIMELY_TARGET_FLAG_INDEX)]),
|
||||
to0xHex([byte(TIMELY_TARGET_FLAG_INDEX)]),
|
||||
TIMELY_HEAD_FLAG_INDEX:
|
||||
"0x" & ncrutils.toHex([byte(TIMELY_HEAD_FLAG_INDEX)]),
|
||||
to0xHex([byte(TIMELY_HEAD_FLAG_INDEX)]),
|
||||
TIMELY_SOURCE_WEIGHT:
|
||||
Base10.toString(uint64(TIMELY_SOURCE_WEIGHT)),
|
||||
TIMELY_TARGET_WEIGHT:
|
||||
|
@ -215,14 +207,11 @@ proc installConfigApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
WEIGHT_DENOMINATOR:
|
||||
Base10.toString(uint64(WEIGHT_DENOMINATOR)),
|
||||
DOMAIN_SYNC_COMMITTEE:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_SYNC_COMMITTEE).toBytesLE()),
|
||||
to0xHex(DOMAIN_SYNC_COMMITTEE.data),
|
||||
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF).toBytesLE()),
|
||||
to0xHex(DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF.data),
|
||||
DOMAIN_CONTRIBUTION_AND_PROOF:
|
||||
"0x" & ncrutils.toHex(
|
||||
uint32(DOMAIN_CONTRIBUTION_AND_PROOF).toBytesLE()),
|
||||
to0xHex(DOMAIN_CONTRIBUTION_AND_PROOF.data),
|
||||
# PARTICIPATION_FLAG_WEIGHTS
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/validator.md#constants
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import
|
||||
std/[sequtils],
|
||||
stew/results,
|
||||
stew/[byteutils, results],
|
||||
chronicles,
|
||||
eth/p2p/discoveryv5/enr,
|
||||
libp2p/[multiaddress, multicodec, peerstore],
|
||||
nimcrypto/utils as ncrutils,
|
||||
../version, ../beacon_node, ../sync/sync_manager,
|
||||
../networking/[eth2_network, peer_pool],
|
||||
../spec/datatypes/base,
|
||||
|
@ -158,8 +157,8 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
discovery_addresses: discoveryAddresses,
|
||||
metadata: (
|
||||
seq_number: node.network.metadata.seq_number,
|
||||
syncnets: "0x" & ncrutils.toHex(node.network.metadata.syncnets.bytes),
|
||||
attnets: "0x" & ncrutils.toHex(node.network.metadata.attnets.bytes)
|
||||
syncnets: to0xHex(node.network.metadata.syncnets.bytes),
|
||||
attnets: to0xHex(node.network.metadata.attnets.bytes)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import std/[options, macros],
|
||||
stew/byteutils, presto,
|
||||
nimcrypto/utils as ncrutils,
|
||||
../spec/[forks],
|
||||
../spec/eth2_apis/[rest_types, eth2_rest_serialization],
|
||||
../beacon_node,
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
import std/[typetraits, strutils, sets]
|
||||
import stew/[results, base10], chronicles,
|
||||
nimcrypto/utils as ncrutils
|
||||
import stew/[results, base10], chronicles
|
||||
import ".."/[beacon_chain_db, beacon_node],
|
||||
".."/networking/eth2_network,
|
||||
".."/consensus_object_pools/[blockchain_dag, spec_cache,
|
||||
|
@ -54,9 +53,13 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if epoch.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
|
||||
$epoch.error())
|
||||
let res = epoch.get()
|
||||
if res > MaxEpoch:
|
||||
return RestApiResponse.jsonError(Http400, EpochOverflowValueError)
|
||||
let
|
||||
res = epoch.get()
|
||||
wallTime = node.beaconClock.now() + MAXIMUM_GOSSIP_CLOCK_DISPARITY
|
||||
wallEpoch = wallTime.slotOrZero().epoch
|
||||
if res > wallEpoch + 1:
|
||||
return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
|
||||
"Cannot request duties past next epoch")
|
||||
res
|
||||
let qhead =
|
||||
block:
|
||||
|
@ -64,11 +67,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if res.isErr():
|
||||
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
|
||||
res.get()
|
||||
let droot =
|
||||
if qepoch >= Epoch(2):
|
||||
qhead.atSlot(compute_start_slot_at_epoch(qepoch - 1) - 1).blck.root
|
||||
else:
|
||||
node.dag.genesis.root
|
||||
let droot = qhead.prevDependentBlock(node.dag.tail, qepoch).root
|
||||
let duties =
|
||||
block:
|
||||
var res: seq[RestAttesterDuty]
|
||||
|
@ -112,9 +111,13 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if epoch.isErr():
|
||||
return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
|
||||
$epoch.error())
|
||||
let res = epoch.get()
|
||||
if res > MaxEpoch:
|
||||
return RestApiResponse.jsonError(Http400, EpochOverflowValueError)
|
||||
let
|
||||
res = epoch.get()
|
||||
wallTime = node.beaconClock.now() + MAXIMUM_GOSSIP_CLOCK_DISPARITY
|
||||
wallEpoch = wallTime.slotOrZero().epoch
|
||||
if res > wallEpoch + 1:
|
||||
return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
|
||||
"Cannot request duties past next epoch")
|
||||
res
|
||||
let qhead =
|
||||
block:
|
||||
|
@ -122,11 +125,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
if res.isErr():
|
||||
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
|
||||
res.get()
|
||||
let droot =
|
||||
if qepoch >= Epoch(2):
|
||||
qhead.atSlot(compute_start_slot_at_epoch(qepoch - 1) - 1).blck.root
|
||||
else:
|
||||
node.dag.genesis.root
|
||||
let droot = qhead.dependentBlock(node.dag.tail, qepoch).root
|
||||
let duties =
|
||||
block:
|
||||
var res: seq[RestProposerDuty]
|
||||
|
@ -504,8 +503,6 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
router.api(MethodPost,
|
||||
"/eth/v1/validator/beacon_committee_subscriptions") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
# TODO (cheatfate): This call could not be finished because more complex
|
||||
# peer manager implementation needed.
|
||||
let requests =
|
||||
block:
|
||||
if contentBody.isNone():
|
||||
|
@ -605,7 +602,8 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
subs
|
||||
|
||||
warn "Sync committee subscription request served, but not implemented"
|
||||
# TODO because we subscribe to all sync committee subnets, we don not need
|
||||
# to remember which ones are requested by validator clients
|
||||
return RestApiResponse.jsonMsgResponse(SyncCommitteeSubscriptionSuccess)
|
||||
|
||||
# https://ethereum.github.io/beacon-APIs/#/Validator/produceSyncCommitteeContribution
|
||||
|
|
|
@ -9,10 +9,9 @@
|
|||
|
||||
import
|
||||
std/[parseutils, sequtils, strutils, sets],
|
||||
stew/results,
|
||||
stew/[byteutils, results],
|
||||
json_rpc/servers/httpserver,
|
||||
chronicles,
|
||||
nimcrypto/utils as ncrutils,
|
||||
../beacon_node,
|
||||
../networking/eth2_network,
|
||||
../validators/validator_duties,
|
||||
|
@ -452,7 +451,7 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
|
|||
|
||||
for item in node.attestationPool[].attestations(qslot, qindex):
|
||||
let atuple = (
|
||||
aggregation_bits: "0x" & ncrutils.toHex(item.aggregation_bits.bytes),
|
||||
aggregation_bits: to0xHex(item.aggregation_bits.bytes),
|
||||
data: item.data,
|
||||
signature: item.signature
|
||||
)
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
stew/endians2,
|
||||
stew/[byteutils],
|
||||
json_rpc/servers/httpserver,
|
||||
chronicles,
|
||||
nimcrypto/utils as ncrutils,
|
||||
../beacon_node,
|
||||
../eth1/eth1_monitor,
|
||||
../spec/forks
|
||||
|
@ -59,7 +58,7 @@ proc installConfigApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
|
|||
"EFFECTIVE_BALANCE_INCREMENT": $EFFECTIVE_BALANCE_INCREMENT,
|
||||
"GENESIS_FORK_VERSION":
|
||||
"0x" & $node.dag.cfg.GENESIS_FORK_VERSION,
|
||||
"BLS_WITHDRAWAL_PREFIX": "0x" & ncrutils.toHex([BLS_WITHDRAWAL_PREFIX]),
|
||||
"BLS_WITHDRAWAL_PREFIX": to0xHex([BLS_WITHDRAWAL_PREFIX]),
|
||||
"GENESIS_DELAY": $node.dag.cfg.GENESIS_DELAY,
|
||||
"SECONDS_PER_SLOT": $SECONDS_PER_SLOT,
|
||||
"MIN_ATTESTATION_INCLUSION_DELAY": $MIN_ATTESTATION_INCLUSION_DELAY,
|
||||
|
@ -88,19 +87,19 @@ proc installConfigApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
|
|||
"MAX_DEPOSITS": $MAX_DEPOSITS,
|
||||
"MAX_VOLUNTARY_EXITS": $MAX_VOLUNTARY_EXITS,
|
||||
"DOMAIN_BEACON_PROPOSER":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_BEACON_PROPOSER).toBytesLE()),
|
||||
to0xHex(DOMAIN_BEACON_PROPOSER.data),
|
||||
"DOMAIN_BEACON_ATTESTER":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_BEACON_ATTESTER).toBytesLE()),
|
||||
to0xHex(DOMAIN_BEACON_ATTESTER.data),
|
||||
"DOMAIN_RANDAO":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_RANDAO).toBytesLE()),
|
||||
to0xHex(DOMAIN_RANDAO.data),
|
||||
"DOMAIN_DEPOSIT":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_DEPOSIT).toBytesLE()),
|
||||
to0xHex(DOMAIN_DEPOSIT.data),
|
||||
"DOMAIN_VOLUNTARY_EXIT":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_VOLUNTARY_EXIT).toBytesLE()),
|
||||
to0xHex(DOMAIN_VOLUNTARY_EXIT.data),
|
||||
"DOMAIN_SELECTION_PROOF":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_SELECTION_PROOF).toBytesLE()),
|
||||
to0xHex(DOMAIN_SELECTION_PROOF.data),
|
||||
"DOMAIN_AGGREGATE_AND_PROOF":
|
||||
"0x" & ncrutils.toHex(uint32(DOMAIN_AGGREGATE_AND_PROOF).toBytesLE())
|
||||
to0xHex(DOMAIN_AGGREGATE_AND_PROOF.data)
|
||||
}
|
||||
|
||||
rpcServer.rpc("get_v1_config_deposit_contract") do () -> JsonNode:
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import std/[options, sequtils],
|
||||
import
|
||||
std/[options, sequtils],
|
||||
stew/byteutils,
|
||||
chronicles,
|
||||
json_rpc/servers/httpserver,
|
||||
eth/p2p/discoveryv5/enr,
|
||||
libp2p/[multiaddress, multicodec, peerstore],
|
||||
nimcrypto/utils as ncrutils,
|
||||
../beacon_node, ../version,
|
||||
../networking/[eth2_network, peer_pool],
|
||||
../sync/sync_manager,
|
||||
|
@ -174,7 +175,7 @@ proc installNodeApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
|
|||
p2p_addresses: p2pAddresses,
|
||||
discovery_addresses: discoveryAddresses,
|
||||
metadata: (node.network.metadata.seq_number,
|
||||
"0x" & ncrutils.toHex(node.network.metadata.attnets.bytes))
|
||||
to0xHex(node.network.metadata.attnets.bytes))
|
||||
)
|
||||
|
||||
rpcServer.rpc("get_v1_node_peers") do (state: Option[seq[string]],
|
||||
|
|
|
@ -724,7 +724,7 @@ func get_next_sync_committee_keys(state: altair.BeaconState | bellatrix.BeaconSt
|
|||
hash_buffer: array[40, byte]
|
||||
hash_buffer[0..31] = seed.data
|
||||
while index < SYNC_COMMITTEE_SIZE:
|
||||
hash_buffer[32..39] = uint_to_bytes8(uint64(i div 32))
|
||||
hash_buffer[32..39] = uint_to_bytes(uint64(i div 32))
|
||||
let
|
||||
shuffled_index = compute_shuffled_index(uint64(i mod active_validator_count), active_validator_count, seed)
|
||||
candidate_index = active_validator_indices[shuffled_index]
|
||||
|
|
|
@ -30,8 +30,9 @@ import
|
|||
stew/[endians2, objects, results, byteutils],
|
||||
blscurve,
|
||||
chronicles,
|
||||
json_serialization,
|
||||
nimcrypto/utils as ncrutils
|
||||
json_serialization
|
||||
|
||||
from nimcrypto/utils import burnMem
|
||||
|
||||
export options, results, json_serialization, blscurve
|
||||
|
||||
|
@ -474,4 +475,4 @@ func infinity*(T: type ValidatorSig): T =
|
|||
result.blob[0] = byte 0xC0
|
||||
|
||||
proc burnMem*(key: var ValidatorPrivKey) =
|
||||
ncrutils.burnMem(addr key, sizeof(ValidatorPrivKey))
|
||||
burnMem(addr key, sizeof(ValidatorPrivKey))
|
||||
|
|
|
@ -120,20 +120,7 @@ template maxSize*(n: int) {.pragma.}
|
|||
type
|
||||
# Domains
|
||||
# ---------------------------------------------------------------
|
||||
DomainType* = enum
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#domain-types
|
||||
DOMAIN_BEACON_PROPOSER = 0
|
||||
DOMAIN_BEACON_ATTESTER = 1
|
||||
DOMAIN_RANDAO = 2
|
||||
DOMAIN_DEPOSIT = 3
|
||||
DOMAIN_VOLUNTARY_EXIT = 4
|
||||
DOMAIN_SELECTION_PROOF = 5
|
||||
DOMAIN_AGGREGATE_AND_PROOF = 6
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/beacon-chain.md#domain-types
|
||||
DOMAIN_SYNC_COMMITTEE = 7
|
||||
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF = 8
|
||||
DOMAIN_CONTRIBUTION_AND_PROOF = 9
|
||||
DomainType* = distinct array[4, byte]
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#custom-types
|
||||
Eth2Domain* = array[32, byte]
|
||||
|
@ -526,6 +513,21 @@ type
|
|||
# time of attestation.
|
||||
previous_epoch_head_attesters_raw*: Gwei
|
||||
|
||||
const
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#domain-types
|
||||
DOMAIN_BEACON_PROPOSER* = DomainType([byte 0x00, 0x00, 0x00, 0x00])
|
||||
DOMAIN_BEACON_ATTESTER* = DomainType([byte 0x01, 0x00, 0x00, 0x00])
|
||||
DOMAIN_RANDAO* = DomainType([byte 0x02, 0x00, 0x00, 0x00])
|
||||
DOMAIN_DEPOSIT* = DomainType([byte 0x03, 0x00, 0x00, 0x00])
|
||||
DOMAIN_VOLUNTARY_EXIT* = DomainType([byte 0x04, 0x00, 0x00, 0x00])
|
||||
DOMAIN_SELECTION_PROOF* = DomainType([byte 0x05, 0x00, 0x00, 0x00])
|
||||
DOMAIN_AGGREGATE_AND_PROOF* = DomainType([byte 0x06, 0x00, 0x00, 0x00])
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/beacon-chain.md#domain-types
|
||||
DOMAIN_SYNC_COMMITTEE* = DomainType([byte 0x07, 0x00, 0x00, 0x00])
|
||||
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF* = DomainType([byte 0x08, 0x00, 0x00, 0x00])
|
||||
DOMAIN_CONTRIBUTION_AND_PROOF* = DomainType([byte 0x09, 0x00, 0x00, 0x00])
|
||||
|
||||
func getImmutableValidatorData*(validator: Validator): ImmutableValidatorData2 =
|
||||
let cookedKey = validator.pubkey.load() # Loading the pubkey is slow!
|
||||
doAssert cookedKey.isSome,
|
||||
|
@ -607,22 +609,16 @@ proc readValue*(reader: var JsonReader, value: var SubnetId)
|
|||
reader, "Subnet id must be <= " & $ATTESTATION_SUBNET_COUNT)
|
||||
value = SubnetId(v)
|
||||
|
||||
template writeValue*(writer: var JsonWriter, value: Version | ForkDigest) =
|
||||
writeValue(writer, $value)
|
||||
template writeValue*(
|
||||
writer: var JsonWriter, value: Version | ForkDigest | DomainType) =
|
||||
writeValue(writer, to0xHex(distinctBase(value)))
|
||||
|
||||
proc readValue*(reader: var JsonReader, value: var Version)
|
||||
proc readValue*(
|
||||
reader: var JsonReader, value: var (Version | ForkDigest | DomainType))
|
||||
{.raises: [IOError, SerializationError, Defect].} =
|
||||
let hex = reader.readValue(string)
|
||||
try:
|
||||
hexToByteArray(hex, array[4, byte](value))
|
||||
except ValueError:
|
||||
raiseUnexpectedValue(reader, "Hex string of 4 bytes expected")
|
||||
|
||||
proc readValue*(reader: var JsonReader, value: var ForkDigest)
|
||||
{.raises: [IOError, SerializationError, Defect].} =
|
||||
let hex = reader.readValue(string)
|
||||
try:
|
||||
hexToByteArray(hex, array[4, byte](value))
|
||||
hexToByteArray(hex, distinctBase(value))
|
||||
except ValueError:
|
||||
raiseUnexpectedValue(reader, "Hex string of 4 bytes expected")
|
||||
|
||||
|
@ -734,8 +730,8 @@ template newClone*[T](x: ref T not nil): ref T =
|
|||
template lenu64*(x: untyped): untyped =
|
||||
uint64(len(x))
|
||||
|
||||
func `$`*(v: ForkDigest | Version): string =
|
||||
toHex(array[4, byte](v))
|
||||
func `$`*(v: ForkDigest | Version | DomainType): string =
|
||||
toHex(distinctBase(v))
|
||||
|
||||
func toGaugeValue*(x: uint64 | Epoch | Slot): int64 =
|
||||
if x > uint64(int64.high):
|
||||
|
@ -744,16 +740,17 @@ func toGaugeValue*(x: uint64 | Epoch | Slot): int64 =
|
|||
int64(x)
|
||||
|
||||
# TODO where's borrow support when you need it
|
||||
func `==`*(a, b: ForkDigest | Version): bool =
|
||||
func `==`*(a, b: ForkDigest | Version | DomainType): bool =
|
||||
array[4, byte](a) == array[4, byte](b)
|
||||
func `<`*(a, b: ForkDigest | Version): bool =
|
||||
uint32.fromBytesBE(array[4, byte](a)) < uint32.fromBytesBE(array[4, byte](b))
|
||||
func len*(v: ForkDigest | Version): int = sizeof(v)
|
||||
func len*(v: ForkDigest | Version | DomainType): int = sizeof(v)
|
||||
func low*(v: ForkDigest | Version): int = 0
|
||||
func high*(v: ForkDigest | Version): int = len(v) - 1
|
||||
func `[]`*(v: ForkDigest | Version, idx: int): byte = array[4, byte](v)[idx]
|
||||
func `[]`*(v: ForkDigest | Version | DomainType, idx: int): byte =
|
||||
array[4, byte](v)[idx]
|
||||
|
||||
template bytes*(v: ForkDigest): array[4, byte] =
|
||||
template data*(v: ForkDigest | Version | DomainType): array[4, byte] =
|
||||
distinctBase(v)
|
||||
|
||||
func shortLog*(s: Slot): uint64 =
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import std/typetraits
|
||||
import stew/[assign2, results, base10, byteutils, endians2], presto/common,
|
||||
libp2p/peerid, nimcrypto/utils as ncrutils,
|
||||
import stew/[assign2, results, base10, byteutils], presto/common,
|
||||
libp2p/peerid,
|
||||
serialization, json_serialization, json_serialization/std/[options, net, sets]
|
||||
import ".."/[eth2_ssz_serialization, forks],
|
||||
".."/datatypes/[phase0, altair, bellatrix],
|
||||
|
@ -320,7 +320,7 @@ proc sszResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
|
|||
RestApiResponse.response(res, Http200, "application/octet-stream")
|
||||
|
||||
template hexOriginal(data: openarray[byte]): string =
|
||||
"0x" & ncrutils.toHex(data, true)
|
||||
to0xHex(data)
|
||||
|
||||
proc decodeJsonString*[T](t: typedesc[T],
|
||||
data: JsonString,
|
||||
|
@ -384,25 +384,6 @@ proc readValue*(reader: var JsonReader[RestJson], value: var UInt256) {.
|
|||
raiseUnexpectedValue(reader,
|
||||
"UInt256 value should be a valid decimal string")
|
||||
|
||||
## DomainType
|
||||
proc writeValue*(w: var JsonWriter[RestJson], value: DomainType) {.
|
||||
raises: [IOError, Defect].} =
|
||||
writeValue(w, hexOriginal(uint32(value).toBytesLE()))
|
||||
|
||||
proc readValue*(reader: var JsonReader[RestJson], value: var DomainType) {.
|
||||
raises: [IOError, SerializationError, Defect].} =
|
||||
try:
|
||||
let
|
||||
data = hexToByteArray(reader.readValue(string), 4)
|
||||
res = uint32.fromBytesLE(data)
|
||||
if res >= uint32(low(DomainType)) and res <= uint32(high(DomainType)):
|
||||
value = cast[DomainType](res)
|
||||
else:
|
||||
raiseUnexpectedValue(reader, "Incorrect DomainType value")
|
||||
except ValueError:
|
||||
raiseUnexpectedValue(reader,
|
||||
"DomainType value should be a valid hex string")
|
||||
|
||||
## Slot
|
||||
proc writeValue*(writer: var JsonWriter[RestJson], value: Slot) {.
|
||||
raises: [IOError, Defect].} =
|
||||
|
@ -648,43 +629,15 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: Eth1Address) {.
|
|||
writeValue(writer, hexOriginal(distinctBase(value)))
|
||||
|
||||
## Version
|
||||
proc readValue*(reader: var JsonReader[RestJson], value: var Version) {.
|
||||
proc readValue*(
|
||||
reader: var JsonReader[RestJson],
|
||||
value: var (Version | ForkDigest | DomainType | GraffitiBytes)) {.
|
||||
raises: [IOError, SerializationError, Defect].} =
|
||||
try:
|
||||
hexToByteArray(reader.readValue(string), distinctBase(value))
|
||||
except ValueError:
|
||||
raiseUnexpectedValue(reader,
|
||||
"Version value should be a valid hex string")
|
||||
|
||||
proc writeValue*(writer: var JsonWriter[RestJson], value: Version) {.
|
||||
raises: [IOError, Defect].} =
|
||||
writeValue(writer, hexOriginal(distinctBase(value)))
|
||||
|
||||
## ForkDigest
|
||||
proc readValue*(reader: var JsonReader[RestJson], value: var ForkDigest) {.
|
||||
raises: [IOError, SerializationError, Defect].} =
|
||||
try:
|
||||
hexToByteArray(reader.readValue(string), distinctBase(value))
|
||||
except ValueError:
|
||||
raiseUnexpectedValue(reader,
|
||||
"ForkDigest value should be a valid hex string")
|
||||
|
||||
proc writeValue*(writer: var JsonWriter[RestJson], value: ForkDigest) {.
|
||||
raises: [IOError, Defect].} =
|
||||
writeValue(writer, hexOriginal(distinctBase(value)))
|
||||
|
||||
## GraffitiBytes
|
||||
proc readValue*(reader: var JsonReader[RestJson], value: var GraffitiBytes) {.
|
||||
raises: [IOError, SerializationError, Defect].} =
|
||||
try:
|
||||
hexToByteArray(reader.readValue(string), distinctBase(value))
|
||||
except ValueError:
|
||||
raiseUnexpectedValue(reader,
|
||||
"GraffitiBytes value should be a valid hex string")
|
||||
|
||||
proc writeValue*(writer: var JsonWriter[RestJson], value: GraffitiBytes) {.
|
||||
raises: [IOError, Defect].} =
|
||||
writeValue(writer, hexOriginal(distinctBase(value)))
|
||||
raiseUnexpectedValue(
|
||||
reader, "Expected a valid hex string with " & $value.len() & " bytes")
|
||||
|
||||
## ForkedBeaconBlock
|
||||
proc readValue*(reader: var JsonReader[RestJson],
|
||||
|
|
|
@ -424,19 +424,10 @@ func bytes_to_uint64*(data: openArray[byte]): uint64 =
|
|||
# Little-endian data representation
|
||||
uint64.fromBytesLE(data)
|
||||
|
||||
# Have 1, 4, and 8-byte versions. Spec only defines 8-byte version, but useful
|
||||
# to check invariants on rest.
|
||||
func uint_to_bytes8*(x: uint64): array[8, byte] =
|
||||
x.toBytesLE()
|
||||
|
||||
func uint_to_bytes4*(x: uint64): array[4, byte] =
|
||||
doAssert x < 2'u64^32
|
||||
|
||||
# Little-endian data representation
|
||||
result[0] = ((x shr 0) and 0xff).byte
|
||||
result[1] = ((x shr 8) and 0xff).byte
|
||||
result[2] = ((x shr 16) and 0xff).byte
|
||||
result[3] = ((x shr 24) and 0xff).byte
|
||||
func uint_to_bytes*(x: uint64): array[8, byte] = toBytesLE(x)
|
||||
func uint_to_bytes*(x: uint32): array[4, byte] = toBytesLE(x)
|
||||
func uint_to_bytes*(x: uint16): array[2, byte] = toBytesLE(x)
|
||||
func uint_to_bytes*(x: uint8): array[1, byte] = toBytesLE(x)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#compute_domain
|
||||
func compute_domain*(
|
||||
|
@ -446,7 +437,7 @@ func compute_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] = uint_to_bytes4(domain_type.uint64)
|
||||
result[0..3] = domain_type.data
|
||||
result[4..31] = fork_data_root.data.toOpenArray(0, 27)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#get_domain
|
||||
|
@ -491,8 +482,8 @@ func get_seed*(state: ForkyBeaconState, epoch: Epoch, domain_type: DomainType):
|
|||
static:
|
||||
doAssert EPOCHS_PER_HISTORICAL_VECTOR > MIN_SEED_LOOKAHEAD
|
||||
|
||||
seed_input[0..3] = uint_to_bytes4(domain_type.uint64)
|
||||
seed_input[4..11] = uint_to_bytes8(epoch.uint64)
|
||||
seed_input[0..3] = domain_type.data
|
||||
seed_input[4..11] = uint_to_bytes(epoch.uint64)
|
||||
seed_input[12..43] =
|
||||
get_randao_mix(state, # Avoid underflow
|
||||
epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1).data
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
|
||||
import
|
||||
std/[macros, strutils, parseutils, tables],
|
||||
stew/endians2, stint, web3/[ethtypes]
|
||||
|
||||
export
|
||||
toBytesBE
|
||||
stew/[byteutils], stint, web3/[ethtypes]
|
||||
|
||||
const
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#withdrawal-prefixes
|
||||
|
@ -376,7 +373,7 @@ template parse(T: type byte, input: string): T =
|
|||
|
||||
func parse(T: type Version, input: string): T
|
||||
{.raises: [ValueError, Defect].} =
|
||||
Version toBytesBE(uint32 parse(uint64, input))
|
||||
Version hexToByteArray(input, 4)
|
||||
|
||||
template parse(T: type Slot, input: string): T =
|
||||
Slot parse(uint64, input)
|
||||
|
|
|
@ -48,7 +48,7 @@ func shuffle_list*(input: var seq[ValidatorIndex], seed: Eth2Digest) =
|
|||
# and take a uint64 from it, and modulo it to get a pivot within range.
|
||||
let
|
||||
pivotDigest = eth2digest(buf.toOpenArray(0, PIVOT_VIEW_SIZE - 1))
|
||||
pivot = bytes_to_uint64(pivotDigest.data.toOpenArray(0, 7)) mod listSize
|
||||
pivot = bytes_to_uint64(pivotDigest.data.toOpenArray(0, 7)) mod list_size
|
||||
|
||||
# Split up the for-loop in two:
|
||||
# 1. Handle the part from 0 (incl) to pivot (incl). This is mirrored around
|
||||
|
@ -77,7 +77,7 @@ func shuffle_list*(input: var seq[ValidatorIndex], seed: Eth2Digest) =
|
|||
# (of the part left to the pivot).
|
||||
# This makes us process each pear exactly once (instead of unnecessarily
|
||||
# twice, like in the spec)
|
||||
buf[33..<37] = uint_to_bytes4(pivot shr 8)
|
||||
buf[33..<37] = uint_to_bytes(uint32(pivot shr 8))
|
||||
|
||||
var
|
||||
mirror = (pivot + 1) shr 1
|
||||
|
@ -92,7 +92,7 @@ func shuffle_list*(input: var seq[ValidatorIndex], seed: Eth2Digest) =
|
|||
# Every 256th bit (aligned to j).
|
||||
if (j and 0xff) == 0xff:
|
||||
# just overwrite the last part of the buffer, reuse the start (seed, round)
|
||||
buf[33..<37] = uint_to_bytes4(j shr 8)
|
||||
buf[33..<37] = uint_to_bytes(uint32(j shr 8))
|
||||
source = eth2digest(buf)
|
||||
|
||||
# Same trick with byte retrieval. Only every 8th.
|
||||
|
@ -116,7 +116,7 @@ func shuffle_list*(input: var seq[ValidatorIndex], seed: Eth2Digest) =
|
|||
# Again, seed and round input is in place, just update the position.
|
||||
# We start at the end, and work back to the mirror point.
|
||||
# This makes us process each pear exactly once (instead of unnecessarily twice, like in the spec)
|
||||
buf[33..<37] = uint_to_bytes4(lend shr 8)
|
||||
buf[33..<37] = uint_to_bytes(uint32(lend shr 8))
|
||||
|
||||
source = eth2digest(buf)
|
||||
byteV = source.data[(lend and 0xff) shr 3]
|
||||
|
@ -344,7 +344,7 @@ func compute_shuffled_index*(
|
|||
|
||||
flip = ((index_count + pivot) - cur_idx_permuted) mod index_count
|
||||
position = max(cur_idx_permuted, flip)
|
||||
source_buffer[33..36] = uint_to_bytes4((position shr 8))
|
||||
source_buffer[33..36] = uint_to_bytes(uint32(position shr 8))
|
||||
let
|
||||
source = eth2digest(source_buffer).data
|
||||
byte_value = source[(position mod 256) shr 3]
|
||||
|
@ -370,7 +370,7 @@ func compute_proposer_index(state: ForkyBeaconState,
|
|||
buffer: array[32+8, byte]
|
||||
buffer[0..31] = seed.data
|
||||
while true:
|
||||
buffer[32..39] = uint_to_bytes8(i div 32)
|
||||
buffer[32..39] = uint_to_bytes(i div 32)
|
||||
let
|
||||
candidate_index =
|
||||
indices[compute_shuffled_index(i mod seq_len, seq_len, seed)]
|
||||
|
@ -411,7 +411,7 @@ func get_beacon_proposer_index*(
|
|||
|
||||
var res: Option[ValidatorIndex]
|
||||
for i in 0..<SLOTS_PER_EPOCH:
|
||||
buffer[32..39] = uint_to_bytes8((start + i).uint64)
|
||||
buffer[32..39] = uint_to_bytes((start + i).uint64)
|
||||
let seed = eth2digest(buffer)
|
||||
let pi = compute_proposer_index(state, indices, seed)
|
||||
if start + i == slot:
|
||||
|
|
|
@ -96,15 +96,15 @@ proc sendResponseChunk*(response: UntypedResponse,
|
|||
of BeaconBlockFork.Phase0:
|
||||
response.stream.writeChunk(some ResponseCode.Success,
|
||||
SSZ.encode(val.phase0Data),
|
||||
response.peer.network.forkDigests.phase0.bytes)
|
||||
response.peer.network.forkDigests.phase0.data)
|
||||
of BeaconBlockFork.Altair:
|
||||
response.stream.writeChunk(some ResponseCode.Success,
|
||||
SSZ.encode(val.altairData),
|
||||
response.peer.network.forkDigests.altair.bytes)
|
||||
response.peer.network.forkDigests.altair.data)
|
||||
of BeaconBlockFork.Bellatrix:
|
||||
response.stream.writeChunk(some ResponseCode.Success,
|
||||
SSZ.encode(val.mergeData),
|
||||
response.peer.network.forkDigests.bellatrix.bytes)
|
||||
response.peer.network.forkDigests.bellatrix.data)
|
||||
|
||||
func shortLog*(s: StatusMsg): auto =
|
||||
(
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
The `JSON-RPC API` is a collection of APIs for querying the state of the application at runtime.
|
||||
|
||||
The API is based on the common [eth2 APIs](https://github.com/ethereum/eth2.0-APIs) with the exception that `JSON-RPC` is used instead of [http `REST`](./rest-api.md) (the method names, parameters and results are all the same except for the encoding / access method).
|
||||
The API is based on an early version of the common [beacon APIs](https://github.com/ethereum/beacon-APIs) with the exception that `JSON-RPC` is used instead of [http `REST`](./rest-api.md) (the method names, parameters and results are all the same except for the encoding / access method).
|
||||
|
||||
Nimbus also implements the [common REST API](./rest-api.md) - new applications should consider using it instead of JSON RPC.
|
||||
|
||||
The `JSON-RPC API` should not be exposed to the public internet.
|
||||
|
||||
## Introduction
|
||||
|
||||
The `nimbus-eth2` API is implemented using JSON-RPC 2.0. To query it, you can use a JSON-RPC library in the language of your choice, or a tool like `curl` to access it from the command line. A tool like [jq](https://stedolan.github.io/jq/) is helpful to pretty-print the responses.
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
# REST API
|
||||
|
||||
Nimbus supports the [common REST API](https://ethereum.github.io/beacon-APIs/) for runtime communication. To enable it, use the `--rest` option when starting the beacon node, then access the API from http://localhost:5052/. The port and listening address can be further configured through the options `--rest-port` and `--rest-address`.
|
||||
Nimbus exposes a high-performance implementation of the [Beacon Node API](https://ethereum.github.io/beacon-APIs/).
|
||||
|
||||
The API is a REST interface, accessed via HTTP. The API should not, unless protected by additional security layers, be exposed to the public Internet as the API includes multiple endpoints which could open your node to denial-of-service (DoS) attacks through endpoints triggering heavy processing.
|
||||
The API is a `REST` interface accessed via `HTTP`. The API should not, unless protected by additional security layers, be exposed to the public Internet as the API includes multiple endpoints which could open your node to denial-of-service (DoS) attacks.
|
||||
|
||||
The beacon node (BN) maintains the state of the beacon chain by communicating with other beacon nodes in the Ethereum network. Conceptually, it does not maintain keypairs that participate with the beacon chain.
|
||||
The API can be used with any conforming consumer, including alternative validator client implementations, explorers and tooling.
|
||||
|
||||
The validator client (VC) is a conceptually separate entity which utilizes private keys to perform validator related tasks, called "duties", on the beacon chain. These duties include the production of beacon blocks and signing of attestations.
|
||||
## Configuration
|
||||
|
||||
> Please note that using a validator client with a Nimbus beacon node requires the node to be launched with the `--subscribe-all-subnets` option. This limitation will be removed in Nimbus 1.6.
|
||||
By default, the REST interface is disabled. To enable it, start the beacon node with the `--rest` option, then access the API from http://localhost:5052/.
|
||||
|
||||
The goal of this specification is to promote interoperability between various beacon node implementations.
|
||||
> **Warning:** If you are using a validator client with a Nimbus beacon node, and running a Nimbus version prior to `v1.5.5`, then you will need to launch the node with the `--subscribe-all-subnets` option enabled.
|
||||
|
||||
By default, only connections from the same machine are entertained. The port and listening address can be further configured through the options `--rest-port` and `--rest-address`.
|
||||
|
||||
## Specification
|
||||
|
||||
The specification is documented [here](https://ethereum.github.io/beacon-APIs/).
|
||||
|
||||
See the Readme [here](https://github.com/ethereum/beacon-apis).
|
||||
See the Readme [here](https://github.com/ethereum/beacon-APIs).
|
||||
|
||||
## Quickly test your tooling against Nimbus
|
||||
|
||||
The [Nimbus REST api](https://nimbus.guide/rest-api.html) is now available from:
|
||||
|
||||
|
||||
* http://testing.mainnet.beacon-api.nimbus.team/
|
||||
* http://unstable.mainnet.beacon-api.nimbus.team/
|
||||
* http://unstable.prater.beacon-api.nimbus.team/
|
||||
|
||||
|
||||
Note that right now these are very much unstable testing instances. They may be unresponsive at times - so **please do not rely on them for validation**. We may also disable them at any time.
|
||||
|
||||
|
||||
|
|
|
@ -1037,7 +1037,7 @@
|
|||
},
|
||||
"response": {
|
||||
"status": {"operator": "oneof", "value": ["404", "200"]},
|
||||
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
|
||||
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1139,7 +1139,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"},
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1150,7 +1150,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"},
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1161,7 +1161,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"},
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1172,7 +1172,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"},
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1183,7 +1183,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"},
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1270,7 +1270,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "oneof", "value": ["400", "200"]},
|
||||
"status": {"operator": "oneof", "value": ["400", "200"]}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1489,18 +1489,6 @@
|
|||
"body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "slot": "", "validators": [""]}]}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["beacon", "states_committees"],
|
||||
"request": {
|
||||
"url": "/eth/v1/beacon/states/head/committees?slot=18446744073709551615",
|
||||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "200"},
|
||||
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
|
||||
"body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "slot": "", "validators": [""]}]}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["beacon", "states_committees"],
|
||||
"request": {
|
||||
|
@ -1525,18 +1513,6 @@
|
|||
"body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "slot": "", "validators": [""]}]}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["beacon", "states_committees"],
|
||||
"request": {
|
||||
"url": "/eth/v1/beacon/states/head/committees?epoch=576460752303423487",
|
||||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "200"},
|
||||
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
|
||||
"body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "slot": "", "validators": [""]}]}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["beacon", "states_committees"],
|
||||
"request": {
|
||||
|
@ -2939,19 +2915,6 @@
|
|||
"body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data": [{"pubkey": "", "validator_index": "", "slot": ""}]}}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["validator", "proposer_duties", "mainnet"],
|
||||
"comment": "Maximum epoch for mainnet parameters",
|
||||
"request": {
|
||||
"url": "/eth/v1/validator/duties/proposer/576460752303423487",
|
||||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "503"},
|
||||
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
|
||||
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["validator", "proposer_duties", "mainnet"],
|
||||
"comment": "Maximum epoch + 1 for mainnet parameters",
|
||||
|
@ -3138,7 +3101,7 @@
|
|||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "oneof", "value": ["400", "200"]},
|
||||
"status": {"operator": "oneof", "value": ["400", "200"]}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3188,24 +3151,10 @@
|
|||
"request": {
|
||||
"method": "POST",
|
||||
"url": "/eth/v1/validator/duties/attester/0",
|
||||
"headers": {"Accept": "application/json"},
|
||||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"},
|
||||
}
|
||||
},
|
||||
{
|
||||
"topics": ["validator", "attester_duties"],
|
||||
"request": {
|
||||
"url": "/eth/v1/validator/duties/attester/576460752303423487",
|
||||
"method": "POST",
|
||||
"headers": {"Accept": "application/json"},
|
||||
"body": {"content-type": "application/json", "data": "[\"0\"]"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "503"},
|
||||
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
|
||||
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}]
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -3255,7 +3204,7 @@
|
|||
"request": {
|
||||
"url": "/eth/v1/validator/duties/attester/foobar",
|
||||
"method": "POST",
|
||||
"headers": {"Accept": "application/json"},
|
||||
"headers": {"Accept": "application/json"}
|
||||
},
|
||||
"response": {
|
||||
"status": {"operator": "equals", "value": "400"}
|
||||
|
|
|
@ -1041,7 +1041,8 @@ proc startTests(conf: RestTesterConf, uri: Uri,
|
|||
|
||||
let tcaseRes = fut.read()
|
||||
results[tcaseRes.index] = tcaseRes.data
|
||||
notice "Got test result", index = tcaseRes.index,
|
||||
notice "Got test result", name = rules[tcaseRes.index].getTestName(),
|
||||
index = tcaseRes.index,
|
||||
value = tcaseRes.data.kind
|
||||
pending[i] = nil
|
||||
|
||||
|
|
|
@ -59,6 +59,10 @@ suite "BlockSlot and helpers":
|
|||
s1 = BlockRef(bid: BlockId(slot: Slot(1)), parent: s0)
|
||||
s2 = BlockRef(bid: BlockId(slot: Slot(2)), parent: s1)
|
||||
s4 = BlockRef(bid: BlockId(slot: Slot(4)), parent: s2)
|
||||
se1 = BlockRef(bid:
|
||||
BlockId(slot: Epoch(1).compute_start_slot_at_epoch()), parent: s2)
|
||||
se2 = BlockRef(bid:
|
||||
BlockId(slot: Epoch(2).compute_start_slot_at_epoch()), parent: se1)
|
||||
|
||||
check:
|
||||
s0.atSlot(Slot(0)).blck == s0
|
||||
|
@ -69,6 +73,14 @@ suite "BlockSlot and helpers":
|
|||
|
||||
s4.atSlot() == s4.atSlot(s4.slot)
|
||||
|
||||
se2.dependentBlock(s0, Epoch(2)) == se1
|
||||
se2.dependentBlock(s0, Epoch(1)) == s2
|
||||
se2.dependentBlock(s0, Epoch(0)) == s0
|
||||
|
||||
se2.prevDependentBlock(s0, Epoch(2)) == s2
|
||||
se2.prevDependentBlock(s0, Epoch(1)) == s0
|
||||
se2.prevDependentBlock(s0, Epoch(0)) == s0
|
||||
|
||||
test "parent sanity":
|
||||
let
|
||||
s0 = BlockRef(bid: BlockId(slot: Slot(0)))
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 543c7edbc710a3f9d329e094499159a315ee4905
|
||||
Subproject commit 17cd8c846fd6238f074b67875d17617c90ccace5
|
Loading…
Reference in New Issue