Skip to content

Commit

Permalink
adds tests for encoding input. Failing to encode public keys
Browse files Browse the repository at this point in the history
  • Loading branch information
willemolding committed Oct 11, 2023
1 parent 7f0018d commit 28cfa79
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 6 deletions.
21 changes: 21 additions & 0 deletions contracts/src/RotateLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;


library RotateLib {
struct RotateInput {
bytes32 syncCommitteeSSZ;
bytes32 syncCommitteePoseidon;
}

/**
* @notice Compute the public input commitment for the rotation
* This must always match the method used in lightclient-circuits/src/committee_udate_circuit.rs - CommitteeUpdateCircuit::instance()
* @param args The arguments for the sync step
* @return The public input commitment that can be sent to the verifier contract.
*/
function toInputCommitment(RotateInput memory args, bytes32 finalizedHeaderRoot) internal pure returns (uint256[] memory) {
// TODO: Impment this
return new uint256[](0);
}
}
17 changes: 17 additions & 0 deletions contracts/test/RotateExternal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { RotateLib } from "../src/RotateLib.sol";

/**
* @title SyncStepLibTest
* @dev This contract exists solely for the purpose of exposing the SyncStepLib functions
* so they can be used in the Rust test suite. It should not be part of a production deployment
*/
contract RotateExternal {
using RotateLib for RotateLib.RotateInput;

function toInputCommitment(RotateLib.RotateInput calldata args, bytes32 finalizedHeaderRoot) public pure returns (uint256[] memory) {
return args.toInputCommitment(finalizedHeaderRoot);
}
}
1 change: 1 addition & 0 deletions lightclient-circuits/src/committee_update_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ impl<S: Spec, F: Field> CommitteeUpdateCircuit<S, F> {

let instance_vec = iter::once(poseidon_commitment)
.chain(ssz_root.0.map(|b| bn256::Fr::from(b as u64)))
.chain(finalized_header_root.0.map(|b| bn256::Fr::from(b as u64)))
.collect();

vec![instance_vec]
Expand Down
70 changes: 64 additions & 6 deletions lightclient-circuits/tests/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,14 +544,14 @@ mod solidity_tests {
/// Ensure that the instance encoding implemented in Solidity matches exactly the instance encoding expected by the circuit
#[rstest]
#[tokio::test]
async fn test_instance_commitment_evm_equivalence(
async fn test_step_instance_commitment_evm_equivalence(
#[files("../consensus-spec-tests/tests/minimal/capella/light_client/sync/pyspec_tests/**")]
#[exclude("deneb*")]
path: PathBuf,
) -> anyhow::Result<()> {
let (witness, _) = read_test_files_and_gen_witness(path);
let instance = SyncStepCircuit::<Minimal, bn256::Fr>::instance_commitment(&witness);
let poseidon_commitment_le = extract_poseidon_committee_commitment(&witness)?;
let poseidon_commitment_le = extract_poseidon_committee_commitment(&witness.pubkeys_uncompressed)?;

let anvil_instance = Anvil::new().spawn();
let ethclient: Arc<SignerMiddleware<Provider<Http>, _>> = make_client(&anvil_instance);
Expand All @@ -568,6 +568,37 @@ mod solidity_tests {
Ok(())
}

#[rstest]
#[tokio::test]
async fn test_rotate_public_input_evm_equivalence(
#[files("../consensus-spec-tests/tests/minimal/capella/light_client/sync/pyspec_tests/**")]
#[exclude("deneb*")]
path: PathBuf,
) -> anyhow::Result<()> {
let (_, witness) = read_test_files_and_gen_witness(path);
let instance = CommitteeUpdateCircuit::<Minimal, bn256::Fr>::instance(&witness);
let finalized_block_root = witness.finalized_header.clone().hash_tree_root().unwrap().as_bytes().try_into().unwrap();

let anvil_instance = Anvil::new().spawn();
let ethclient: Arc<SignerMiddleware<Provider<Http>, _>> = make_client(&anvil_instance);
let contract = RotateExternal::deploy(ethclient, ())?.send().await?;

let result = contract
.to_input_commitment(RotateInput::from(witness), finalized_block_root)
.call()
.await?;

// convert each of the returned values to a field element
let result_decoded: Vec<_> = result.iter().map(|v| {
let mut b = [0_u8; 32];
v.to_little_endian(&mut b);
bn256::Fr::from_bytes(&b).unwrap()
}).collect();

assert_eq!(vec![result_decoded], instance);
Ok(())
}

abigen!(
SyncStepExternal,
"../contracts/out/SyncStepExternal.sol/SyncStepExternal.json"
Expand Down Expand Up @@ -603,11 +634,38 @@ mod solidity_tests {
}
}

fn extract_poseidon_committee_commitment<Spec: eth_types::Spec>(
witness: &SyncStepArgs<Spec>,
abigen!(
RotateExternal,
"../contracts/out/RotateExternal.sol/RotateExternal.json"
);

// CommitteeRotationArgs type produced by abigen macro matches the solidity struct type
impl<Spec: eth_types::Spec> From<CommitteeRotationArgs<Spec, Fr>> for RotateInput {
fn from(args: CommitteeRotationArgs<Spec, Fr>) -> Self {
let poseidon_commitment_le = extract_poseidon_committee_commitment(&args.pubkeys_compressed).unwrap();

let mut pk_vector: Vector<Vector<u8, 48>, 512> = args
.pubkeys_compressed
.iter()
.cloned()
.map(|v| v.try_into().unwrap())
.collect_vec()
.try_into()
.unwrap();

let sync_committee_ssz = pk_vector.hash_tree_root().unwrap().as_bytes().try_into().unwrap();

RotateInput {
sync_committee_ssz,
sync_committee_poseidon: poseidon_commitment_le,
}
}
}

fn extract_poseidon_committee_commitment(
pubkeys_uncompressed: &Vec<Vec<u8>>
) -> anyhow::Result<[u8; 32]> {
let pubkey_affines = witness
.pubkeys_uncompressed
let pubkey_affines = pubkeys_uncompressed
.iter()
.cloned()
.map(|bytes| {
Expand Down

0 comments on commit 28cfa79

Please sign in to comment.