Skip to content

Commit

Permalink
wip: evm
Browse files Browse the repository at this point in the history
  • Loading branch information
dndll committed Jan 31, 2024
1 parent 90aaab5 commit b17847d
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 419 deletions.
2 changes: 1 addition & 1 deletion circuits/plonky2x/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ version = "0.1.0"
async-trait.workspace = true
borsh.workspace = true
ethers = "2.0.11"
hex.workspace = true
log.workspace = true
pretty_assertions = "1.4.0"
serde.workspace = true
Expand All @@ -21,7 +22,6 @@ near-light-client-rpc.workspace = true

[dev-dependencies]
borsh.workspace = true
hex.workspace = true
near-primitives.workspace = true
pretty_env_logger.workspace = true
serde_json.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion circuits/plonky2x/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,12 @@ impl<L: PlonkParameters<D>, const D: usize> Sync<L, D> for CircuitBuilder<L, D>
let d = self.ensure_stake_is_sufficient(&stake);
self.assertx(d);

// TODO: might not need this now, also the logic is wrong because nbps is always >0
let (next_bps_epoch, next_bps) = if next_block.next_bps.len() > 0 {
// TODO: hashing bps in circut
let e = self.ensure_next_bps_is_valid(
&next_block.header.inner_lite.next_bp_hash,
Some(&next_block.next_bp_hash),
Some(&next_block.next_bps_hash),
);
self.assertx(e);
assert!(next_block.next_bps.len() == NUM_BLOCK_PRODUCER_SEATS);
Expand Down
106 changes: 0 additions & 106 deletions circuits/plonky2x/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,112 +52,6 @@ impl FetchNextHeaderInputs {
}
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FetchBatchInputs<const AMT: usize = 1>(pub Network);

#[async_trait]
impl<L: PlonkParameters<D>, const D: usize, const AMT: usize> AsyncHint<L, D>
for FetchBatchInputs<AMT>
{
async fn hint(
&self,
input_stream: &mut ValueStream<L, D>,
output_stream: &mut ValueStream<L, D>,
) {
let client = NearRpcClient::new(self.0.clone());
let trusted_head = input_stream.read_value::<HeaderVariable>();
let mut head_hash = input_stream.read_value::<CryptoHashVariable>().0;

let trusted_bps = client
.fetch_epoch_bps(&CryptoHash(trusted_head.inner_lite.epoch_id.0))
.await
.unwrap();

let mut headers = vec![trusted_head.clone()];
let mut bps = vec![trusted_bps.clone()];
let mut blocks = vec![];

for i in 0..AMT {
let next = client
.fetch_latest_header(&CryptoHash(head_hash))
.await
.unwrap()
.ok_or_else(|| anyhow!("No header found"))
.unwrap();
blocks.push(next.clone());

let new_head = LightClientBlockLiteView {
prev_block_hash: next.prev_block_hash,
inner_rest_hash: next.inner_rest_hash,
inner_lite: next.inner_lite,
};
head_hash = new_head.hash().0;

if i < AMT - 1 {
headers.push(new_head.into());
bps.push(next.next_bps.unwrap());
}
}

assert_eq!(headers.len(), AMT);
assert_eq!(bps.len(), AMT);
assert_eq!(blocks.len(), AMT);

for h in headers {
log::debug!("headers: {:#?}", h);
output_stream.write_value::<HeaderVariable>(h);
}

for b in bps {
log::debug!("bps: {:?}", b.len());
output_stream
.write_value::<BpsArr<ValidatorStakeVariable>>(bps_to_variable(Some(b)).into());
}

for b in blocks {
log::debug!("blocks: {:#?}", b.inner_lite);
output_stream.write_value::<BlockVariable>(b.into());
}
}
}

#[derive(CircuitVariable, Debug, Clone)]
pub struct HeaderInput {
pub header: HeaderVariable,
pub bps: BpsArr<ValidatorStakeVariable>,
pub block: BlockVariable,
}

impl<const AMT: usize> FetchBatchInputs<AMT> {
pub fn write_inputs(h: &HeaderVariable, hash: &CryptoHashVariable) -> VariableStream {
let mut input_stream = VariableStream::new();
input_stream.write::<HeaderVariable>(h);
input_stream.write::<CryptoHashVariable>(hash);
input_stream
}

pub fn read_outputs<L: PlonkParameters<D>, const D: usize>(
output_stream: &mut OutputVariableStream<L, D>,
b: &mut CircuitBuilder<L, D>,
) -> (
BpsArr<ValidatorStakeVariable>,
ArrayVariable<HeaderInput, AMT>,
) {
let headers = output_stream.read_vec::<HeaderVariable>(b, AMT);
let bps = output_stream.read_vec::<BpsArr<ValidatorStakeVariable>>(b, AMT);
let blocks = output_stream.read_vec::<BlockVariable>(b, AMT);

(
bps[0].clone(),
izip!(headers, bps, blocks)
.into_iter()
.map(|(header, bps, block)| HeaderInput { header, bps, block })
.collect_vec()
.into(),
)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
71 changes: 56 additions & 15 deletions circuits/plonky2x/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
use builder::Sync;
use hint::FetchNextHeaderInputs;
pub use plonky2x::{self, backend::circuit::Circuit, prelude::*};
use variables::{BpsArr, HeaderVariable, ValidatorStakeVariable};
use variables::{
BpsArr, CryptoHashVariable, HashBpsInputs, HeaderVariable, ValidatorStakeVariable,
};
use variables::{BuildEndorsement, EncodeInner, SyncedVariable};

/// Building blocks injected into the CircuitBuilder
Expand Down Expand Up @@ -39,15 +41,24 @@ impl<const AMT: usize> Circuit for SyncCircuit<AMT> {
<<L as PlonkParameters<D>>::Config as plonky2::plonk::config::GenericConfig<D>>::Hasher:
plonky2::plonk::config::AlgebraicHasher<<L as PlonkParameters<D>>::Field>,
{
// TODO: evm
let head = b.read::<HeaderVariable>();
let bps = b.read::<BpsArr<ValidatorStakeVariable>>();

let head_hash = head.hash(b);
let next_block =
FetchNextHeaderInputs(near_light_client_rpc::Network::Testnet).fetch(b, &head_hash);
let synced = b.sync(&head, &bps, &next_block.unwrap());
b.write::<SyncedVariable>(synced);
let network = near_light_client_rpc::Network::Testnet;
let trusted_head = b.evm_read::<HeaderVariable>();

// This is a very interesting cheat to be able to get the BPS for the next epoch
// without the need to store the BPS, we can verify the hash of the BPS in the circuit
let bps = FetchNextHeaderInputs(near_light_client_rpc::Network::Testnet)
.fetch(b, &trusted_head.inner_lite.next_epoch_id)
.unwrap()
.next_bps;
let bps_hash = HashBpsInputs.hash(b, &bps);
b.assert_is_equal(trusted_head.inner_lite.next_bp_hash, bps_hash);

let head_hash = trusted_head.hash(b);
let next_block = FetchNextHeaderInputs(network).fetch(b, &head_hash).unwrap();
b.watch(&bps_hash, "calculate_bps_hash");

let synced = b.sync(&trusted_head, &bps, &next_block);
b.evm_write::<HeaderVariable>(synced.new_head);
}

fn register_generators<L: PlonkParameters<D>, const D: usize>(registry: &mut HintRegistry<L, D>)
Expand All @@ -58,6 +69,7 @@ impl<const AMT: usize> Circuit for SyncCircuit<AMT> {
registry.register_async_hint::<FetchNextHeaderInputs>();
registry.register_hint::<EncodeInner>();
registry.register_hint::<BuildEndorsement>();
registry.register_hint::<HashBpsInputs>();
}
}

Expand All @@ -66,31 +78,60 @@ impl<const AMT: usize> Circuit for SyncCircuit<AMT> {
mod beefy_tests {
use super::*;
use crate::{
test_utils::{builder_suite, test_state, B, PI, PO},
test_utils::{builder_suite, testnet_state, B, PI, PO},
variables::bps_to_variable,
};
use ::test_utils::CryptoHash;
use near_light_client_protocol::{prelude::Itertools, ValidatorStake, ValidatorStakeView};
use near_light_client_rpc::{LightClientRpc, NearRpcClient};
use near_primitives::types::AccountId;
use serial_test::serial;

#[test]
#[serial]
fn beefy_test_sync_e2e() {
const SYNC_AMT: usize = 1;
let (header, bps, _nb) = test_state();
let (header, _, _) = testnet_state();

let define = |b: &mut B| {
b.set_debug();
SyncCircuit::<SYNC_AMT>::define(b);
};
let writer = |input: &mut PI| {
input.write::<HeaderVariable>(header.into());
input.write::<BpsArr<ValidatorStakeVariable>>(bps_to_variable(Some(bps)));
input.evm_write::<HeaderVariable>(header.into());
};
let assertions = |mut output: PO| {
println!("{:#?}", output.read::<SyncedVariable>().new_head);
println!("{:#?}", output.evm_read::<HeaderVariable>());
};
builder_suite(define, writer, assertions);
}

fn account_ids<T: Into<ValidatorStake>>(bps: Vec<T>) -> Vec<AccountId> {
bps.into_iter()
.map(Into::<ValidatorStake>::into)
.map(|x| x.account_id().clone())
.collect_vec()
}

#[tokio::test]
async fn test_epoch_madness() {
use pretty_assertions::assert_eq;
let c = NearRpcClient::new(near_light_client_rpc::Network::Testnet);
let (h, bps, n) = testnet_state();
println!("{:#?}", h);

assert_eq!(h.inner_lite.next_bp_hash, CryptoHash::hash_borsh(&bps));
let bps = account_ids(bps);

let next_epoch = c.fetch_latest_header(&h.inner_lite.next_epoch_id).await;
let ne_nbps = account_ids(next_epoch.unwrap().unwrap().next_bps.unwrap());
assert_eq!(ne_nbps, bps);

let nb_epoch = c.fetch_latest_header(&n.inner_lite.epoch_id).await;
let nb_nbps = account_ids(nb_epoch.unwrap().unwrap().next_bps.unwrap());
assert_eq!(nb_nbps, bps);
}

#[test]
fn test_prove() {}

Expand Down
6 changes: 6 additions & 0 deletions circuits/plonky2x/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ pub fn builder_suite<F, WriteInputs, Assertions>(
writer(&mut inputs);

let (proof, output) = circuit.prove(&inputs);
//proof.public_inputs
std::fs::write(
"input3.bin",
serde_json::to_string(&proof.public_inputs).unwrap(),
)
.unwrap();

assertions(output.clone());

Expand Down
Loading

0 comments on commit b17847d

Please sign in to comment.