Allow duplicate validator identifiers in REST requests. (#2992)

Missing validators are not error anymore.
Fix tests.
This commit is contained in:
Eugene Kabanov 2021-10-18 11:54:20 +03:00 committed by GitHub
parent 75a8fe3b2c
commit 073389377e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 71 deletions

View File

@ -253,9 +253,6 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
for item in validatorIds: for item in validatorIds:
case item.kind case item.kind
of ValidatorQueryKind.Key: of ValidatorQueryKind.Key:
if item.key in keyset:
return RestApiResponse.jsonError(Http400,
UniqueValidatorKeyError)
keyset.incl(item.key) keyset.incl(item.key)
of ValidatorQueryKind.Index: of ValidatorQueryKind.Index:
let vindex = let vindex =
@ -270,27 +267,20 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http500, return RestApiResponse.jsonError(Http500,
UnsupportedValidatorIndexValueError) UnsupportedValidatorIndexValueError)
let index = vres.get() let index = vres.get()
if uint64(index) >= validatorsCount:
return RestApiResponse.jsonError(Http400,
ValidatorNotFoundError)
if index in indexset:
return RestApiResponse.jsonError(Http400,
UniqueValidatorIndexError)
index index
indexset.incl(vindex) if uint64(vindex) < validatorsCount:
# We only adding validator indices which are present in
# validators list at this moment.
indexset.incl(vindex)
if len(keyset) > 0: if len(keyset) > 0:
let optIndices = keysToIndices(node.restKeysCache, stateData.data, let optIndices = keysToIndices(node.restKeysCache, stateData.data,
keyset.toSeq()) keyset.toSeq())
# Remove all the duplicates.
for item in optIndices: for item in optIndices:
if item.isNone(): # We ignore missing keys.
return RestApiResponse.jsonError(Http400, if item.isSome():
ValidatorNotFoundError) indexset.incl(item.get())
let vindex = item.get()
if vindex in indexset:
return RestApiResponse.jsonError(Http400,
UniqueValidatorIndexError)
indexset.incl(vindex)
indexset.toSeq() indexset.toSeq()
let response = let response =
@ -369,7 +359,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let optIndices = keysToIndices(node.restKeysCache, stateData.data, let optIndices = keysToIndices(node.restKeysCache, stateData.data,
[vid.key]) [vid.key])
if optIndices[0].isNone(): if optIndices[0].isNone():
return RestApiResponse.jsonError(Http400, ValidatorNotFoundError) return RestApiResponse.jsonError(Http404, ValidatorNotFoundError)
optIndices[0].get() optIndices[0].get()
of ValidatorQueryKind.Index: of ValidatorQueryKind.Index:
let vres = vid.index.toValidatorIndex() let vres = vid.index.toValidatorIndex()
@ -383,7 +373,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
UnsupportedValidatorIndexValueError) UnsupportedValidatorIndexValueError)
let index = vres.get() let index = vres.get()
if uint64(index) >= validatorsCount: if uint64(index) >= validatorsCount:
return RestApiResponse.jsonError(Http400, ValidatorNotFoundError) return RestApiResponse.jsonError(Http404, ValidatorNotFoundError)
index index
let let
@ -445,9 +435,6 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
for item in validatorIds: for item in validatorIds:
case item.kind case item.kind
of ValidatorQueryKind.Key: of ValidatorQueryKind.Key:
if item.key in keyset:
return RestApiResponse.jsonError(Http400,
UniqueValidatorKeyError)
keyset.incl(item.key) keyset.incl(item.key)
of ValidatorQueryKind.Index: of ValidatorQueryKind.Index:
let vindex = let vindex =
@ -461,28 +448,20 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
of ValidatorIndexError.UnsupportedValue: of ValidatorIndexError.UnsupportedValue:
return RestApiResponse.jsonError(Http500, return RestApiResponse.jsonError(Http500,
UnsupportedValidatorIndexValueError) UnsupportedValidatorIndexValueError)
let index = vres.get() vres.get()
if uint64(index) >= validatorsCount: # We only adding validator indices which are present in
return RestApiResponse.jsonError(Http400, # validators list at this moment.
ValidatorNotFoundError) if uint64(vindex) < validatorsCount:
if index in indexset: indexset.incl(vindex)
return RestApiResponse.jsonError(Http400,
UniqueValidatorIndexError)
index
indexset.incl(vindex)
if len(keyset) > 0: if len(keyset) > 0:
let optIndices = keysToIndices(node.restKeysCache, stateData.data, let optIndices = keysToIndices(node.restKeysCache, stateData.data,
keyset.toSeq()) keyset.toSeq())
# Remove all the duplicates.
for item in optIndices: for item in optIndices:
if item.isNone(): # We ignore missing keys.
return RestApiResponse.jsonError(Http400, if item.isSome():
ValidatorNotFoundError) indexset.incl(item.get())
let vindex = item.get()
if vindex in indexset:
return RestApiResponse.jsonError(Http400,
UniqueValidatorIndexError)
indexset.incl(vindex)
indexset.toSeq() indexset.toSeq()
let response = let response =
@ -648,8 +627,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
var res: seq[ValidatorIndex] var res: seq[ValidatorIndex]
let optIndices = keysToIndices(node.restKeysCache, stateData().data, let optIndices = keysToIndices(node.restKeysCache, stateData().data,
keys) keys)
# Remove all the duplicates.
for item in optIndices: for item in optIndices:
if item.isNone(): if item.isNone():
# This should not be happened, because keys are from state.
return RestApiResponse.jsonError(Http500, InternalServerError, return RestApiResponse.jsonError(Http500, InternalServerError,
"Could not get validator indices") "Could not get validator indices")
res.add(item.get()) res.add(item.get())

View File

@ -108,14 +108,10 @@ const
"Could not find validator" "Could not find validator"
ValidatorStatusNotFoundError* = ValidatorStatusNotFoundError* =
"Could not obtain validator's status" "Could not obtain validator's status"
UniqueValidatorKeyError* =
"Only unique validator's keys are allowed"
TooHighValidatorIndexValueError* = TooHighValidatorIndexValueError* =
"Validator index exceeds maximum number of validators allowed" "Validator index exceeds maximum number of validators allowed"
UnsupportedValidatorIndexValueError* = UnsupportedValidatorIndexValueError* =
"Validator index exceeds maximum supported number of validators" "Validator index exceeds maximum supported number of validators"
UniqueValidatorIndexError* =
"Only unique validator's index are allowed"
StateNotFoundError* = StateNotFoundError* =
"Could not get requested state" "Could not get requested state"
SlotNotFoundError* = SlotNotFoundError* =

View File

@ -3,7 +3,7 @@
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * 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). # * 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. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import std/[typetraits, strutils, sequtils] import std/[typetraits, strutils, sets, sequtils]
import stew/[results, base10], chronicles, json_serialization, import stew/[results, base10], chronicles, json_serialization,
json_serialization/std/[options, net], json_serialization/std/[options, net],
nimcrypto/utils as ncrutils nimcrypto/utils as ncrutils
@ -31,7 +31,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidValidatorIndexValueError, InvalidValidatorIndexValueError,
$dres.error()) $dres.error())
var res: seq[ValidatorIndex] var res: HashSet[ValidatorIndex]
let items = dres.get() let items = dres.get()
for item in items: for item in items:
let vres = item.toValidatorIndex() let vres = item.toValidatorIndex()
@ -43,7 +43,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
of ValidatorIndexError.UnsupportedValue: of ValidatorIndexError.UnsupportedValue:
return RestApiResponse.jsonError(Http500, return RestApiResponse.jsonError(Http500,
UnsupportedValidatorIndexValueError) UnsupportedValidatorIndexValueError)
res.add(vres.get()) res.incl(vres.get())
if len(res) == 0: if len(res) == 0:
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
EmptyValidatorIndexArrayError) EmptyValidatorIndexArrayError)
@ -203,7 +203,8 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
kres kres
# TODO: We doing this because `participants` are stored as array of # TODO: We doing this because `participants` are stored as array of
# validator keys, so we need to convert it to indices. # 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 = let participantIndices =
block: block:
var res: seq[ValidatorIndex] var res: seq[ValidatorIndex]
@ -220,10 +221,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
block: block:
var res: Table[ValidatorIndex, int] var res: Table[ValidatorIndex, int]
for listIndex, validatorIndex in indexList.pairs(): for listIndex, validatorIndex in indexList.pairs():
if uint64(validatorIndex) >= validatorsCount: # We ignore indices which could not fit in current list of
return RestApiResponse.jsonError(Http400, # validators.
ValidatorNotFoundError) if uint64(validatorIndex) < validatorsCount:
res[validatorIndex] = listIndex res[validatorIndex] = listIndex
res res
template isEmpty(duty: RestSyncCommitteeDuty): bool = template isEmpty(duty: RestSyncCommitteeDuty): bool =

View File

@ -672,9 +672,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -685,9 +685,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -698,9 +698,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -880,9 +880,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -893,9 +893,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -997,7 +997,7 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "oneof", "value": ["400", "200"]}, "status": {"operator": "oneof", "value": ["404", "200"]},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
} }
}, },
@ -1155,7 +1155,7 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "404"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}]
} }
@ -1168,7 +1168,7 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "404"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}]
} }
@ -1359,9 +1359,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -1372,9 +1372,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {
@ -1385,9 +1385,9 @@
"headers": {"Accept": "application/json"} "headers": {"Accept": "application/json"}
}, },
"response": { "response": {
"status": {"operator": "equals", "value": "400"}, "status": {"operator": "equals", "value": "200"},
"headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}],
"body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}]
} }
}, },
{ {