nimbus-eth2/beacon_chain/rpc/rest_rewards_api.nim

246 lines
9.1 KiB
Nim
Raw Permalink Normal View History

# beacon_chain
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * 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.
{.push raises: [].}
import
std/[typetraits, sets],
stew/base10,
chronicles, metrics,
./rest_utils,
../beacon_node,
../consensus_object_pools/[blockchain_dag, spec_cache, validator_change_pool],
../spec/[forks, state_transition]
export rest_utils
logScope: topics = "rest_rewardsapi"
func isGenesis(node: BeaconNode,
blockId: BlockIdent,
genesisBsid: BlockSlotId): bool =
case blockId.kind
of BlockQueryKind.Named:
case blockId.value
of BlockIdentType.Genesis:
true
of BlockIdentType.Head:
node.dag.head.bid.slot == GENESIS_SLOT
of BlockIdentType.Finalized:
node.dag.finalizedHead.slot == GENESIS_SLOT
of BlockQueryKind.Slot:
blockId.slot == GENESIS_SLOT
of BlockQueryKind.Root:
blockId.root == genesisBsid.bid.root
proc installRewardsApiHandlers*(router: var RestRouter, node: BeaconNode) =
let
genesisBlockRewardsResponse =
RestApiResponse.prepareJsonResponseFinalized(
(
proposer_index: "0", total: "0", attestations: "0",
sync_aggregate: "0", proposer_slashings: "0", attester_slashings: "0"
),
Opt.some(false),
true,
)
genesisBsid = node.dag.getBlockIdAtSlot(GENESIS_SLOT).get()
# https://ethereum.github.io/beacon-APIs/#/Rewards/getBlockRewards
router.api2(MethodGet, "/eth/v1/beacon/rewards/blocks/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse:
let
bident = block_id.valueOr:
return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError,
$error)
if node.isGenesis(bident, genesisBsid):
return RestApiResponse.response(
genesisBlockRewardsResponse, Http200, "application/json")
let
bdata = node.getForkedBlock(bident).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
bid = BlockId(slot: bdata.slot, root: bdata.root)
targetBlock =
withBlck(bdata):
let parentBid =
node.dag.getBlockId(forkyBlck.message.parent_root).valueOr:
return RestApiResponse.jsonError(Http404, BlockParentUnknownError)
if parentBid.slot >= forkyBlck.message.slot:
return RestApiResponse.jsonError(Http404, BlockOlderThanParentError)
BlockSlotId.init(parentBid, forkyBlck.message.slot)
var
cache = StateCache()
tmpState = assignClone(node.dag.headState)
if not updateState(
Light forward sync mechanism (#6515) * Initial commit. * Add hybrid syncing. * Compilation fixes. * Cast custom event for our purposes. * Instantiate AsyncEventQueue properly. * Fix mistype. * Further research on optimistic updates. * Fixing circular deps. * Add backfilling. * Add block download feature. * Add block store. * Update backfill information before storing block. * Use custom block verifier for backfilling sync. * Skip signature verification in backfilling. * Add one more generic reload to storeBackfillBlock(). * Add block verification debugging statements. * Add more debugging * Do not use database for backfilling, part 1. * Fix for stash. * Stash fixes part 2. * Prepare for testing. * Fix assertion. * Fix post-restart syncing process. * Update backfill loading log statement. Use proper backfill slot callback for sync manager. * Add handling of Duplicates. * Fix store duration and block backfilled log statements. * Add proper syncing state log statement. * Add snappy compression to beaconchain_file. Format syncing speed properly. * Add blobs verification. * Add `slot` number to file structure for easy navigation over stream of compressed objects. * Change database filename. * Fix structure size. * Add more consistency properties. * Fix checkRepair() issues. * Preparation to state rebuild process. * Add plain & compressed size. * Debugging snappy encode process. * Add one more debugging line. * Dump blocks. * One more filedump. * Fix chunk corruption code. * Fix detection issue. * Some fixes in state rebuilding process. * Add more clearance steps. * Move updateHead() back to block_processor. * Fix compilation issues. * Make code more async friendly. * Fix async issues. Add more information when proposer verification failed. * Fix 8192 slots issue. * Fix Future double completion issue. * Pass updateFlags to some of the core procedures. * Fix tests. * Improve initial sync handling mechanism. * Fix checkStateTransition() performance improvements. * Add some performance tuning and meters. * Light client performance tuning. * Remove debugging statement. * Use single file descriptor for blockchain file. * Attempt to fix LC. * Fix timeleft calculation when untrusted sync backfilling started right after LC block received. * Workaround for `chronicles` + `results` `error` issue. Remove some compilation warnings. Fix `CatchableError` leaks on Windows. * Address review comments. * Address review comments part 2. * Address review comments part 1. * Rebase and fix the issues. * Address review comments part 3. * Add tests and fix some issues in auto-repair mechanism. * Add tests to all_tests. * Rename binary test file to pass restrictions. * Add `bin` extension to excluded list. Recover binary test data. * Rename fixture file to .bin again. * Update AllTests. * Address review comments part 4. * Address review comments part 5 and fix tests. * Address review comments part 6. * Eliminate foldl and combine from blobs processing. Add some tests to ensure that checkResponse() also checks for correct order. * Fix forgotten place. * Post rebase fixes. * Add unique slots tests. * Optimize updateHead() code. * Add forgotten changes. * Address review comments on state as argument.
2024-10-30 05:38:53 +00:00
node.dag, tmpState[], targetBlock, false, cache, node.dag.updateFlags):
return RestApiResponse.jsonError(Http404, ParentBlockMissingStateError)
func rollbackProc(state: var ForkedHashedBeaconState) {.
gcsafe, noSideEffect, raises: [].} =
discard
let
rewards =
withBlck(bdata):
state_transition_block(
node.dag.cfg, tmpState[], forkyBlck,
cache, node.dag.updateFlags, rollbackProc).valueOr:
return RestApiResponse.jsonError(Http400, BlockInvalidError)
total = rewards.attestations + rewards.sync_aggregate +
rewards.proposer_slashings + rewards.attester_slashings
proposerIndex =
withBlck(bdata):
forkyBlck.message.proposer_index
RestApiResponse.jsonResponseFinalized(
(
proposer_index: Base10.toString(uint64(proposerIndex)),
total: Base10.toString(uint64(total)),
attestations: Base10.toString(uint64(rewards.attestations)),
sync_aggregate: Base10.toString(uint64(rewards.sync_aggregate)),
proposer_slashings: Base10.toString(uint64(rewards.proposer_slashings)),
attester_slashings: Base10.toString(uint64(rewards.attester_slashings))
),
node.getBlockOptimistic(bdata),
node.dag.isFinalized(bid)
)
# https://ethereum.github.io/beacon-APIs/#/Rewards/getSyncCommitteeRewards
router.api2(
MethodPost, "/eth/v1/beacon/rewards/sync_committee/{block_id}") do (
block_id: BlockIdent,
contentBody: Option[ContentBody]) -> RestApiResponse:
let
idents =
block:
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let res = decodeBody(seq[ValidatorIdent], contentBody.get()).valueOr:
return RestApiResponse.jsonError(
Http400, InvalidRequestBodyError, $error)
res
bident = block_id.valueOr:
return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError,
$error)
bdata = node.getForkedBlock(bident).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
bid = BlockId(slot: bdata.slot, root: bdata.root)
sync_aggregate =
withBlck(bdata):
when consensusFork > ConsensusFork.Phase0:
forkyBlck.message.body.sync_aggregate
else:
default(TrustedSyncAggregate)
targetBlock =
withBlck(bdata):
if node.isGenesis(bident, genesisBsid):
genesisBsid
else:
let parentBid =
node.dag.getBlockId(forkyBlck.message.parent_root).valueOr:
return RestApiResponse.jsonError(
Http404, BlockParentUnknownError)
if parentBid.slot >= forkyBlck.message.slot:
return RestApiResponse.jsonError(
Http404, BlockOlderThanParentError)
BlockSlotId.init(parentBid, forkyBlck.message.slot)
var
cache = StateCache()
tmpState = assignClone(node.dag.headState)
if not updateState(
Light forward sync mechanism (#6515) * Initial commit. * Add hybrid syncing. * Compilation fixes. * Cast custom event for our purposes. * Instantiate AsyncEventQueue properly. * Fix mistype. * Further research on optimistic updates. * Fixing circular deps. * Add backfilling. * Add block download feature. * Add block store. * Update backfill information before storing block. * Use custom block verifier for backfilling sync. * Skip signature verification in backfilling. * Add one more generic reload to storeBackfillBlock(). * Add block verification debugging statements. * Add more debugging * Do not use database for backfilling, part 1. * Fix for stash. * Stash fixes part 2. * Prepare for testing. * Fix assertion. * Fix post-restart syncing process. * Update backfill loading log statement. Use proper backfill slot callback for sync manager. * Add handling of Duplicates. * Fix store duration and block backfilled log statements. * Add proper syncing state log statement. * Add snappy compression to beaconchain_file. Format syncing speed properly. * Add blobs verification. * Add `slot` number to file structure for easy navigation over stream of compressed objects. * Change database filename. * Fix structure size. * Add more consistency properties. * Fix checkRepair() issues. * Preparation to state rebuild process. * Add plain & compressed size. * Debugging snappy encode process. * Add one more debugging line. * Dump blocks. * One more filedump. * Fix chunk corruption code. * Fix detection issue. * Some fixes in state rebuilding process. * Add more clearance steps. * Move updateHead() back to block_processor. * Fix compilation issues. * Make code more async friendly. * Fix async issues. Add more information when proposer verification failed. * Fix 8192 slots issue. * Fix Future double completion issue. * Pass updateFlags to some of the core procedures. * Fix tests. * Improve initial sync handling mechanism. * Fix checkStateTransition() performance improvements. * Add some performance tuning and meters. * Light client performance tuning. * Remove debugging statement. * Use single file descriptor for blockchain file. * Attempt to fix LC. * Fix timeleft calculation when untrusted sync backfilling started right after LC block received. * Workaround for `chronicles` + `results` `error` issue. Remove some compilation warnings. Fix `CatchableError` leaks on Windows. * Address review comments. * Address review comments part 2. * Address review comments part 1. * Rebase and fix the issues. * Address review comments part 3. * Add tests and fix some issues in auto-repair mechanism. * Add tests to all_tests. * Rename binary test file to pass restrictions. * Add `bin` extension to excluded list. Recover binary test data. * Rename fixture file to .bin again. * Update AllTests. * Address review comments part 4. * Address review comments part 5 and fix tests. * Address review comments part 6. * Eliminate foldl and combine from blobs processing. Add some tests to ensure that checkResponse() also checks for correct order. * Fix forgotten place. * Post rebase fixes. * Add unique slots tests. * Optimize updateHead() code. * Add forgotten changes. * Address review comments on state as argument.
2024-10-30 05:38:53 +00:00
node.dag, tmpState[], targetBlock, false, cache, node.dag.updateFlags):
return RestApiResponse.jsonError(Http404, ParentBlockMissingStateError)
let response =
withState(tmpState[]):
let total_active_balance =
get_total_active_balance(forkyState.data, cache)
var resp: seq[RestSyncCommitteeReward]
when consensusFork > ConsensusFork.Phase0:
let
keys =
block:
var res: HashSet[ValidatorPubKey]
for item in idents:
case item.kind
of ValidatorQueryKind.Index:
let vindex = item.index.toValidatorIndex().valueOr:
case error
of ValidatorIndexError.TooHighValue:
return RestApiResponse.jsonError(
Http400, TooHighValidatorIndexValueError)
of ValidatorIndexError.UnsupportedValue:
return RestApiResponse.jsonError(
Http500, UnsupportedValidatorIndexValueError)
if uint64(vindex) >= lenu64(forkyState.data.validators):
return RestApiResponse.jsonError(
Http400, ValidatorNotFoundError)
res.incl(forkyState.data.validators.item(vindex).pubkey)
of ValidatorQueryKind.Key:
res.incl(item.key)
res
committeeKeys =
toHashSet(forkyState.data.current_sync_committee.pubkeys.data)
pubkeyIndices =
block:
var res: Table[ValidatorPubKey, ValidatorIndex]
for vindex in forkyState.data.validators.vindices:
let pubkey = forkyState.data.validators.item(vindex).pubkey
if pubkey in committeeKeys:
res[pubkey] = vindex
res
reward =
block:
let res = uint64(get_participant_reward(total_active_balance))
if res > uint64(high(int64)):
return RestApiResponse.jsonError(
Http500, RewardOverflowError)
res
for i in 0 ..< min(
len(forkyState.data.current_sync_committee.pubkeys),
len(sync_aggregate.sync_committee_bits)):
let
pubkey = forkyState.data.current_sync_committee.pubkeys.data[i]
vindex =
try:
pubkeyIndices[pubkey]
except KeyError:
raiseAssert "Unknown sync committee pubkey encountered!"
vreward =
if sync_aggregate.sync_committee_bits[i]:
cast[int64](reward)
else:
-cast[int64](reward)
if (len(idents) == 0) or (pubkey in keys):
resp.add(RestSyncCommitteeReward(
validator_index: RestValidatorIndex(vindex),
reward: RestReward(vreward)))
resp
RestApiResponse.jsonResponseFinalized(
response,
node.getBlockOptimistic(bdata),
node.dag.isFinalized(bid)
)