diff --git a/beacon_chain/rpc/rest_validator_api.nim b/beacon_chain/rpc/rest_validator_api.nim index c6b994425..5c45dbbf1 100644 --- a/beacon_chain/rpc/rest_validator_api.nim +++ b/beacon_chain/rpc/rest_validator_api.nim @@ -3,7 +3,7 @@ # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * 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, sequtils] +import std/[typetraits, strutils, sets] import stew/[results, base10], chronicles, nimcrypto/utils as ncrutils import ".."/[beacon_chain_db, beacon_node], @@ -11,7 +11,7 @@ import ".."/[beacon_chain_db, beacon_node], ".."/consensus_object_pools/[blockchain_dag, spec_cache, attestation_pool, sync_committee_msg_pool], ".."/validators/validator_duties, - ".."/spec/[forks, network], + ".."/spec/[beaconstate, forks, network], ".."/spec/datatypes/[phase0, altair], "."/rest_utils @@ -190,8 +190,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = let bslot = node.dag.head.atSlot(qepoch.compute_start_slot_at_epoch()) node.withStateForBlockSlot(bslot): - let validatorsCount = lenu64(getStateField(stateData.data, validators)) - let participants = + let syncCommittee = block: let res = syncCommitteeParticipants(stateData().data, qepoch) if res.isErr(): @@ -203,52 +202,20 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = "List of sync committee participants is empty") kres - # TODO: We doing this because `participants` are stored as array of - # validator keys, so we need to convert it to indices, and if any of - # public keys are missing, it means some unexpected error. - let participantIndices = - block: - var res: seq[ValidatorIndex] - let optIndices = keysToIndices(node.restKeysCache, stateData().data, - participants) - for item in optIndices: - if item.isNone(): - return RestApiResponse.jsonError(Http500, InternalServerError, - "Could not get validator indices") - res.add(item.get()) - res - - let validatorsSet = - block: - var res: Table[ValidatorIndex, int] - for listIndex, validatorIndex in indexList.pairs(): - # We ignore indices which could not fit in current list of - # validators. - if uint64(validatorIndex) < validatorsCount: - res[validatorIndex] = listIndex - res - - template isEmpty(duty: RestSyncCommitteeDuty): bool = - len(duty.validator_sync_committee_indices) == 0 - var duties = - block: + withState(stateData().data): var res = newSeq[RestSyncCommitteeDuty](len(indexList)) - for committeeIdx in allSyncSubcommittees(): - for valIndex, arrIndex in syncSubcommitteePairs(participantIndices, - committeeIdx): - let listIndex = validatorsSet.getOrDefault(valIndex, -1) - if listIndex >= 0: - if res[listIndex].isEmpty(): - let key = - getStateField(stateData().data, validators)[valIndex].pubkey - res[listIndex] = RestSyncCommitteeDuty( - validator_index: valIndex, - pubkey: key - ) - res[listIndex].validator_sync_committee_indices.add( - committeeIdx) - res.keepItIf(not(isEmpty(it))) + for resIdx, validatorIdx in indexList: + if not validatorIdx.isValidInState(state.data): + return RestApiResponse.jsonError(Http400, "Invalid index: " & $validatorIdx) + + res[resIdx].pubkey = state.data.validators[validatorIdx].pubkey + res[resIdx].validator_index = validatorIdx + + for idx, pubkey in syncCommittee: + if pubkey == res[resIdx].pubkey: + res[resIdx].validator_sync_committee_indices.add( + ValidatorIndexInSyncCommittee idx) res return RestApiResponse.jsonResponse(duties) diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 6b84ef91a..c1d84e664 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -19,6 +19,9 @@ import export extras, forks, validator +type + SomeBeaconState* = phase0.BeaconState | altair.BeaconState | merge.BeaconState + # https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/phase0/beacon-chain.md#increase_balance func increase_balance*(balance: var Gwei, delta: Gwei) = balance += delta @@ -860,3 +863,7 @@ func upgrade_to_merge*(cfg: RuntimeConfig, pre: altair.BeaconState): # Execution-layer latest_execution_payload_header: ExecutionPayloadHeader() ) + +template isValidInState*(idx: ValidatorIndex, state: SomeBeaconState): bool = + idx.int < state.validators.len + diff --git a/beacon_chain/spec/datatypes/altair.nim b/beacon_chain/spec/datatypes/altair.nim index 50c82691c..e884af046 100644 --- a/beacon_chain/spec/datatypes/altair.nim +++ b/beacon_chain/spec/datatypes/altair.nim @@ -432,6 +432,7 @@ type SomeBeaconBlockBody* = BeaconBlockBody | SigVerifiedBeaconBlockBody | TrustedBeaconBlockBody SyncSubcommitteeIndex* = distinct uint8 + ValidatorIndexInSyncCommittee* = distinct uint16 chronicles.formatIt BeaconBlock: it.shortLog chronicles.formatIt SyncSubcommitteeIndex: uint8(it) @@ -440,7 +441,12 @@ template asInt*(x: SyncSubcommitteeIndex): int = int(x) template asUInt8*(x: SyncSubcommitteeIndex): uint8 = uint8(x) template asUInt64*(x: SyncSubcommitteeIndex): uint64 = uint64(x) -template `[]`*(a: auto; i: SyncSubcommitteeIndex): auto = a[i.asInt] +template `[]`*(a: auto; i: SyncSubcommitteeIndex): auto = + a[i.asInt] + +template `[]`*(arr: array[SYNC_COMMITTEE_SIZE, any] | seq; + idx: ValidatorIndexInSyncCommittee): auto = + arr[int idx] template `==`*(x, y: SyncSubcommitteeIndex): bool = distinctBase(x) == distinctBase(y) diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index 827853c66..7bb6f18c6 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -412,12 +412,12 @@ proc readValue*(reader: var JsonReader[RestJson], value: var Epoch) {. reader.raiseUnexpectedValue($res.error()) ## ValidatorIndex -proc writeValue*(writer: var JsonWriter[RestJson], value: ValidatorIndex) {. - raises: [IOError, Defect].} = +proc writeValue*(writer: var JsonWriter[RestJson], value: ValidatorIndex) + {.raises: [IOError, Defect].} = writeValue(writer, Base10.toString(uint64(value))) -proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorIndex) {. - raises: [IOError, SerializationError, Defect].} = +proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorIndex) + {.raises: [IOError, SerializationError, Defect].} = let svalue = reader.readValue(string) let res = Base10.decode(uint64, svalue) if res.isOk(): @@ -430,6 +430,24 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorIndex) {. else: reader.raiseUnexpectedValue($res.error()) +proc writeValue*(writer: var JsonWriter[RestJson], value: ValidatorIndexInSyncCommittee) + {.raises: [IOError, Defect].} = + writeValue(writer, Base10.toString(distinctBase(value))) + +proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorIndexInSyncCommittee) + {.raises: [IOError, SerializationError, Defect].} = + let svalue = reader.readValue(string) + let res = Base10.decode(uint64, svalue) + if res.isOk(): + let v = res.get() + if v < SYNC_COMMITTEE_SIZE: + value = ValidatorIndexInSyncCommittee(v) + else: + reader.raiseUnexpectedValue( + "Validator index is bigger then SYNC_COMMITTEE_SIZE") + else: + reader.raiseUnexpectedValue($res.error()) + ## RestValidatorIndex proc writeValue*(writer: var JsonWriter[RestJson], value: RestValidatorIndex) {. diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 2a5f7c0aa..bd670fecb 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -105,7 +105,7 @@ type RestSyncCommitteeDuty* = object pubkey*: ValidatorPubKey validator_index*: ValidatorIndex - validator_sync_committee_indices*: seq[SyncSubcommitteeIndex] + validator_sync_committee_indices*: seq[ValidatorIndexInSyncCommittee] RestSyncCommitteeMessage* = object slot*: Slot