Skip to content

Commit

Permalink
feat(plonky2): Validators commitment mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimo99 committed Jun 21, 2023
1 parent 3cab47f commit 71431ce
Show file tree
Hide file tree
Showing 13 changed files with 1,369 additions and 3 deletions.
17 changes: 14 additions & 3 deletions beacon-light-client/circom/scripts/validator_balances/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { verifyMerkleProof } from '../../../../libs/typescript/ts-utils/ssz-util
);

const beaconStateSZZ = await fetch(
`http://unstable.prater.beacon-api.nimbus.team/eth/v2/debug/beacon/states/5794336`,
`http://testing.mainnet.beacon-api.nimbus.team/eth/v2/debug/beacon/states/6616005`,
{
headers: {
Accept: 'application/octet-stream',
Expand All @@ -24,8 +24,19 @@ import { verifyMerkleProof } from '../../../../libs/typescript/ts-utils/ssz-util

const beaconState = ssz.capella.BeaconState.deserialize(beaconStateSZZ);

console.log(beaconState.balances.length);
console.log(beaconState.validators.length);
console.log(
BigInt(
'0x' +
bytesToHex(
ssz.phase0.Validator.hashTreeRoot(beaconState.validators[0]),
),
)
.toString(2)
.padStart(256, '0')
.split('')
.map(x => `"${x.toString()}"`)
.join(','),
);

// const beaconStateView = ssz.capella.BeaconState.toViewDU(beaconState);
// const beaconStateTree = new Tree(beaconStateView.node);
Expand Down
17 changes: 17 additions & 0 deletions beacon-light-client/plonky2/Commitment_Mapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Optimizing ZK Proofs for Validator Balances

## Objective

Our goal is to confirm the total of a certain collection of validator balances on-chain, aiming to diminish the amount of required on-chain Merkle proofs. Our current strategy utilizes one zk-proof validation on-chain to ensure that a specific validator set exhibits a certain balance total for a designated beacon state root.

The primary obstacle encountered is the lack of zk-friendliness in the SHA-256 hash function, which is presently employed in our Merkle proofs. The performance deficiency due to the significant computational burden of the SHA-256 makes it unsuitable for our requirements. To illustrate, a single Merkle proof for a validator demands about 43 hashes, whereas the balance tree calls for 39 hashes.

## Suggested Approach

### Handling Validators

We propose to create a commitment mapping from the validators' root (a SHA-256 Merkle tree comprising all validators) to a Poseidon root of validators. This process would involve generating a proof that a given SHA-256 Merkle tree of validators matches a corresponding Poseidon Merkle tree of validators. Given the sheer number of validators, this tree will be formed using recursive proofs. Since only a small fraction of validators changes per epoch, we can economically update and reuse the proofs. The Poseidon hash function, being more zk-friendly, could then be used to validate that a specific validator belongs to the tree in a more cost-effective manner.

### Dealing with Balances

The complexity with balances lies in their tendency to alter with each epoch. Consequently, adopting a similar approach as the one for validators would not be feasible. This is due to the need for recalculating the proofs for all balances from the ground up for each epoch.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { writeFileSync, readFileSync, existsSync } from 'fs';
import { sleep } from '../../../libs/typescript/ts-utils/common-utils';

(async () => {
const { ssz } = await import('@lodestar/types');

let prevSlot = 0;

while (true) {
const beaconStateSZZ = await fetch(
`https://floral-chaotic-sea.discover.quiknode.pro/c1133f4fcc19bbe54fa6e4caf0c05ac79ec7d300/eth/v2/debug/beacon/states/head`,
{
headers: {
Accept: 'application/octet-stream',
},
},
)
.then(response => response.arrayBuffer())
.then(buffer => new Uint8Array(buffer));

const beaconState = ssz.capella.BeaconState.deserialize(beaconStateSZZ);

if (!existsSync('prev_beacon_state.ssz')) {
console.log('prev_beacon_state.ssz does not exist. Creating it.');

prevSlot = beaconState.slot;

writeFileSync(
'prev_beacon_state.ssz',
Buffer.from(beaconStateSZZ),
'binary',
);

await sleep(12000);
continue;
}

console.log(beaconState.slot);

if (prevSlot >= beaconState.slot) {
console.log('Waiting for the next epoch.');
await sleep(12000);
continue;
}

const prevBeaconStateSSZ = new Uint8Array(
readFileSync('prev_beacon_state.ssz'),
);

const prevBeaconState =
ssz.capella.BeaconState.deserialize(prevBeaconStateSSZ);

const balances = beaconState.balances;
const prevBalances = prevBeaconState.balances;

const balancesWithIndices = balances.map((balance, index) => ({
balance: balance,
index,
}));

const changedBalances = balancesWithIndices.filter(
({ balance, index }) =>
prevBalances[index] === undefined || balance !== prevBalances[index],
);

// TODO: push the changed validators to the tree

console.log('#changedBalances', changedBalances.length);

console.log(
'changed indices',
changedBalances.map(({ index }) => index),
);

writeFileSync(
'prev_beacon_state.ssz',
Buffer.from(beaconStateSZZ),
'binary',
);

prevSlot = beaconState.slot;

// wait for the next slot
await sleep(12000);
}
})();
Loading

0 comments on commit 71431ce

Please sign in to comment.