Skip to content

Commit

Permalink
Support many attestations (#74)
Browse files Browse the repository at this point in the history
* update script

* fix minor bugs

* commit data

* WIP

* test many attestations

* refactor
  • Loading branch information
nulltea authored Aug 1, 2023
1 parent cb54ee7 commit 19747c5
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 78 deletions.
22 changes: 20 additions & 2 deletions eth-types/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,12 @@ pub trait Spec: 'static + Sized + Copy + Default + Debug {
}
}

/// Ethereum Foundation specifications.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct Test;

impl Spec for Test {
const VALIDATOR_REGISTRY_LIMIT: usize = 100;
const MAX_VALIDATORS_PER_COMMITTEE: usize = 5;
const MAX_VALIDATORS_PER_COMMITTEE: usize = 10;
const MAX_COMMITTEES_PER_SLOT: usize = 1;
const SLOTS_PER_EPOCH: usize = 1;
const VALIDATOR_0_GINDEX: usize = 94557999988736;
Expand All @@ -48,6 +47,25 @@ impl Spec for Test {
type SiganturesCurve = bls12_381::G2;
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct Sepolia;

impl Spec for Sepolia {
const VALIDATOR_REGISTRY_LIMIT: usize = 100;
const MAX_VALIDATORS_PER_COMMITTEE: usize = 10;
const MAX_COMMITTEES_PER_SLOT: usize = 10;
const SLOTS_PER_EPOCH: usize = 1;
const VALIDATOR_0_GINDEX: usize = 94557999988736;
const STATE_TREE_DEPTH: usize = 51;
const STATE_TREE_LEVEL_PUBKEYS: usize = Self::STATE_TREE_DEPTH;
const STATE_TREE_LEVEL_VALIDATORS: usize = Self::STATE_TREE_LEVEL_PUBKEYS - 1;
const STATE_TREE_LEVEL_BEACON_STATE: usize = 6;
const DST: &'static [u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";

type PubKeysCurve = bls12_381::G1;
type SiganturesCurve = bls12_381::G2;
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct Mainnet;

Expand Down
114 changes: 56 additions & 58 deletions preprocessor/scripts/generateInputData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fs from "fs";
import { bls12_381 } from '@noble/curves/bls12-381';
import { bls12_381 } from '@noble/curves/bls12-381'
import {
BitArray,
ContainerType,
Expand All @@ -10,9 +10,9 @@ import {
ssz,
} from "@lodestar/types"
import { createProof, ProofType, MultiProof, Node } from "@chainsafe/persistent-merkle-tree";
import { g1PointToLeBytes as g1PointToBytesLE, g2PointToLeBytes, serialize } from "./util";
import { chunkArray, g1PointToLeBytes as g1PointToBytesLE, g2PointToLeBytes, serialize } from "./util";
import { createNodeFromMultiProofWithTrace, printTrace } from "./merkleTrace";
import { hexToBytes, bytesToHex } from "@noble/curves/abstract/utils";
import { hexToBytes, bytesToHex, numberToBytesBE } from "@noble/curves/abstract/utils";
import { ProjPointType } from "@noble/curves/abstract/weierstrass";
import { createNodeFromCompactMultiProof } from "@chainsafe/persistent-merkle-tree/lib/proof/compactMulti";
import { ValidatorsSsz, Validator, BeaconStateSsz } from "./types";
Expand All @@ -21,21 +21,16 @@ const DST = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";

console.log("VALIDATOR_0_GINDEX:", BeaconStateSsz.getPathInfo(['validators', 0]).gindex);

const N = 5;
const N_validators = parseInt(process.argv[2]) || 5;
const N_committees = parseInt(process.argv[3]) || 1;
let gindices: bigint[] = [];
let validatorBaseGindices: bigint[] = [];

let nonRlcGindices: bigint[] = [];

let privKeyHexes = [
"5644920314564b11404384380c1d677871ada2ec9470d5f43f03aa931ecef54b",
"4314d9849e5cb4add3579426ba8833621dcfeba8f8b33ec8779e76c8facf6b6a",
"6d973b68057d1b01425eb705d9951cf725aa4f138ded6d56fca23d03b7200575",
"3ae65153efe6e1103561cc672aa0044784df0244bf9cae8489fe9ab93120ee70",
"078384584ee0800afb493de39a95955be2132f21fdbf82d2c35a603846cb4cc8"
];
let privKeyHexes: string[] = JSON.parse(fs.readFileSync("../test_data/private_keys.json").toString());

const target_epoch = 25;
const targetEpoch = 25;

//----------------- Beacon state -----------------//

Expand All @@ -46,9 +41,9 @@ beaconState.validators = [];

let pubKeyPoints: ProjPointType<bigint>[] = [];

for (let i = 0; i < N; i++) {
for (let i = 0; i < N_validators * N_committees; i++) {
// use 5 pregenerated private keys to avoid changing JSON files.
let privKey = i < 5 ? hexToBytes(privKeyHexes[i]) : bls12_381.utils.randomPrivateKey();
let privKey = i < privKeyHexes.length ? hexToBytes(privKeyHexes[i]) : bls12_381.utils.randomPrivateKey();
let p = bls12_381.G1.ProjectivePoint.fromPrivateKey(privKey);
let pubkey = g1PointToBytesLE(p, true);

Expand Down Expand Up @@ -83,8 +78,8 @@ fs.writeFileSync(
serialize(Array.from(beaconState.validators.entries()).map(([i, validator]) => ({
id: i,
shufflePos: i,
committee: 0,
isActive: !validator.slashed && validator.activationEpoch <= target_epoch && target_epoch < validator.exitEpoch,
committee: Math.floor(i / N_validators),
isActive: !validator.slashed && validator.activationEpoch <= targetEpoch && targetEpoch < validator.exitEpoch,
isAttested: true,
pubkey: Array.from(validator.pubkey),
pubkeyUncompressed: Array.from(g1PointToBytesLE(pubKeyPoints[i], false)),
Expand All @@ -96,14 +91,16 @@ fs.writeFileSync(
})))
);

//----------------- Committees -----------------//
fs.writeFileSync(
`../test_data/private_keys.json`,
serialize(privKeyHexes)
);

const aggregatedPubKey = bls12_381.aggregatePublicKeys(pubKeyPoints);
const aggPubkeyBytes = g1PointToBytesLE(aggregatedPubKey, false);

let bytesPubkeys = [
Array.from(aggPubkeyBytes),
];
//----------------- Committees -----------------//
const committeePubkeys = chunkArray(pubKeyPoints, N_validators);
const aggregatedPubKeys = committeePubkeys.map((pubKeys) => bls12_381.aggregatePublicKeys(pubKeys));
let bytesPubkeys = aggregatedPubKeys.map((aggPubkey) => Array.from(g1PointToBytesLE(aggPubkey, false)));

fs.writeFileSync(
`../test_data/aggregated_pubkeys.json`,
Expand All @@ -115,44 +112,45 @@ fs.writeFileSync(
type Attestations = ValueOf<typeof ssz.phase0.BeaconBlockBody.fields.attestations>;
let attestations: Attestations = [];

let data = {
slot: 0,
index: 0,
beaconBlockRoot: Uint8Array.from(Array(32).fill(0)),
source: {
epoch: target_epoch - 1,
root: Uint8Array.from(Array(32).fill(0))
},
target: {
epoch: target_epoch,
root: Uint8Array.from(Array(32).fill(0))
}
};

let dataRoot = ssz.phase0.AttestationData.hashTreeRoot(data);

let msgPoint = bls12_381.G2.ProjectivePoint.fromAffine(bls12_381.G2.hashToCurve(dataRoot, {
DST: DST,
}).toAffine());

let signatures = [];
for (const privKey of privKeyHexes) {
const sigPoint = msgPoint.multiply(BigInt('0x' + privKey));
signatures.push(sigPoint);
const beaconStateRoot = BeaconStateSsz.hashTreeRoot(beaconState);
const committeePrivKeys = chunkArray(privKeyHexes, N_validators);

for (let i = 0; i < N_committees; i++) {
let data = {
slot: 32,
index: i,
beaconBlockRoot: Uint8Array.from(Array(32).fill(0)),
source: {
epoch: targetEpoch - 1,
root: Uint8Array.from(Array(32).fill(0))
},
target: {
epoch: targetEpoch,
root: beaconStateRoot,
}
};

let dataRoot = ssz.phase0.AttestationData.hashTreeRoot(data);

let msgPoint = bls12_381.G2.ProjectivePoint.fromAffine(bls12_381.G2.hashToCurve(dataRoot, {
DST: DST,
}).toAffine());

let signatures = committeePrivKeys[i].map((privKey) => msgPoint.multiply(BigInt('0x' + privKey)));
let aggSignature = bls12_381.aggregateSignatures(signatures);

// assert signature is valid
bls12_381.verify(aggSignature, msgPoint, aggregatedPubKeys[i]);

let sigBytes = g2PointToLeBytes(aggSignature, true);

attestations.push({
aggregationBits: BitArray.fromBoolArray(Array(N_validators).fill(1)),
data: data,
signature: sigBytes
});
}

let signature = bls12_381.aggregateSignatures(signatures)

// assert signature is valid
bls12_381.verify(signature, msgPoint, aggregatedPubKey);

let sigBytes = g2PointToLeBytes(signature, true);

attestations.push({
aggregationBits: BitArray.fromBoolArray(Array(N).fill(1)),
data: data,
signature: sigBytes
});

let attestationJson = ssz.phase0.BeaconBlockBody.fields.attestations.toJson(attestations);

Expand Down
8 changes: 8 additions & 0 deletions preprocessor/scripts/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,11 @@ function bigIntToBytesLE(value: bigint): ArrayBuffer {

return buffer;
}

export function chunkArray<T>(arr: T[], chunkSize: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < arr.length; i += chunkSize) {
chunks.push(arr.slice(i, i + chunkSize));
}
return chunks;
}
2 changes: 1 addition & 1 deletion test_data/attestations.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"aggregation_bits":"0x20","data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"24","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"25","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0xc4ed3cb07dea6af7e29d2325e6ca014fcf6087f14aff95077db1f44ab3c559a28eced6412b407cb39a75901d1b7d410bc8ebc51a39e4400d82d26df488836e58dcd85dd9d31e2cda88ca82d83cd244cd45db6501f17a436f41e9156f05b72a85"}]
[{"aggregation_bits":"0x20","data":{"slot":"32","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"24","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"25","root":"0x77bc60ef873b1c2189bd695615bef4b1a4ce6e7749f70d84be36b5b3aff8bdbd"}},"signature":"0x2a0fb48e038894574031eb316b9b7cc5a9d2397862bc185a032be5fd1f76f31b697090a2c9c112ec14f4d71416517304c8c293691cd7ea70f415c095bdc800c22481048d7f57f83ac2c5087947683aa3241863e862f569e6da620e7e3d82cf82"}]
1 change: 1 addition & 0 deletions test_data/private_keys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["5644920314564b11404384380c1d677871ada2ec9470d5f43f03aa931ecef54b","4314d9849e5cb4add3579426ba8833621dcfeba8f8b33ec8779e76c8facf6b6a","6d973b68057d1b01425eb705d9951cf725aa4f138ded6d56fca23d03b7200575","3ae65153efe6e1103561cc672aa0044784df0244bf9cae8489fe9ab93120ee70","078384584ee0800afb493de39a95955be2132f21fdbf82d2c35a603846cb4cc8","59d25ba11238f35255602fcf7ac83b9837b1fbe7e689035c768c54ca4a3ce282","195f06da64e1e2ff662946bd9d7629a190dc497c426b044b887f9f7c3ecdc0ea","300d4aca21cba9142d6b5b3e9d849544f388d34dc6ba0fe777e613af916133b4","60da23b0c5c98767bf584f07bce20e044889ca5bafddbb35b45a306b39447ed0","000650b0abaaad66fe96255f35d31f88e12cd0be91dd62a9d9d9156ef0e8d0c0","2df434973bd420f17ec7d4e587267bed171a80393ff8d4ca3f09a2bb6d31608c","0ba9a63db43f9646f2b201bde96a09b94740df95dd47975e1f300a793c26a059","0cdbbc50e2ce09a14ec1007742d9273608f6d9e8fca2e34067896cb5bf83db07","3f6a7202ffeb9be2216601cace8e0a44be13cb7741d39c78a820ccd7ce98b4f1","718768440b75c5496c8179161cca6b75ab8c038586dee1c1be6ab7420a2f4f24","5672758099f7e0adb0fb5a942057ea6a51ddee05cf2fb25864be0867108a5e35","43a77bcbf8fc1dda16f338dbb64a095a6004490019b20676108905d918d09e5d","1094db7ed6b30287b14a2a270778616ad36cea0f8c596d0fd66e1967d16cb675","4d01394df040462b56ef12eb19e882f55d187ed78c80b22d632906dc5460c98b","16f6f7948e34c2a813278903187274a8c478bca7386c36c4d41765cf3d2ec112","1b66d10e405a196e5f4d8513caaf7fe54e02a467cd1201d0f8698f33d5aeebe7","14eb6118b4d84d36a0d4f89dd6b5cdfc326aa648d3ab266ab711b4d767d77786","140f0e7bb6b5ae3ad718b2b5c58ef650c549487cb2c18256a053e3d2ffcefdc4","36d5975c9a46fdaceacbb744378ab242365a2571b9c82a2c0edfbeb6ae60d64e","3e00e0ca0ad28501fd5071860266caf82f934908e5da80355ee17bca9f58cbc3","4b72aae8fde12fd0c15ff9028fcb0e7475f95abe30bd5ed822a74729597eaad8","34165281201282f6a8f0d0f7917fb401445d157838f2f586478f6f220211779b","2c53bdc8dc2c55d88a217441f223219d41af5e82dd39b0f14fd3da229e898554","1c5746663a0a5c0094c2ebc569fd5210335e88c5e60b1fc0f03f20377cd297e4","53ccc622d2e632485375d2bfa6018b2c8a2bbcb35bdd30df8de0479bb6e619f5","0fe3a14ec1cc214c51fac82bb9d2304f9ed64c41f6b7be796b1c0a5693a88c11","2c1933d8e4a425d9a01c590c4a88c111e1f59626402c82fbf33662839a3604d4","0ff76e507e4833e6058f3daf0143c4ff95976578e9a9e620bbf1b2030d0e47e4","3b8ebecece2b7a2c603c50c9183b286c9c2a18baae023d8d1ed5ff301cb84a38","53c472d80f5b5c802fb1acb398dbd61ee311838462416887a1a23af1d15773ae","0d4f1fa4bbce8d72b5e31b76ce37dc90bbe35b25cc422b71a872ff6955954daa","631cce4e6488aac450cb03899f6e63420592d7d36aca387358c71f13f7e047c9","65c24de0bfbc3775bde749d4f6ac75ffb6639f75b22b33b772c5f40df22ff754","6155edc45095f930006c792697a02bfaaf2870d71a0a9ef37dff45b48e6aa7e6","3eb3cb7e504d416a26215bf0cb839d3a55eed80a4ba4849524c8d3e4e65a8442","31635c71b7f74a16616882f12a7669714cf81444b09723d3fe7196c88edd3ec0","44bdc784b50438b28ba0eaafebd0cb898c7f71f54869f6c15d8baaba3324e1dc","3a58e1b61c2a3efbc3e1bea572f3fa9701865713da74f6679be921a600782f9e","5290d6dce47afabd3fffda909edea31ba02c838b47bc6ea16208961c5bc6bae5","1b86ef2bf007fd7aa1731b28d3a7dc7e980a555d2cc92093149fea1d29907627","3180f2c41bba02d89b3fce0dcae16a05bea902bb1391b041a7cf0f4e9c89b67a","6954f93bba6f226efeeb26c9980b478872693b381643665b7c6b7509601a9c01","2fe35a78c11b02f306d4c40f934b3b3ac89c1a71018dd3b979d71c43978fc065","6b1b36b62699782796da374de9227b3c40d1e1293b0a284d483392ada1e843f9","3fb278a452b070a4512c526dd75f95ab1e9e3065337fba8d570d254f633634f2"]
4 changes: 4 additions & 0 deletions zkcasper-circuits/src/aggregation_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ where
// check that the assigned compressed encoding of pubkey used for constructiong affine point
// is consistent with bytes used in validators table
// assumption: order of self.validators is same as in BeaconState.validators so we treat iterator index as validator id
println!("pubkey_cells.len(): {}", pubkey_cells.len());
for (i, assigned_rlc) in pubkey_rlcs.into_iter().enumerate() {
// convert halo2lib `AssignedValue` into vanilla Halo2 `Cell`
let cells = assigned_rlc
Expand All @@ -157,6 +158,7 @@ where
.map(|&(cell, _)| cell);

// get the corresponding cached cells from the validators table
println!("i: {}", i);
let vs_table_cells =
pubkey_cells.get(i).expect("pubkey cells for validator id");

Expand Down Expand Up @@ -259,6 +261,8 @@ impl<'a, F: Field, S: Spec + Sync> AggregationCircuitBuilder<'a, S, F> {
.zip(self.validators_y.iter())
.group_by(|v| v.0.committee)
.into_iter()
.sorted_by_key(|(committee, _)| *committee)
.take(S::MAX_COMMITTEES_PER_SLOT * S::SLOTS_PER_EPOCH)
.map(|(_, g)| g.into_iter().collect_vec())
.collect_vec();

Expand Down
15 changes: 5 additions & 10 deletions zkcasper-circuits/src/attestations_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use crate::{
HashToCurveCache, HashToCurveChip, Sha256Chip,
},
sha256_circuit::{util::NUM_ROUNDS, Sha256CircuitConfig},
util::{
print_fq2_dev, Challenges, IntoWitness, SubCircuit, SubCircuitBuilder, SubCircuitConfig,
},
util::{Challenges, IntoWitness, SubCircuit, SubCircuitBuilder, SubCircuitConfig},
witness::{self, Attestation, HashInput, HashInputChunk},
};
use eth_types::{AppCurveExt, Field, Spec};
Expand Down Expand Up @@ -128,10 +126,6 @@ where
aggregated_pubkeys: Self::SynthesisArgs,
) -> Result<(), Error> {
assert!(!self.attestations.is_empty(), "no attestations supplied");
assert!(
self.attestations.len() <= S::MAX_COMMITTEES_PER_SLOT * S::SLOTS_PER_EPOCH,
"too many attestations supplied",
);
let mut first_pass = halo2_base::SKIP_FIRST_PASS;

let range = RangeChip::default(config.range.lookup_bits());
Expand Down Expand Up @@ -164,8 +158,6 @@ where
return Ok(());
}

let mut region = region;

let builder = &mut self.builder.borrow_mut();
let ctx = builder.main(0);

Expand Down Expand Up @@ -195,7 +187,10 @@ where

for Attestation::<S> {
data, signature, ..
} in self.attestations.iter()
} in self
.attestations
.iter()
.take(S::MAX_COMMITTEES_PER_SLOT * S::SLOTS_PER_EPOCH)
{
assert!(!signature.is_infinity());

Expand Down
3 changes: 2 additions & 1 deletion zkcasper-circuits/src/table/validators_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ impl ValidatorsTable {
let mut attest_digits_cells = vec![];
for (offset, row) in padded_validators
.iter()
.flat_map(|&v| {
.flat_map(|(committee, v)| {
v.table_assignment::<S, F>(
*committee,
challenge,
&mut attest_digits,
&mut committees_balances,
Expand Down
3 changes: 2 additions & 1 deletion zkcasper-circuits/src/validators_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ impl<F: Field> ValidatorsCircuitConfig<F> {
|| Value::known(F::from(target_epoch)),
)?;

if let Some(&validator) = padded_validators.get(i) {
if let Some((committee, validator)) = padded_validators.get(i) {
target_gte_activation.assign(
region,
offset,
Expand All @@ -394,6 +394,7 @@ impl<F: Field> ValidatorsCircuitConfig<F> {
)?;

let validator_rows = validator.table_assignment::<S, F>(
*committee,
randomness,
&mut attest_digits,
&mut committees_balances,
Expand Down
Loading

0 comments on commit 19747c5

Please sign in to comment.