2021-10-14 10:38:38 +00:00
|
|
|
import std/options,
|
|
|
|
presto,
|
2021-03-23 22:50:18 +00:00
|
|
|
nimcrypto/utils as ncrutils,
|
2021-08-12 13:08:20 +00:00
|
|
|
../spec/[forks],
|
2021-08-03 15:17:11 +00:00
|
|
|
../spec/eth2_apis/[rest_types, eth2_rest_serialization],
|
2021-10-19 14:09:26 +00:00
|
|
|
../beacon_node,
|
2021-10-27 12:01:11 +00:00
|
|
|
../consensus_object_pools/blockchain_dag
|
2021-08-03 15:17:11 +00:00
|
|
|
|
|
|
|
export
|
2021-10-14 10:38:38 +00:00
|
|
|
options, eth2_rest_serialization, blockchain_dag, presto, rest_types
|
2021-03-17 18:46:45 +00:00
|
|
|
|
|
|
|
const
|
2021-03-23 22:50:18 +00:00
|
|
|
MaxEpoch* = compute_epoch_at_slot(not(0'u64))
|
|
|
|
|
2021-04-08 10:49:28 +00:00
|
|
|
BlockValidationError* =
|
|
|
|
"The block failed validation, but was successfully broadcast anyway. It " &
|
|
|
|
"was not integrated into the beacon node's database."
|
|
|
|
BlockValidationSuccess* =
|
|
|
|
"The block was validated successfully and has been broadcast"
|
|
|
|
BeaconNodeInSyncError* =
|
|
|
|
"Beacon node is currently syncing and not serving request on that endpoint"
|
|
|
|
BlockNotFoundError* =
|
|
|
|
"Block header/data has not been found"
|
2021-04-08 14:34:05 +00:00
|
|
|
BlockProduceError* =
|
|
|
|
"Could not produce the block"
|
2021-04-08 10:49:28 +00:00
|
|
|
EmptyRequestBodyError* =
|
|
|
|
"Empty request's body"
|
|
|
|
InvalidBlockObjectError* =
|
|
|
|
"Unable to decode block object(s)"
|
|
|
|
InvalidAttestationObjectError* =
|
|
|
|
"Unable to decode attestation object(s)"
|
|
|
|
AttestationValidationError* =
|
|
|
|
"Some errors happened while validating attestation(s)"
|
|
|
|
AttestationValidationSuccess* =
|
|
|
|
"Attestation object(s) was broadcasted"
|
|
|
|
InvalidAttesterSlashingObjectError* =
|
|
|
|
"Unable to decode attester slashing object(s)"
|
|
|
|
AttesterSlashingValidationError* =
|
|
|
|
"Invalid attester slashing, it will never pass validation so it's rejected"
|
|
|
|
AttesterSlashingValidationSuccess* =
|
|
|
|
"Attester slashing object was broadcasted"
|
|
|
|
InvalidProposerSlashingObjectError* =
|
|
|
|
"Unable to decode proposer slashing object(s)"
|
|
|
|
ProposerSlashingValidationError* =
|
|
|
|
"Invalid proposer slashing, it will never pass validation so it's rejected"
|
|
|
|
ProposerSlashingValidationSuccess* =
|
|
|
|
"Proposer slashing object was broadcasted"
|
|
|
|
InvalidVoluntaryExitObjectError* =
|
|
|
|
"Unable to decode voluntary exit object(s)"
|
|
|
|
VoluntaryExitValidationError* =
|
|
|
|
"Invalid voluntary exit, it will never pass validation so it's rejected"
|
|
|
|
VoluntaryExitValidationSuccess* =
|
|
|
|
"Voluntary exit object(s) was broadcasted"
|
2021-04-08 14:34:05 +00:00
|
|
|
InvalidAggregateAndProofObjectError* =
|
|
|
|
"Unable to decode aggregate and proof object(s)"
|
|
|
|
AggregateAndProofValidationError* =
|
|
|
|
"Invalid aggregate and proof, it will never pass validation so it's " &
|
|
|
|
"rejected"
|
|
|
|
AggregateAndProofValidationSuccess* =
|
|
|
|
"Aggregate and proof object(s) was broadcasted"
|
2021-07-13 11:15:07 +00:00
|
|
|
BeaconCommitteeSubscriptionSuccess* =
|
2021-09-23 22:13:25 +00:00
|
|
|
"Beacon node processed committee subscription request(s)"
|
|
|
|
SyncCommitteeSubscriptionSuccess* =
|
|
|
|
"Beacon node processed sync committee subscription request(s)"
|
2021-04-08 10:49:28 +00:00
|
|
|
InvalidParentRootValueError* =
|
|
|
|
"Invalid parent root value"
|
|
|
|
MissingSlotValueError* =
|
|
|
|
"Missing `slot` value"
|
|
|
|
InvalidSlotValueError* =
|
|
|
|
"Invalid slot value"
|
|
|
|
MissingCommitteeIndexValueError* =
|
|
|
|
"Missing `committee_index` value"
|
|
|
|
InvalidCommitteeIndexValueError* =
|
|
|
|
"Invalid committee index value"
|
2021-04-08 14:34:05 +00:00
|
|
|
MissingAttestationDataRootValueError* =
|
|
|
|
"Missing `attestation_data_root` value"
|
|
|
|
InvalidAttestationDataRootValueError* =
|
|
|
|
"Invalid attestation data root value"
|
|
|
|
UnableToGetAggregatedAttestationError* =
|
|
|
|
"Unable to retrieve an aggregated attestation"
|
|
|
|
MissingRandaoRevealValue* =
|
|
|
|
"Missing `randao_reveal` value"
|
|
|
|
InvalidRandaoRevealValue* =
|
|
|
|
"Invalid randao reveal value"
|
|
|
|
InvalidGraffitiBytesValye* =
|
|
|
|
"Invalid graffiti bytes value"
|
2021-04-08 10:49:28 +00:00
|
|
|
InvalidEpochValueError* =
|
|
|
|
"Invalid epoch value"
|
2021-10-14 10:38:38 +00:00
|
|
|
EpochFromFutureError* =
|
|
|
|
"Epoch value is far from the future"
|
2021-04-08 10:49:28 +00:00
|
|
|
InvalidStateIdValueError* =
|
|
|
|
"Invalid state identifier value"
|
|
|
|
InvalidBlockIdValueError* =
|
|
|
|
"Invalid block identifier value"
|
|
|
|
InvalidValidatorIdValueError* =
|
|
|
|
"Invalid validator's identifier value(s)"
|
|
|
|
MaximumNumberOfValidatorIdsError* =
|
|
|
|
"Maximum number of validator identifier values exceeded"
|
|
|
|
InvalidValidatorStatusValueError* =
|
|
|
|
"Invalid validator's status value error"
|
2021-04-08 14:34:05 +00:00
|
|
|
InvalidValidatorIndexValueError* =
|
|
|
|
"Invalid validator's index value(s)"
|
|
|
|
EmptyValidatorIndexArrayError* =
|
|
|
|
"Empty validator's index array"
|
|
|
|
InvalidSubscriptionRequestValueError* =
|
|
|
|
"Invalid subscription request object(s)"
|
2021-04-08 10:49:28 +00:00
|
|
|
ValidatorNotFoundError* =
|
|
|
|
"Could not find validator"
|
|
|
|
ValidatorStatusNotFoundError* =
|
|
|
|
"Could not obtain validator's status"
|
|
|
|
TooHighValidatorIndexValueError* =
|
|
|
|
"Validator index exceeds maximum number of validators allowed"
|
|
|
|
UnsupportedValidatorIndexValueError* =
|
|
|
|
"Validator index exceeds maximum supported number of validators"
|
|
|
|
StateNotFoundError* =
|
2021-10-14 10:38:38 +00:00
|
|
|
"Could not get requested state"
|
2021-04-08 10:49:28 +00:00
|
|
|
SlotNotFoundError* =
|
|
|
|
"Slot number is too far away"
|
2021-04-08 14:34:05 +00:00
|
|
|
SlotNotInNextWallSlotEpochError* =
|
|
|
|
"Requested slot not in next wall-slot epoch"
|
|
|
|
SlotFromThePastError* =
|
|
|
|
"Requested slot from the past"
|
2021-09-23 22:13:25 +00:00
|
|
|
SlotFromTheIncorrectForkError* =
|
|
|
|
"Requested slot is from incorrect fork"
|
|
|
|
EpochFromTheIncorrectForkError* =
|
|
|
|
"Requested epoch is from incorrect fork"
|
2021-04-08 14:34:05 +00:00
|
|
|
ProposerNotFoundError* =
|
|
|
|
"Could not find proposer for the head and slot"
|
2021-04-08 10:49:28 +00:00
|
|
|
NoHeadForSlotError* =
|
|
|
|
"Cound not find head for slot"
|
|
|
|
EpochOverflowValueError* =
|
|
|
|
"Requesting epoch for which slot would overflow"
|
2021-04-08 14:34:05 +00:00
|
|
|
InvalidPeerStateValueError* =
|
|
|
|
"Invalid peer's state value(s) error"
|
|
|
|
InvalidPeerDirectionValueError* =
|
|
|
|
"Invalid peer's direction value(s) error"
|
|
|
|
InvalidPeerIdValueError* =
|
|
|
|
"Invalid peer's id value(s) error"
|
|
|
|
PeerNotFoundError* =
|
|
|
|
"Peer not found"
|
|
|
|
InvalidLogLevelValueError* =
|
|
|
|
"Invalid log level value error"
|
2021-09-16 13:32:32 +00:00
|
|
|
ContentNotAcceptableError* =
|
|
|
|
"Could not find out accepted content type"
|
|
|
|
InvalidAcceptError* =
|
|
|
|
"Incorrect accept response type"
|
2021-09-23 22:13:25 +00:00
|
|
|
MissingSubCommitteeIndexValueError* =
|
|
|
|
"Missing `subcommittee_index` value"
|
|
|
|
InvalidSubCommitteeIndexValueError* =
|
|
|
|
"Invalid `subcommittee_index` value"
|
|
|
|
MissingBeaconBlockRootValueError* =
|
|
|
|
"Missing `beacon_block_root` value"
|
|
|
|
InvalidBeaconBlockRootValueError* =
|
|
|
|
"Invalid `beacon_block_root` value"
|
|
|
|
EpochOutsideSyncCommitteePeriodError* =
|
|
|
|
"Epoch is outside the sync committee period of the state"
|
|
|
|
InvalidSyncCommitteeSignatureMessageError* =
|
|
|
|
"Unable to decode sync committee message(s)"
|
|
|
|
InvalidSyncCommitteeSubscriptionRequestError* =
|
|
|
|
"Unable to decode sync committee subscription request(s)"
|
|
|
|
InvalidContributionAndProofMessageError* =
|
|
|
|
"Unable to decode contribute and proof message(s)"
|
|
|
|
SyncCommitteeMessageValidationError* =
|
|
|
|
"Some errors happened while validating sync committee message(s)"
|
|
|
|
SyncCommitteeMessageValidationSuccess* =
|
|
|
|
"Sync committee message(s) was broadcasted"
|
|
|
|
ContributionAndProofValidationError* =
|
|
|
|
"Some errors happened while validating contribution and proof(s)"
|
|
|
|
ContributionAndProofValidationSuccess* =
|
|
|
|
"Contribution and proof(s) was broadcasted"
|
|
|
|
ProduceContributionError* =
|
|
|
|
"Unable to produce contribution using the passed parameters"
|
2021-04-08 10:49:28 +00:00
|
|
|
InternalServerError* =
|
|
|
|
"Internal server error"
|
|
|
|
NoImplementationError* =
|
|
|
|
"Not implemented yet"
|
2021-10-04 19:08:31 +00:00
|
|
|
KeystoreAdditionFailure* =
|
|
|
|
"Could not add some keystores"
|
|
|
|
InvalidKeystoreObjects* =
|
|
|
|
"Invalid keystore objects found"
|
|
|
|
KeystoreAdditionSuccess* =
|
|
|
|
"All keystores has been added"
|
|
|
|
KeystoreModificationFailure* =
|
|
|
|
"Could not change keystore(s) state"
|
|
|
|
KeystoreModificationSuccess* =
|
|
|
|
"Keystore(s) state was successfully modified"
|
|
|
|
KeystoreRemovalSuccess* =
|
|
|
|
"Keystore(s) was successfully removed"
|
|
|
|
KeystoreRemovalFailure* =
|
|
|
|
"Could not remove keystore(s)"
|
|
|
|
InvalidValidatorPublicKey* =
|
|
|
|
"Invalid validator's public key(s) found"
|
2021-04-08 10:49:28 +00:00
|
|
|
|
2021-03-17 18:46:45 +00:00
|
|
|
type
|
2021-04-03 00:21:44 +00:00
|
|
|
ValidatorIndexError* {.pure.} = enum
|
|
|
|
UnsupportedValue, TooHighValue
|
|
|
|
|
2021-03-17 18:46:45 +00:00
|
|
|
func match(data: openarray[char], charset: set[char]): int =
|
|
|
|
for ch in data:
|
|
|
|
if ch notin charset:
|
|
|
|
return 1
|
|
|
|
0
|
|
|
|
|
|
|
|
proc validate(key: string, value: string): int =
|
|
|
|
## This is rough validation procedure which should be simple and fast,
|
|
|
|
## because it will be used for query routing.
|
|
|
|
case key
|
|
|
|
of "{epoch}":
|
2021-04-12 16:23:45 +00:00
|
|
|
0
|
2021-03-17 18:46:45 +00:00
|
|
|
of "{slot}":
|
2021-04-12 16:23:45 +00:00
|
|
|
0
|
2021-03-17 18:46:45 +00:00
|
|
|
of "{peer_id}":
|
2021-04-12 16:23:45 +00:00
|
|
|
0
|
2021-03-17 18:46:45 +00:00
|
|
|
of "{state_id}":
|
2021-04-12 16:23:45 +00:00
|
|
|
0
|
2021-03-17 18:46:45 +00:00
|
|
|
of "{block_id}":
|
2021-04-12 16:23:45 +00:00
|
|
|
0
|
2021-03-17 18:46:45 +00:00
|
|
|
of "{validator_id}":
|
2021-04-12 16:23:45 +00:00
|
|
|
0
|
2021-03-17 18:46:45 +00:00
|
|
|
else:
|
|
|
|
1
|
|
|
|
|
|
|
|
proc getCurrentHead*(node: BeaconNode,
|
|
|
|
slot: Slot): Result[BlockRef, cstring] =
|
2021-06-01 11:13:40 +00:00
|
|
|
let res = node.dag.head
|
2021-03-17 18:46:45 +00:00
|
|
|
# if not(node.isSynced(res)):
|
|
|
|
# return err("Cannot fulfill request until node is synced")
|
|
|
|
if res.slot + uint64(2 * SLOTS_PER_EPOCH) < slot:
|
|
|
|
return err("Requesting way ahead of the current head")
|
|
|
|
ok(res)
|
|
|
|
|
|
|
|
proc getCurrentHead*(node: BeaconNode,
|
|
|
|
epoch: Epoch): Result[BlockRef, cstring] =
|
2021-04-27 20:46:24 +00:00
|
|
|
if epoch > MaxEpoch:
|
2021-03-17 18:46:45 +00:00
|
|
|
return err("Requesting epoch for which slot would overflow")
|
|
|
|
node.getCurrentHead(compute_start_slot_at_epoch(epoch))
|
|
|
|
|
|
|
|
proc toBlockSlot*(blckRef: BlockRef): BlockSlot =
|
|
|
|
blckRef.atSlot(blckRef.slot)
|
|
|
|
|
|
|
|
proc getBlockSlot*(node: BeaconNode,
|
|
|
|
stateIdent: StateIdent): Result[BlockSlot, cstring] =
|
|
|
|
case stateIdent.kind
|
|
|
|
of StateQueryKind.Slot:
|
|
|
|
let head = ? getCurrentHead(node, stateIdent.slot)
|
|
|
|
let bslot = head.atSlot(stateIdent.slot)
|
|
|
|
if isNil(bslot.blck):
|
|
|
|
return err("Block not found")
|
|
|
|
ok(bslot)
|
|
|
|
of StateQueryKind.Root:
|
2021-06-01 11:13:40 +00:00
|
|
|
let blckRef = node.dag.getRef(stateIdent.root)
|
2021-03-17 18:46:45 +00:00
|
|
|
if isNil(blckRef):
|
|
|
|
return err("Block not found")
|
|
|
|
ok(blckRef.toBlockSlot())
|
|
|
|
of StateQueryKind.Named:
|
|
|
|
case stateIdent.value
|
|
|
|
of StateIdentType.Head:
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.head.toBlockSlot())
|
2021-03-17 18:46:45 +00:00
|
|
|
of StateIdentType.Genesis:
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.getGenesisBlockSlot())
|
2021-03-17 18:46:45 +00:00
|
|
|
of StateIdentType.Finalized:
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.finalizedHead)
|
2021-03-17 18:46:45 +00:00
|
|
|
of StateIdentType.Justified:
|
2021-06-11 17:51:46 +00:00
|
|
|
ok(node.dag.head.atEpochStart(getStateField(
|
|
|
|
node.dag.headState.data, current_justified_checkpoint).epoch))
|
2021-03-17 18:46:45 +00:00
|
|
|
|
|
|
|
proc getBlockDataFromBlockIdent*(node: BeaconNode,
|
|
|
|
id: BlockIdent): Result[BlockData, cstring] =
|
|
|
|
case id.kind
|
|
|
|
of BlockQueryKind.Named:
|
|
|
|
case id.value
|
|
|
|
of BlockIdentType.Head:
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.get(node.dag.head))
|
2021-03-17 18:46:45 +00:00
|
|
|
of BlockIdentType.Genesis:
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.getGenesisBlockData())
|
2021-03-17 18:46:45 +00:00
|
|
|
of BlockIdentType.Finalized:
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.get(node.dag.finalizedHead.blck))
|
2021-03-17 18:46:45 +00:00
|
|
|
of BlockQueryKind.Root:
|
2021-06-01 11:13:40 +00:00
|
|
|
let res = node.dag.get(id.root)
|
2021-03-17 18:46:45 +00:00
|
|
|
if res.isNone():
|
|
|
|
return err("Block not found")
|
|
|
|
ok(res.get())
|
|
|
|
of BlockQueryKind.Slot:
|
|
|
|
let head = ? node.getCurrentHead(id.slot)
|
|
|
|
let blockSlot = head.atSlot(id.slot)
|
|
|
|
if isNil(blockSlot.blck):
|
|
|
|
return err("Block not found")
|
2021-06-01 11:13:40 +00:00
|
|
|
ok(node.dag.get(blockSlot.blck))
|
2021-03-17 18:46:45 +00:00
|
|
|
|
2021-03-23 22:50:18 +00:00
|
|
|
template withStateForBlockSlot*(node: BeaconNode,
|
|
|
|
blockSlot: BlockSlot, body: untyped): untyped =
|
2021-03-17 20:42:55 +00:00
|
|
|
template isState(state: StateData): bool =
|
2021-06-11 17:51:46 +00:00
|
|
|
state.blck.atSlot(getStateField(state.data, slot)) == blockSlot
|
2021-03-17 20:42:55 +00:00
|
|
|
|
2021-06-01 11:13:40 +00:00
|
|
|
if isState(node.dag.headState):
|
|
|
|
withStateVars(node.dag.headState):
|
2021-03-17 20:42:55 +00:00
|
|
|
var cache {.inject.}: StateCache
|
|
|
|
body
|
|
|
|
else:
|
2021-06-01 11:13:40 +00:00
|
|
|
let rpcState = assignClone(node.dag.headState)
|
|
|
|
node.dag.withState(rpcState[], blockSlot):
|
2021-03-17 20:42:55 +00:00
|
|
|
body
|
2021-04-03 00:21:44 +00:00
|
|
|
|
|
|
|
proc toValidatorIndex*(value: RestValidatorIndex): Result[ValidatorIndex,
|
|
|
|
ValidatorIndexError] =
|
|
|
|
when sizeof(ValidatorIndex) == 4:
|
|
|
|
if uint64(value) < VALIDATOR_REGISTRY_LIMIT:
|
2021-04-09 16:12:59 +00:00
|
|
|
# On x86 platform Nim allows only `int32` indexes, so all the indexes in
|
|
|
|
# range `2^31 <= x < 2^32` are not supported.
|
|
|
|
if uint64(value) <= uint64(high(int32)):
|
2021-04-03 00:21:44 +00:00
|
|
|
ok(ValidatorIndex(value))
|
|
|
|
else:
|
|
|
|
err(ValidatorIndexError.UnsupportedValue)
|
|
|
|
else:
|
|
|
|
err(ValidatorIndexError.TooHighValue)
|
|
|
|
elif sizeof(ValidatorIndex) == 8:
|
|
|
|
if uint64(value) < VALIDATOR_REGISTRY_LIMIT:
|
|
|
|
ok(ValidatorIndex(value))
|
|
|
|
else:
|
|
|
|
err(ValidatorIndexError.TooHighValue)
|
|
|
|
else:
|
|
|
|
doAssert(false, "ValidatorIndex type size is incorrect")
|
2021-07-13 11:15:07 +00:00
|
|
|
|
2021-10-14 10:38:38 +00:00
|
|
|
func syncCommitteeParticipants*(forkedState: ForkedHashedBeaconState,
|
|
|
|
epoch: Epoch): Result[seq[ValidatorPubKey], cstring] =
|
|
|
|
withState(forkedState):
|
2021-10-18 16:37:27 +00:00
|
|
|
when stateFork >= BeaconStateFork.Altair:
|
2021-10-14 10:38:38 +00:00
|
|
|
let
|
2021-10-20 16:32:46 +00:00
|
|
|
epochPeriod = sync_committee_period(epoch)
|
|
|
|
curPeriod = sync_committee_period(state.data.slot)
|
|
|
|
if epochPeriod == curPeriod:
|
2021-10-14 10:38:38 +00:00
|
|
|
ok(@(state.data.current_sync_committee.pubkeys.data))
|
2021-10-20 16:32:46 +00:00
|
|
|
elif epochPeriod == curPeriod + 1:
|
2021-10-14 10:38:38 +00:00
|
|
|
ok(@(state.data.next_sync_committee.pubkeys.data))
|
|
|
|
else:
|
|
|
|
err("Epoch is outside the sync committee period of the state")
|
|
|
|
else:
|
|
|
|
err("State's fork do not support sync committees")
|
|
|
|
|
|
|
|
func keysToIndices*(cacheTable: var Table[ValidatorPubKey, ValidatorIndex],
|
|
|
|
forkedState: ForkedHashedBeaconState,
|
|
|
|
keys: openArray[ValidatorPubKey]
|
|
|
|
): seq[Option[ValidatorIndex]] =
|
|
|
|
var indices = newSeq[Option[ValidatorIndex]](len(keys))
|
|
|
|
var keyset =
|
|
|
|
block:
|
|
|
|
var res: Table[ValidatorPubKey, int]
|
|
|
|
for inputIndex, pubkey in keys.pairs():
|
|
|
|
# Try to search in cache first.
|
|
|
|
cacheTable.withValue(pubkey, vindex):
|
|
|
|
indices[inputIndex] = some(vindex[])
|
|
|
|
do:
|
|
|
|
res[pubkey] = inputIndex
|
|
|
|
res
|
|
|
|
if len(keyset) > 0:
|
|
|
|
for validatorIndex, validator in getStateField(forkedState,
|
|
|
|
validators).pairs():
|
|
|
|
keyset.withValue(validator.pubkey, listIndex):
|
|
|
|
# Store pair (pubkey, index) into cache table.
|
|
|
|
cacheTable[validator.pubkey] = ValidatorIndex(validatorIndex)
|
|
|
|
# Fill result sequence.
|
|
|
|
indices[listIndex[]] = some(ValidatorIndex(validatorIndex))
|
|
|
|
indices
|
|
|
|
|
2021-08-03 15:17:11 +00:00
|
|
|
proc getRouter*(): RestRouter =
|
|
|
|
RestRouter.init(validate)
|