-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(plonky2): Validators commitment mapper
- Loading branch information
Showing
13 changed files
with
1,369 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
86 changes: 86 additions & 0 deletions
86
beacon-light-client/plonky2/balances_commitment_mapper_tree/get_changed_balances.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
})(); |
Oops, something went wrong.