Skip to content

Commit d56ff4f

Browse files
authored
Clarify #3977 with unbounded uint issue (#4018)
1 parent d7acdcf commit d56ff4f

File tree

17 files changed

+101
-58
lines changed

17 files changed

+101
-58
lines changed

packages/beacon-state-transition/src/allForks/block/isValidIndexedAttestation.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {MAX_VALIDATORS_PER_COMMITTEE} from "@chainsafe/lodestar-params";
22
import {phase0} from "@chainsafe/lodestar-types";
33
import {CachedBeaconStateAllForks} from "../../types";
44
import {verifySignatureSet} from "../../util";
5-
import {getIndexedAttestationBnSignatureSet, getIndexedAttestationSignatureSet} from "../signatureSets";
5+
import {getIndexedAttestationBigintSignatureSet, getIndexedAttestationSignatureSet} from "../signatureSets";
66

77
/**
88
* Check if `indexedAttestation` has sorted and unique indices and a valid aggregate signature.
@@ -23,17 +23,17 @@ export function isValidIndexedAttestation(
2323
}
2424
}
2525

26-
export function isValidIndexedAttestationBn(
26+
export function isValidIndexedAttestationBigint(
2727
state: CachedBeaconStateAllForks,
28-
indexedAttestation: phase0.IndexedAttestationBn,
28+
indexedAttestation: phase0.IndexedAttestationBigint,
2929
verifySignature: boolean
3030
): boolean {
3131
if (!isValidIndexedAttestationIndices(state, indexedAttestation.attestingIndices)) {
3232
return false;
3333
}
3434

3535
if (verifySignature) {
36-
return verifySignatureSet(getIndexedAttestationBnSignatureSet(state, indexedAttestation));
36+
return verifySignatureSet(getIndexedAttestationBigintSignatureSet(state, indexedAttestation));
3737
} else {
3838
return true;
3939
}

packages/beacon-state-transition/src/allForks/block/processAttesterSlashing.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {ForkName} from "@chainsafe/lodestar-params";
44
import {isSlashableValidator, isSlashableAttestationData, getAttesterSlashableIndices} from "../../util";
55
import {CachedBeaconStateAllForks} from "../../types";
66
import {slashValidatorAllForks} from "./slashValidator";
7-
import {isValidIndexedAttestationBn} from "./isValidIndexedAttestation";
7+
import {isValidIndexedAttestationBigint} from "./isValidIndexedAttestation";
88

99
/**
1010
* Process an AttesterSlashing operation. Initiates the exit of a validator, decreases the balance of the slashed
@@ -49,8 +49,11 @@ export function assertValidAttesterSlashing(
4949
throw new Error("AttesterSlashing is not slashable");
5050
}
5151

52+
// In state transition, AttesterSlashing attestations are only partially validated. Their slot and epoch could
53+
// be higher than the clock and the slashing would still be valid. Same applies to attestation data index, which
54+
// can be any arbitrary value. Must use bigint variants to hash correctly to all possible values
5255
for (const [i, attestation] of [attestation1, attestation2].entries()) {
53-
if (!isValidIndexedAttestationBn(state, attestation, verifySignatures)) {
56+
if (!isValidIndexedAttestationBigint(state, attestation, verifySignatures)) {
5457
throw new Error(`AttesterSlashing attestation${i} is invalid`);
5558
}
5659
}

packages/beacon-state-transition/src/allForks/block/processProposerSlashing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function assertValidProposerSlashing(
4444
}
4545

4646
// verify headers are different
47-
if (ssz.phase0.BeaconBlockHeaderBn.equals(header1, header2)) {
47+
if (ssz.phase0.BeaconBlockHeaderBigint.equals(header1, header2)) {
4848
throw new Error("ProposerSlashing headers are equal");
4949
}
5050

packages/beacon-state-transition/src/allForks/signatureSets/attesterSlashings.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ export function getAttesterSlashingSignatureSets(
1919
attesterSlashing: phase0.AttesterSlashing
2020
): ISignatureSet[] {
2121
return [attesterSlashing.attestation1, attesterSlashing.attestation2].map((attestation) =>
22-
getIndexedAttestationBnSignatureSet(state, attestation)
22+
getIndexedAttestationBigintSignatureSet(state, attestation)
2323
);
2424
}
2525

26-
export function getIndexedAttestationBnSignatureSet(
26+
export function getIndexedAttestationBigintSignatureSet(
2727
state: CachedBeaconStateAllForks,
28-
indexedAttestation: phase0.IndexedAttestationBn
28+
indexedAttestation: phase0.IndexedAttestationBigint
2929
): ISignatureSet {
3030
const {index2pubkey} = state.epochCtx;
3131
const slot = computeStartSlotAtEpoch(Number(indexedAttestation.data.target.epoch as bigint));
@@ -34,7 +34,7 @@ export function getIndexedAttestationBnSignatureSet(
3434
return {
3535
type: SignatureSetType.aggregate,
3636
pubkeys: indexedAttestation.attestingIndices.map((i) => index2pubkey[i]),
37-
signingRoot: computeSigningRoot(ssz.phase0.AttestationDataBn, indexedAttestation.data, domain),
37+
signingRoot: computeSigningRoot(ssz.phase0.AttestationDataBigint, indexedAttestation.data, domain),
3838
signature: indexedAttestation.signature,
3939
};
4040
}

packages/beacon-state-transition/src/allForks/signatureSets/proposerSlashings.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ export function getProposerSlashingSignatureSets(
1313
const {epochCtx} = state;
1414
const pubkey = epochCtx.index2pubkey[proposerSlashing.signedHeader1.message.proposerIndex];
1515

16+
// In state transition, ProposerSlashing headers are only partially validated. Their slot could be higher than the
17+
// clock and the slashing would still be valid. Must use bigint variants to hash correctly to all possible values
1618
return [proposerSlashing.signedHeader1, proposerSlashing.signedHeader2].map(
1719
(signedHeader): ISignatureSet => {
1820
const domain = state.config.getDomain(DOMAIN_BEACON_PROPOSER, Number(signedHeader.message.slot as bigint));
1921

2022
return {
2123
type: SignatureSetType.single,
2224
pubkey,
23-
signingRoot: computeSigningRoot(ssz.phase0.BeaconBlockHeaderBn, signedHeader.message, domain),
25+
signingRoot: computeSigningRoot(ssz.phase0.BeaconBlockHeaderBigint, signedHeader.message, domain),
2426
signature: signedHeader.signature,
2527
};
2628
}

packages/beacon-state-transition/src/util/attestation.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import {phase0, Slot, ssz, ValidatorIndex} from "@chainsafe/lodestar-types";
88
/**
99
* Check if [[data1]] and [[data2]] are slashable according to Casper FFG rules.
1010
*/
11-
export function isSlashableAttestationData(data1: phase0.AttestationDataBn, data2: phase0.AttestationDataBn): boolean {
11+
export function isSlashableAttestationData(
12+
data1: phase0.AttestationDataBigint,
13+
data2: phase0.AttestationDataBigint
14+
): boolean {
1215
return (
1316
// Double vote
14-
(!ssz.phase0.AttestationDataBn.equals(data1, data2) && data1.target.epoch === data2.target.epoch) ||
17+
(!ssz.phase0.AttestationDataBigint.equals(data1, data2) && data1.target.epoch === data2.target.epoch) ||
1518
// Surround vote
1619
(data1.source.epoch < data2.source.epoch && data2.target.epoch < data1.target.epoch)
1720
);

packages/beacon-state-transition/test/perf/phase0/block/util.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function getBlockPhase0(
7171
const startIndex = attesterSlashingStartIndex + i * bitsLen * exitedIndexStep;
7272
const attestingIndices = linspace(startIndex, bitsLen, exitedIndexStep);
7373

74-
const attData: phase0.AttestationDataBn = {
74+
const attData: phase0.AttestationDataBigint = {
7575
slot: BigInt(attSlot),
7676
index: BigInt(0),
7777
beaconBlockRoot: rootA,

packages/beacon-state-transition/test/unit/signatureSets/signatureSets.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ interface IBlockProposerData {
8888

8989
function getMockProposerSlashings(data1: IBlockProposerData, data2: IBlockProposerData): phase0.ProposerSlashing {
9090
return {
91-
signedHeader1: getMockSignedBeaconBlockHeaderBn(data1),
92-
signedHeader2: getMockSignedBeaconBlockHeaderBn(data2),
91+
signedHeader1: getMockSignedBeaconBlockHeaderBigint(data1),
92+
signedHeader2: getMockSignedBeaconBlockHeaderBigint(data2),
9393
};
9494
}
9595

96-
function getMockSignedBeaconBlockHeaderBn(data: IBlockProposerData): phase0.SignedBeaconBlockHeaderBn {
96+
function getMockSignedBeaconBlockHeaderBigint(data: IBlockProposerData): phase0.SignedBeaconBlockHeaderBigint {
9797
return {
9898
message: {
9999
slot: BigInt(0),
@@ -118,10 +118,10 @@ function getMockAttesterSlashings(data1: IIndexAttestationData, data2: IIndexAtt
118118
};
119119
}
120120

121-
function getMockIndexAttestationBn(data: IIndexAttestationData): phase0.IndexedAttestationBn {
121+
function getMockIndexAttestationBn(data: IIndexAttestationData): phase0.IndexedAttestationBigint {
122122
return {
123123
attestingIndices: data.attestingIndices,
124-
data: getAttestationDataBn(),
124+
data: getAttestationDataBigint(),
125125
signature: data.signature,
126126
};
127127
}
@@ -136,7 +136,7 @@ function getAttestationData(): phase0.AttestationData {
136136
};
137137
}
138138

139-
function getAttestationDataBn(): phase0.AttestationDataBn {
139+
function getAttestationDataBigint(): phase0.AttestationDataBigint {
140140
return {
141141
slot: BigInt(0),
142142
index: BigInt(0),

packages/beacon-state-transition/test/unit/util/slashing.test.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import {assert} from "chai";
33
import {SLOTS_PER_EPOCH} from "@chainsafe/lodestar-params";
44
import {isSlashableAttestationData} from "../../../src/util";
55
import {randBetween} from "../../utils/misc";
6-
import {generateAttestationDataBn} from "../../utils/attestation";
6+
import {generateAttestationDataBigint} from "../../utils/attestation";
77

88
describe("isSlashableAttestationData", () => {
99
it("Attestation data with the same target epoch should return true", () => {
1010
const epoch1 = randBetween(1, 1000);
1111
const epoch2 = epoch1 + 1;
12-
const a1 = generateAttestationDataBn(epoch1, epoch2);
13-
const a2 = generateAttestationDataBn(epoch1 - 1, epoch2);
12+
const a1 = generateAttestationDataBigint(epoch1, epoch2);
13+
const a2 = generateAttestationDataBigint(epoch1 - 1, epoch2);
1414
assert.isTrue(isSlashableAttestationData(a1, a2));
1515
});
1616

@@ -19,8 +19,8 @@ describe("isSlashableAttestationData", () => {
1919
const epoch2 = epoch1 + 1;
2020
const epoch3 = epoch1 + 2;
2121
const epoch4 = epoch1 + 3;
22-
const a1 = generateAttestationDataBn(epoch1, epoch2);
23-
const a2 = generateAttestationDataBn(epoch3, epoch4);
22+
const a1 = generateAttestationDataBigint(epoch1, epoch2);
23+
const a2 = generateAttestationDataBigint(epoch3, epoch4);
2424
assert.isFalse(isSlashableAttestationData(a1, a2));
2525
});
2626

@@ -32,14 +32,14 @@ describe("isSlashableAttestationData", () => {
3232
const targetEpoch1 = randBetween(1, 1000);
3333
const targetEpoch2 = targetEpoch1 - 1;
3434

35-
const a1 = generateAttestationDataBn(sourceEpoch1, targetEpoch1);
36-
const a2Hi = generateAttestationDataBn(sourceEpoch2Hi, targetEpoch2);
35+
const a1 = generateAttestationDataBigint(sourceEpoch1, targetEpoch1);
36+
const a2Hi = generateAttestationDataBigint(sourceEpoch2Hi, targetEpoch2);
3737

3838
assert.isFalse(isSlashableAttestationData(a1, a2Hi));
3939

4040
// Second attestation has a smaller source epoch.
4141
const sourceEpoch2Lo = sourceEpoch1 - 1;
42-
const a2Lo = generateAttestationDataBn(sourceEpoch2Lo, targetEpoch2);
42+
const a2Lo = generateAttestationDataBigint(sourceEpoch2Lo, targetEpoch2);
4343
assert.isFalse(isSlashableAttestationData(a1, a2Lo));
4444
});
4545

@@ -55,16 +55,16 @@ describe("isSlashableAttestationData", () => {
5555
// First slot in the epoch
5656
let targetSlot2 = (targetEpoch - 1) * SLOTS_PER_EPOCH;
5757

58-
let a1 = generateAttestationDataBn(targetSlot1, sourceEpoch1);
59-
let a2 = generateAttestationDataBn(targetSlot2, sourceEpoch2);
58+
let a1 = generateAttestationDataBigint(targetSlot1, sourceEpoch1);
59+
let a2 = generateAttestationDataBigint(targetSlot2, sourceEpoch2);
6060

6161
assert.isFalse(isSlashableAttestationData(a1, a2));
6262

6363
// Second attestation has a greater target epoch.
6464
targetSlot1 = targetEpoch * SLOTS_PER_EPOCH;
6565
targetSlot2 = (targetEpoch + 1) * SLOTS_PER_EPOCH;
66-
a1 = generateAttestationDataBn(targetSlot1, sourceEpoch1);
67-
a2 = generateAttestationDataBn(targetSlot2, sourceEpoch2);
66+
a1 = generateAttestationDataBigint(targetSlot1, sourceEpoch1);
67+
a2 = generateAttestationDataBigint(targetSlot2, sourceEpoch2);
6868
assert.isFalse(isSlashableAttestationData(a1, a2));
6969
});
7070
});

packages/beacon-state-transition/test/utils/attestation.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function generateAttestationData(sourceEpoch: Epoch, targetEpoch: Epoch):
1818
};
1919
}
2020

21-
export function generateAttestationDataBn(sourceEpoch: Epoch, targetEpoch: Epoch): phase0.AttestationDataBn {
21+
export function generateAttestationDataBigint(sourceEpoch: Epoch, targetEpoch: Epoch): phase0.AttestationDataBigint {
2222
return {
2323
slot: BigInt(0),
2424
index: BigInt(0),

packages/lodestar/test/unit/api/impl/beacon/pool/pool.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {expect} from "chai";
22
import sinon from "sinon";
3-
import {generateAttestationDataBn} from "@chainsafe/lodestar-beacon-state-transition/test/utils/attestation";
3+
import {generateAttestationDataBigint} from "@chainsafe/lodestar-beacon-state-transition/test/utils/attestation";
44
import {getBeaconPoolApi} from "../../../../../../src/api/impl/beacon/pool";
55
import {Network} from "../../../../../../src/network/network";
66
import {
@@ -88,12 +88,12 @@ describe("beacon pool api impl", function () {
8888
const atterterSlashing: phase0.AttesterSlashing = {
8989
attestation1: {
9090
attestingIndices: [0],
91-
data: generateAttestationDataBn(0, 1),
91+
data: generateAttestationDataBigint(0, 1),
9292
signature: Buffer.alloc(96),
9393
},
9494
attestation2: {
9595
attestingIndices: [0],
96-
data: generateAttestationDataBn(0, 1),
96+
data: generateAttestationDataBigint(0, 1),
9797
signature: Buffer.alloc(96),
9898
},
9999
};

packages/lodestar/test/unit/chain/validation/attesterSlashing.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe("GossipMessageValidator", () => {
5353
});
5454

5555
it("should return valid attester slashing", async () => {
56-
const attestationData = ssz.phase0.AttestationDataBn.defaultValue();
56+
const attestationData = ssz.phase0.AttestationDataBigint.defaultValue();
5757
const attesterSlashing: phase0.AttesterSlashing = {
5858
attestation1: {
5959
data: attestationData,

packages/lodestar/test/unit/chain/validation/proposerSlashing.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ describe("validate proposer slashing", () => {
5555
});
5656

5757
it("should return valid proposer slashing", async () => {
58-
const signedHeader1 = ssz.phase0.SignedBeaconBlockHeaderBn.defaultValue();
59-
const signedHeader2 = ssz.phase0.SignedBeaconBlockHeaderBn.defaultValue();
58+
const signedHeader1 = ssz.phase0.SignedBeaconBlockHeaderBigint.defaultValue();
59+
const signedHeader2 = ssz.phase0.SignedBeaconBlockHeaderBigint.defaultValue();
6060
// Make it different, so slashable
6161
signedHeader2.message.stateRoot = Buffer.alloc(32, 1);
6262

packages/lodestar/test/utils/block.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function generateEmptySignedBlockHeader(): phase0.SignedBeaconBlockHeader
7575
};
7676
}
7777

78-
export function generateSignedBlockHeaderBn(): phase0.SignedBeaconBlockHeaderBn {
78+
export function generateSignedBlockHeaderBn(): phase0.SignedBeaconBlockHeaderBigint {
7979
return {
8080
message: {
8181
slot: BigInt(0),

0 commit comments

Comments
 (0)