Skip to content

Commit

Permalink
chore: add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
twoeths committed May 23, 2023
1 parent dfa0e7e commit 508274f
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 5 deletions.
9 changes: 8 additions & 1 deletion packages/beacon-node/src/chain/validation/attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ export type AttestationOrBytes =
attSlot: Slot;
};

/**
* The beacon chain shufflings are designed to provide 1 epoch lookahead
* At each state, we have previous shuffling, current shuffling and next shuffling
*/
const SHUFFLING_LOOK_AHEAD_EPOCHS = 1;

/**
* Only deserialize the attestation if needed, use the cached AttestationData instead
* This is to avoid deserializing similar attestation multiple times which could help the gc
Expand Down Expand Up @@ -382,7 +388,8 @@ export async function getStateForAttestationVerification(
): Promise<CachedBeaconStateAllForks> {
const isSameFork = chain.config.getForkSeq(attSlot) === chain.config.getForkSeq(attHeadBlock.slot);
// thanks for 1 epoch look ahead of shuffling, a state at epoch n can get committee for epoch n+1
const headStateHasTargetEpochCommmittee = attEpoch - computeEpochAtSlot(attHeadBlock.slot) <= 1;
const headStateHasTargetEpochCommmittee =
attEpoch - computeEpochAtSlot(attHeadBlock.slot) <= SHUFFLING_LOOK_AHEAD_EPOCHS;
try {
if (isSameFork && headStateHasTargetEpochCommmittee) {
// most of the time it should just use head state
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import sinon, {SinonStubbedInstance} from "sinon";
import {expect} from "chai";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {BitArray} from "@chainsafe/ssz";
import {processSlots} from "@lodestar/state-transition";
import {ssz} from "@lodestar/types";
import {computeEpochAtSlot, computeStartSlotAtEpoch, processSlots} from "@lodestar/state-transition";
import {defaultChainConfig, createChainForkConfig, BeaconConfig} from "@lodestar/config";
import {Slot, ssz} from "@lodestar/types";
import {ProtoBlock} from "@lodestar/fork-choice";
import {IBeaconChain} from "../../../../src/chain/index.js";
import {AttestationErrorCode, GossipErrorCode} from "../../../../src/chain/errors/index.js";
import {AttestationOrBytes, validateGossipAttestation} from "../../../../src/chain/validation/index.js";
import {
AttestationOrBytes,
getStateForAttestationVerification,
validateGossipAttestation,
} from "../../../../src/chain/validation/index.js";
import {expectRejectedWithLodestarError} from "../../../utils/errors.js";
import {generateTestCachedBeaconStateOnlyValidators} from "../../../../../state-transition/test/perf/util.js";
import {memoOnce} from "../../../utils/cache.js";
import {getAttestationValidData, AttestationValidDataOpts} from "../../../utils/validationData/attestation.js";
import {IStateRegenerator} from "../../../../src/chain/regen/interface.js";
import {IStateRegenerator, RegenCaller} from "../../../../src/chain/regen/interface.js";
import {StateRegenerator} from "../../../../src/chain/regen/regen.js";
import {ZERO_HASH_HEX} from "../../../../src/constants/constants.js";

describe("chain / validation / attestation", () => {
const vc = 64;
Expand Down Expand Up @@ -281,3 +291,72 @@ describe("chain / validation / attestation", () => {
await expectRejectedWithLodestarError(validateGossipAttestation(chain, attestationOrBytes, subnet), errorCode);
}
});

describe("getStateForAttestationVerification", () => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const config = createChainForkConfig({...defaultChainConfig, CAPELLA_FORK_EPOCH: 2});
const sandbox = sinon.createSandbox();
let regenStub: SinonStubbedInstance<StateRegenerator> & StateRegenerator;
let chain: IBeaconChain;

beforeEach(() => {
regenStub = sandbox.createStubInstance(StateRegenerator) as SinonStubbedInstance<StateRegenerator> &
StateRegenerator;
chain = {
config: config as BeaconConfig,
regen: regenStub,
} as Partial<IBeaconChain> as IBeaconChain;
});

afterEach(() => {
sandbox.restore();
});

const forkSlot = computeStartSlotAtEpoch(config.CAPELLA_FORK_EPOCH);
const getBlockSlotStateTestCases: {id: string; attSlot: Slot; headSlot: Slot; regenCall: keyof StateRegenerator}[] = [
{
id: "should call regen.getBlockSlotState at fork boundary",
attSlot: forkSlot + 1,
headSlot: forkSlot - 1,
regenCall: "getBlockSlotState",
},
{
id: "should call regen.getBlockSlotState if > 1 epoch difference",
attSlot: forkSlot + 2 * SLOTS_PER_EPOCH,
headSlot: forkSlot + 1,
regenCall: "getBlockSlotState",
},
{
id: "should call getState if 1 epoch difference",
attSlot: forkSlot + 2 * SLOTS_PER_EPOCH,
headSlot: forkSlot + SLOTS_PER_EPOCH,
regenCall: "getState",
},
{
id: "should call getState if 0 epoch difference",
attSlot: forkSlot + 2 * SLOTS_PER_EPOCH,
headSlot: forkSlot + 2 * SLOTS_PER_EPOCH,
regenCall: "getState",
},
];

for (const {id, attSlot, headSlot, regenCall} of getBlockSlotStateTestCases) {
it(id, async () => {
const attEpoch = computeEpochAtSlot(attSlot);
const attHeadBlock = {
slot: headSlot,
stateRoot: ZERO_HASH_HEX,
blockRoot: ZERO_HASH_HEX,
} as Partial<ProtoBlock> as ProtoBlock;
expect(regenStub[regenCall].callCount).to.equal(0);
await getStateForAttestationVerification(
chain,
attSlot,
attEpoch,
attHeadBlock,
RegenCaller.validateGossipAttestation
);
expect(regenStub[regenCall].callCount).to.equal(1);
});
}
});

0 comments on commit 508274f

Please sign in to comment.