Repository: berachain/beacon-kit
Pull request #2578: chore(node-api): slot fix and correct slot usage in error
Author: login: nidhi-singh02
Labels: name: Ready for Review
Created: 2025-03-07T08:06:16Z | Merged: 2025-03-09T15:25:22Z

Extending [slot fix](https://github.com/berachain/beacon-kit/pull/2538/files), noticed that due to redefining the same variable name, the error message is not printing the correct slot. 

Please note : Will make the error message verbose in other places as and when we implement them.


Diff:
diff --git a/node-api/backend/backend.go b/node-api/backend/backend.go
index b6143c672c..9ada2aaff5 100644
--- a/node-api/backend/backend.go
+++ b/node-api/backend/backend.go
@@ -21,6 +21,8 @@
 package backend
 
 import (
+	"fmt"
+
 	"github.com/berachain/beacon-kit/chain"
 	"github.com/berachain/beacon-kit/node-core/components/storage"
 	"github.com/berachain/beacon-kit/node-core/types"
@@ -82,42 +84,41 @@ func (b *Backend) GetParentSlotByTimestamp(timestamp math.U64) (math.Slot, error
 // stateFromSlot returns the state at the given slot, after also processing the
 // next slot to ensure the returned beacon state is up to date.
 func (b *Backend) stateFromSlot(slot math.Slot) (*statedb.StateDB, math.Slot, error) {
-	var (
-		st  *statedb.StateDB
-		err error
-	)
-	if st, slot, err = b.stateFromSlotRaw(slot); err != nil {
-		return st, slot, err
+	st, slot, err := b.stateFromSlotRaw(slot)
+	if err != nil {
+		return st, slot, fmt.Errorf("stateFromSlotRaw failed: %w", err)
 	}
 
 	// Process the slot to update the latest state and block roots.
-	if _, err = b.sp.ProcessSlots(st, slot+1); err != nil {
-		return st, slot, err
+	targetSlot := slot + 1
+	if _, err = b.sp.ProcessSlots(st, targetSlot); err != nil {
+		return st, slot, fmt.Errorf("ProcessSlots failed, target slot %d: %w", targetSlot, err)
 	}
 
 	// We need to set the slot on the state back since ProcessSlot will update
 	// it to slot + 1.
-	err = st.SetSlot(slot)
-	return st, slot, err
+	if err = st.SetSlot(slot); err != nil {
+		return st, slot, fmt.Errorf("failed resetting slot to %d: %w", slot, err)
+	}
+	return st, slot, nil
 }
 
 // stateFromSlotRaw returns the state at the given slot using query context,
 // resolving an input slot of 0 to the latest slot. It does not process the
 // next slot on the beacon state.
 func (b *Backend) stateFromSlotRaw(slot math.Slot) (*statedb.StateDB, math.Slot, error) {
-	var st *statedb.StateDB
 	queryCtx, err := b.node.CreateQueryContext(int64(slot), false) // #nosec G115 -- not an issue in practice.
 	if err != nil {
-		return st, slot, err
+		return nil, slot, fmt.Errorf("CreateQueryContext failed: %w", err)
 	}
-	st = b.sb.StateFromContext(queryCtx)
+	st := b.sb.StateFromContext(queryCtx)
 
 	// If using height 0 for the query context, make sure to return the latest slot.
 	if slot == 0 {
 		slot, err = st.GetSlot()
 		if err != nil {
-			return st, slot, err
+			return st, slot, fmt.Errorf("GetSlot failed: %w", err)
 		}
 	}
-	return st, slot, err
+	return st, slot, nil
 }
diff --git a/node-api/backend/block.go b/node-api/backend/block.go
index 1dca030d26..a2d1f059b9 100644
--- a/node-api/backend/block.go
+++ b/node-api/backend/block.go
@@ -22,6 +22,7 @@ package backend
 
 import (
 	ctypes "github.com/berachain/beacon-kit/consensus-types/types"
+	"github.com/berachain/beacon-kit/errors"
 	types "github.com/berachain/beacon-kit/node-api/handlers/beacon/types"
 	"github.com/berachain/beacon-kit/primitives/common"
 	"github.com/berachain/beacon-kit/primitives/math"
@@ -29,27 +30,23 @@ import (
 
 // BlockHeader returns the block header at the given slot.
 func (b Backend) BlockHeaderAtSlot(slot math.Slot) (*ctypes.BeaconBlockHeader, error) {
-	var blockHeader *ctypes.BeaconBlockHeader
-
 	st, _, err := b.stateFromSlot(slot)
 	if err != nil {
-		return blockHeader, err
+		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
-
-	blockHeader, err = st.GetLatestBlockHeader()
-	return blockHeader, err
+	return st.GetLatestBlockHeader()
 }
 
 // GetBlockRoot returns the root of the block at the given stateID.
 func (b Backend) BlockRootAtSlot(slot math.Slot) (common.Root, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
-		return common.Root{}, err
+		return common.Root{}, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
 
 	// As calculated by the beacon chain. Ideally, this logic
 	// should be abstracted by the beacon chain.
-	return st.GetBlockRootAtIndex(slot.Unwrap() % b.cs.SlotsPerHistoricalRoot())
+	return st.GetBlockRootAtIndex(resolvedSlot.Unwrap() % b.cs.SlotsPerHistoricalRoot())
 }
 
 // TODO: Implement this.
diff --git a/node-api/backend/genesis.go b/node-api/backend/genesis.go
index 31888d71ea..a7ea406d2f 100644
--- a/node-api/backend/genesis.go
+++ b/node-api/backend/genesis.go
@@ -21,16 +21,17 @@
 package backend
 
 import (
+	"github.com/berachain/beacon-kit/errors"
 	"github.com/berachain/beacon-kit/primitives/common"
 	"github.com/berachain/beacon-kit/primitives/math"
 )
 
 // GetGenesis returns the genesis state of the beacon chain.
 func (b Backend) GenesisValidatorsRoot(slot math.Slot) (common.Root, error) {
-	// needs genesis_time and gensis_fork_version
+	// needs genesis_time and genesis_fork_version
 	st, _, err := b.stateFromSlot(slot)
 	if err != nil {
-		return common.Root{}, err
+		return common.Root{}, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
 	return st.GetGenesisValidatorsRoot()
 }
diff --git a/node-api/backend/randao.go b/node-api/backend/randao.go
index dd6630824e..d66643eb7b 100644
--- a/node-api/backend/randao.go
+++ b/node-api/backend/randao.go
@@ -21,18 +21,19 @@
 package backend
 
 import (
+	"github.com/berachain/beacon-kit/errors"
 	"github.com/berachain/beacon-kit/primitives/common"
 	"github.com/berachain/beacon-kit/primitives/math"
 )
 
 func (b Backend) RandaoAtEpoch(slot math.Slot, epoch math.Epoch) (common.Bytes32, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
-		return common.Bytes32{}, err
+		return common.Bytes32{}, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
 	// Infer the epoch if not provided.
 	if epoch == 0 {
-		epoch = b.cs.SlotToEpoch(slot)
+		epoch = b.cs.SlotToEpoch(resolvedSlot)
 	}
 	index := epoch.Unwrap() % b.cs.EpochsPerHistoricalVector()
 	return st.GetRandaoMixAtIndex(index)
diff --git a/node-api/backend/state.go b/node-api/backend/state.go
index c27f194537..10c6934928 100644
--- a/node-api/backend/state.go
+++ b/node-api/backend/state.go
@@ -36,14 +36,14 @@ func (b *Backend) StateFromSlotForProof(slot math.Slot) (*statedb.StateDB, math.
 
 // GetStateRoot returns the root of the state at the given slot.
 func (b Backend) StateRootAtSlot(slot math.Slot) (common.Root, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
 		return common.Root{}, err
 	}
 
 	// As calculated by the beacon chain. Ideally, this logic
 	// should be abstracted by the beacon chain.
-	return st.StateRootAtIndex(slot.Unwrap() % b.cs.SlotsPerHistoricalRoot())
+	return st.StateRootAtIndex(resolvedSlot.Unwrap() % b.cs.SlotsPerHistoricalRoot())
 }
 
 // StateAtSlot returns the beacon state at a particular slot.
diff --git a/node-api/backend/validator.go b/node-api/backend/validator.go
index 79b6ecbeb4..e613906ded 100644
--- a/node-api/backend/validator.go
+++ b/node-api/backend/validator.go
@@ -80,7 +80,7 @@ func (f *validatorFilters) parseID(id string) {
 func (b Backend) FilteredValidators(
 	slot math.Slot, ids []string, statuses []string,
 ) ([]*beacontypes.ValidatorData, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
@@ -92,7 +92,7 @@ func (b Backend) FilteredValidators(
 
 	// Parse all IDs and pubkeys once at the start
 	filters := parseValidatorIDs(ids)
-	epoch := b.cs.SlotToEpoch(slot)
+	epoch := b.cs.SlotToEpoch(resolvedSlot)
 
 	return filterAndBuildValidatorData(st, validators, filters, epoch, statuses)
 }
@@ -196,7 +196,7 @@ func buildValidatorData(
 }
 
 func (b Backend) ValidatorByID(slot math.Slot, id string) (*beacontypes.ValidatorData, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
@@ -222,7 +222,7 @@ func (b Backend) ValidatorByID(slot math.Slot, id string) (*beacontypes.Validato
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get validator balance for validator pubkey %s and index %d", validator.GetPubkey(), index)
 	}
-	status, err := validator.Status(b.cs.SlotToEpoch(slot))
+	status, err := validator.Status(b.cs.SlotToEpoch(resolvedSlot))
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get validator status for validator pubkey %s and index %d", validator.GetPubkey(), index)
 	}
@@ -237,7 +237,7 @@ func (b Backend) ValidatorByID(slot math.Slot, id string) (*beacontypes.Validato
 }
 
 func (b Backend) ValidatorBalancesByIDs(slot math.Slot, ids []string) ([]*beacontypes.ValidatorBalanceData, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, _, err := b.stateFromSlot(slot)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}


Reviews:
user: login: nidhi-singh02
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:19:31Z
user: login: nidhi-singh02
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:21:42Z
user: login: nidhi-singh02
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T08:23:42Z
user: login: abi87
body: extended error msgs to address @shotes's comments
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T14:45:58Z

Review comments:
diff_hunk: @@ -237,7 +237,7 @@ func (b Backend) ValidatorByID(slot math.Slot, id string) (*beacontypes.Validato
 }
 
 func (b Backend) ValidatorBalancesByIDs(slot math.Slot, ids []string) ([]*beacontypes.ValidatorBalanceData, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, _, err := b.stateFromSlot(slot)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
path: node-api/backend/validator.go
user: login: nidhi-singh02
body: As the slot returned from func `stateFromSlot` is not used, so skipping it. 
created_at: 2025-03-07T08:19:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,7 +80,7 @@ func (f *validatorFilters) parseID(id string) {
 func (b Backend) FilteredValidators(
 	slot math.Slot, ids []string, statuses []string,
 ) ([]*beacontypes.ValidatorData, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
path: node-api/backend/validator.go
user: login: nidhi-singh02
body: Noticed that the error message was getting overridden by the slot from state (stateFromSlot) instead of the slot passed in, hence renamed the var to `resolvedSlot` to correctly log the error message. 
created_at: 2025-03-07T08:21:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -196,7 +196,7 @@ func buildValidatorData(
 }
 
 func (b Backend) ValidatorByID(slot math.Slot, id string) (*beacontypes.ValidatorData, error) {
-	st, slot, err := b.stateFromSlot(slot)
+	st, resolvedSlot, err := b.stateFromSlot(slot)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to get state from slot %d", slot)
 	}
path: node-api/backend/validator.go
user: login: nidhi-singh02
body: To rectify the error message.
created_at: 2025-03-07T08:23:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-07T08:08:56Z
body: ## [Codecov](https://app.codecov.io/gh/berachain/beacon-kit/pull/2578?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=berachain) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 21.17%. Comparing base [(`dcd27b5`)](https://app.codecov.io/gh/berachain/beacon-kit/commit/dcd27b51712e179f9053d853065aa65ccb6d1c47?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=berachain) to head [(`739c85a`)](https://app.codecov.io/gh/berachain/beacon-kit/commit/739c85a95cff4a45f16f15ea3f8d6175ea23385a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=berachain).
> Report is 1 commits behind head on main.

> :exclamation:  There is a different number of reports uploaded between BASE (dcd27b5) and HEAD (739c85a). Click for more details.
> 
> <details><summary>HEAD has 1 upload less than BASE</summary>
>
>| Flag | BASE (dcd27b5) | HEAD (739c85a) |
>|------|------|------|
>||2|1|
></details>

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/berachain/beacon-kit/pull/2578/graphs/tree.svg?width=650&height=150&src=pr&token=0l5iJ3ZbzV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=berachain)](https://app.codecov.io/gh/berachain/beacon-kit/pull/2578?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=berachain)

```diff
@@             Coverage Diff             @@
##             main    #2578       +/-   ##
===========================================
- Coverage   57.94%   21.17%   -36.77%     
===========================================
  Files         338        3      -335     
  Lines       15122       85    -15037     
  Branches       20       20               
===========================================
- Hits         8762       18     -8744     
+ Misses       5698       66     -5632     
+ Partials      662        1      -661     
```

[see 335 files with indirect coverage changes](https://app.codecov.io/gh/berachain/beacon-kit/pull/2578/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=berachain)

</details>
<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: shotes
created_at: 2025-03-07T18:06:41Z
body: I think I liked the original behavior where it prints out the resolved slot, because that seems like more useful information. But also it's good to clarify the variable names so we know which slot is being used.
author_association: CONTRIBUTOR