mirror of
https://github.com/logos-blockchain/logos-blockchain-specs.git
synced 2026-01-04 06:03:10 +00:00
cl: swap jubjub for accel k256
This commit is contained in:
parent
89c70ea0e2
commit
a819123bc3
@ -10,9 +10,17 @@ serde = {version="1.0", features = ["derive"]}
|
||||
bincode = "1.3.3"
|
||||
risc0-groth16 = "1.0.1"
|
||||
blake2 = "0.10.6"
|
||||
jubjub = "0.10.0"
|
||||
# jubjub = "0.10.0"
|
||||
group = "0.13.0"
|
||||
rand_core = "0.6.0"
|
||||
rand_chacha = "0.3.1"
|
||||
lazy_static = "1.4.0"
|
||||
hex = "0.4.3"
|
||||
k256 = {version = "0.13.3", features = ["serde", "hash2curve"]}
|
||||
# [dependencies.k256]
|
||||
# git = "https://github.com/risc0/RustCrypto-elliptic-curves"
|
||||
# tag = "k256/v0.13.3-risczero.0"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
use group::{ff::Field, GroupEncoding};
|
||||
use jubjub::{Scalar, SubgroupPoint};
|
||||
use lazy_static::lazy_static;
|
||||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use k256::{
|
||||
elliptic_curve::{
|
||||
group::GroupEncoding, Field
|
||||
},
|
||||
ProjectivePoint, Scalar, AffinePoint
|
||||
};
|
||||
|
||||
|
||||
lazy_static! {
|
||||
static ref PEDERSON_COMMITMENT_BLINDING_POINT: SubgroupPoint =
|
||||
static ref PEDERSON_COMMITMENT_BLINDING_POINT: ProjectivePoint =
|
||||
crate::crypto::hash_to_curve(b"NOMOS_CL_PEDERSON_COMMITMENT_BLINDING");
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct Balance(#[serde(with = "serde_point")] pub SubgroupPoint);
|
||||
pub struct Balance(pub AffinePoint);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct BalanceWitness {
|
||||
pub value: u64,
|
||||
pub unit: String,
|
||||
#[serde(with = "serde_scalar")]
|
||||
pub blinding: Scalar,
|
||||
}
|
||||
|
||||
impl Balance {
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
self.0.to_bytes()
|
||||
pub fn to_bytes(&self) -> [u8; 33] {
|
||||
self.0.to_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,117 +44,119 @@ impl BalanceWitness {
|
||||
}
|
||||
|
||||
pub fn commit(&self) -> Balance {
|
||||
Balance(balance(self.value, &self.unit, self.blinding))
|
||||
Balance(balance(self.value, &self.unit, self.blinding).into())
|
||||
}
|
||||
|
||||
pub fn unit_point(&self) -> SubgroupPoint {
|
||||
pub fn unit_point(&self) -> ProjectivePoint {
|
||||
unit_point(&self.unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unit_point(unit: &str) -> SubgroupPoint {
|
||||
pub fn unit_point(unit: &str) -> ProjectivePoint {
|
||||
crate::crypto::hash_to_curve(unit.as_bytes())
|
||||
}
|
||||
|
||||
pub fn balance(value: u64, unit: &str, blinding: Scalar) -> SubgroupPoint {
|
||||
pub fn balance(value: u64, unit: &str, blinding: Scalar) -> ProjectivePoint {
|
||||
let value_scalar = Scalar::from(value);
|
||||
unit_point(unit) * value_scalar + *PEDERSON_COMMITMENT_BLINDING_POINT * blinding
|
||||
}
|
||||
|
||||
mod serde_scalar {
|
||||
use super::Scalar;
|
||||
use serde::de::{self, Visitor};
|
||||
use serde::{Deserializer, Serializer};
|
||||
use std::fmt;
|
||||
// mod serde_scalar {
|
||||
// use super::Scalar;
|
||||
// use serde::de::{self, Visitor};
|
||||
// use serde::{Deserializer, Serializer};
|
||||
// use std::fmt;
|
||||
|
||||
// Serialize a SubgroupPoint by converting it to bytes.
|
||||
pub fn serialize<S>(scalar: &Scalar, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes = scalar.to_bytes();
|
||||
serializer.serialize_bytes(&bytes)
|
||||
}
|
||||
// // Serialize a SubgroupPoint by converting it to bytes.
|
||||
// pub fn serialize<S>(scalar: &Scalar, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: Serializer,
|
||||
// {
|
||||
// let bytes = scalar.to_bytes();
|
||||
// serializer.serialize_bytes(&bytes)
|
||||
// }
|
||||
|
||||
// Deserialize a SubgroupPoint by converting it from bytes.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Scalar, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct BytesVisitor;
|
||||
// // Deserialize a SubgroupPoint by converting it from bytes.
|
||||
// pub fn deserialize<'de, D>(deserializer: D) -> Result<Scalar, D::Error>
|
||||
// where
|
||||
// D: Deserializer<'de>,
|
||||
// {
|
||||
// struct BytesVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for BytesVisitor {
|
||||
type Value = Scalar;
|
||||
// impl<'de> Visitor<'de> for BytesVisitor {
|
||||
// type Value = Scalar;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a valid Scalar in byte representation")
|
||||
}
|
||||
// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
// formatter.write_str("a valid Scalar in byte representation")
|
||||
// }
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let mut bytes = <jubjub::SubgroupPoint as group::GroupEncoding>::Repr::default();
|
||||
assert_eq!(bytes.len(), v.len());
|
||||
bytes.copy_from_slice(v);
|
||||
// fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
// where
|
||||
// E: de::Error,
|
||||
// {
|
||||
// let mut bytes = <jubjub::SubgroupPoint as group::GroupEncoding>::Repr::default();
|
||||
// assert_eq!(bytes.len(), v.len());
|
||||
// bytes.copy_from_slice(v);
|
||||
|
||||
Ok(Scalar::from_bytes(&bytes).unwrap())
|
||||
}
|
||||
}
|
||||
// Ok(Scalar::from_bytes(&bytes).unwrap())
|
||||
// }
|
||||
// }
|
||||
|
||||
deserializer.deserialize_bytes(BytesVisitor)
|
||||
}
|
||||
}
|
||||
// deserializer.deserialize_bytes(BytesVisitor)
|
||||
// }
|
||||
// }
|
||||
|
||||
mod serde_point {
|
||||
use super::SubgroupPoint;
|
||||
use group::GroupEncoding;
|
||||
use serde::de::{self, Visitor};
|
||||
use serde::{Deserializer, Serializer};
|
||||
use std::fmt;
|
||||
// mod serde_point {
|
||||
// use super::SubgroupPoint;
|
||||
// use group::GroupEncoding;
|
||||
// use serde::de::{self, Visitor};
|
||||
// use serde::{Deserializer, Serializer};
|
||||
// use std::fmt;
|
||||
|
||||
// Serialize a SubgroupPoint by converting it to bytes.
|
||||
pub fn serialize<S>(point: &SubgroupPoint, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes = point.to_bytes();
|
||||
serializer.serialize_bytes(&bytes)
|
||||
}
|
||||
// // Serialize a SubgroupPoint by converting it to bytes.
|
||||
// pub fn serialize<S>(point: &SubgroupPoint, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: Serializer,
|
||||
// {
|
||||
// let bytes = point.to_bytes();
|
||||
// serializer.serialize_bytes(&bytes)
|
||||
// }
|
||||
|
||||
// Deserialize a SubgroupPoint by converting it from bytes.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<SubgroupPoint, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct BytesVisitor;
|
||||
// // Deserialize a SubgroupPoint by converting it from bytes.
|
||||
// pub fn deserialize<'de, D>(deserializer: D) -> Result<SubgroupPoint, D::Error>
|
||||
// where
|
||||
// D: Deserializer<'de>,
|
||||
// {
|
||||
// struct BytesVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for BytesVisitor {
|
||||
type Value = SubgroupPoint;
|
||||
// impl<'de> Visitor<'de> for BytesVisitor {
|
||||
// type Value = SubgroupPoint;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a valid SubgroupPoint in byte representation")
|
||||
}
|
||||
// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
// formatter.write_str("a valid SubgroupPoint in byte representation")
|
||||
// }
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let mut bytes = <jubjub::SubgroupPoint as group::GroupEncoding>::Repr::default();
|
||||
assert_eq!(bytes.len(), v.len());
|
||||
bytes.copy_from_slice(v);
|
||||
// fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
// where
|
||||
// E: de::Error,
|
||||
// {
|
||||
// let mut bytes = <jubjub::SubgroupPoint as group::GroupEncoding>::Repr::default();
|
||||
// assert_eq!(bytes.len(), v.len());
|
||||
// bytes.copy_from_slice(v);
|
||||
|
||||
Ok(SubgroupPoint::from_bytes(&bytes).unwrap())
|
||||
}
|
||||
}
|
||||
// Ok(SubgroupPoint::from_bytes(&bytes).unwrap())
|
||||
// }
|
||||
// }
|
||||
|
||||
// deserializer.deserialize_bytes(BytesVisitor)
|
||||
// }
|
||||
// }
|
||||
|
||||
deserializer.deserialize_bytes(BytesVisitor)
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use crate::test_util::seed_rng;
|
||||
use k256::elliptic_curve::group::prime::PrimeCurveAffine;
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -168,20 +174,20 @@ mod test {
|
||||
#[test]
|
||||
fn test_balance_blinding() {
|
||||
// balances are blinded
|
||||
let r1 = Scalar::from(12);
|
||||
let r2 = Scalar::from(8);
|
||||
let r1 = Scalar::from(12u32);
|
||||
let r2 = Scalar::from(8u32);
|
||||
let a_w = BalanceWitness::new(10, "NMO", r1);
|
||||
let b_w = BalanceWitness::new(10, "NMO", r2);
|
||||
let a = a_w.commit();
|
||||
let b = b_w.commit();
|
||||
assert_ne!(a, b);
|
||||
assert_eq!(a.0 - b.0, BalanceWitness::new(0, "NMO", r1 - r2).commit().0);
|
||||
assert_eq!(a.0.to_curve() - b.0.to_curve(), BalanceWitness::new(0, "NMO", r1 - r2).commit().0.to_curve());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_balance_units() {
|
||||
// Unit's differentiate between values.
|
||||
let r = Scalar::from(1337);
|
||||
let r = Scalar::from(1337u32);
|
||||
let nmo = BalanceWitness::new(10, "NMO", r);
|
||||
let eth = BalanceWitness::new(10, "ETH", r);
|
||||
assert_ne!(nmo.commit(), eth.commit());
|
||||
@ -192,18 +198,18 @@ mod test {
|
||||
let mut rng = seed_rng(0);
|
||||
let r1 = Scalar::random(&mut rng);
|
||||
let r2 = Scalar::random(&mut rng);
|
||||
let ten = BalanceWitness::new(10, "NMO", 0.into());
|
||||
let eight = BalanceWitness::new(8, "NMO", 0.into());
|
||||
let two = BalanceWitness::new(2, "NMO", 0.into());
|
||||
let ten = BalanceWitness::new(10, "NMO", 0u32.into());
|
||||
let eight = BalanceWitness::new(8, "NMO", 0u32.into());
|
||||
let two = BalanceWitness::new(2, "NMO", 0u32.into());
|
||||
|
||||
// Values of same unit are homomorphic
|
||||
assert_eq!(ten.commit().0 - eight.commit().0, two.commit().0);
|
||||
assert_eq!(ten.commit().0.to_curve() - eight.commit().0.to_curve(), two.commit().0.to_curve());
|
||||
|
||||
// Blinding factors are also homomorphic.
|
||||
assert_eq!(
|
||||
BalanceWitness::new(10, "NMO", r1).commit().0
|
||||
- BalanceWitness::new(10, "NMO", r2).commit().0,
|
||||
BalanceWitness::new(0, "NMO", r1 - r2).commit().0
|
||||
BalanceWitness::new(10, "NMO", r1).commit().0.to_curve()
|
||||
- BalanceWitness::new(10, "NMO", r2).commit().0.to_curve(),
|
||||
BalanceWitness::new(0, "NMO", r1 - r2).commit().0.to_curve()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use jubjub::{Scalar, SubgroupPoint};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use k256::{Scalar, ProjectivePoint};
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
note::NoteCommitment,
|
||||
@ -30,7 +31,7 @@ pub struct BundleProof {
|
||||
}
|
||||
|
||||
impl Bundle {
|
||||
pub fn balance(&self) -> SubgroupPoint {
|
||||
pub fn balance(&self) -> ProjectivePoint {
|
||||
self.partials.iter().map(|ptx| ptx.balance()).sum()
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
use blake2::{Blake2s256, Digest};
|
||||
use group::Group;
|
||||
use jubjub::SubgroupPoint;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use rand_core::SeedableRng;
|
||||
use k256::{Secp256k1, ProjectivePoint, elliptic_curve::{
|
||||
hash2curve::{GroupDigest, ExpandMsgXmd},
|
||||
},sha2::Sha256
|
||||
};
|
||||
|
||||
pub fn hash_to_curve(bytes: &[u8]) -> SubgroupPoint {
|
||||
let mut hasher = Blake2s256::new();
|
||||
hasher.update(b"NOMOS_HASH_TO_CURVE");
|
||||
hasher.update(bytes);
|
||||
let seed: [u8; 32] = hasher.finalize().into();
|
||||
SubgroupPoint::random(ChaCha20Rng::from_seed(seed))
|
||||
pub fn hash_to_curve(bytes: &[u8]) -> ProjectivePoint {
|
||||
Secp256k1::hash_from_bytes::<ExpandMsgXmd<Sha256>>(&[bytes], &[b"NOMOS_HASH_TO_CURVE"]).unwrap()
|
||||
}
|
||||
|
||||
@ -105,11 +105,11 @@ impl Input {
|
||||
&& death_constraint_is_satisfied
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 96] {
|
||||
let mut bytes = [0u8; 96];
|
||||
pub fn to_bytes(&self) -> [u8; 97] {
|
||||
let mut bytes = [0u8; 97];
|
||||
bytes[..32].copy_from_slice(self.note_comm.as_bytes());
|
||||
bytes[32..64].copy_from_slice(self.nullifier.as_bytes());
|
||||
bytes[64..96].copy_from_slice(&self.balance.to_bytes());
|
||||
bytes[64..97].copy_from_slice(&self.balance.to_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use blake2::{Blake2s256, Digest};
|
||||
use group::GroupEncoding;
|
||||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use k256::elliptic_curve::group::GroupEncoding;
|
||||
|
||||
use crate::{
|
||||
balance::{Balance, BalanceWitness},
|
||||
|
||||
@ -61,10 +61,10 @@ impl Output {
|
||||
&& self.balance == witness.note.balance()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 64] {
|
||||
let mut bytes = [0u8; 64];
|
||||
pub fn to_bytes(&self) -> [u8; 65] {
|
||||
let mut bytes = [0u8; 65];
|
||||
bytes[..32].copy_from_slice(self.note_comm.as_bytes());
|
||||
bytes[32..64].copy_from_slice(&self.balance.to_bytes());
|
||||
bytes[32..65].copy_from_slice(&self.balance.to_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use jubjub::SubgroupPoint;
|
||||
use rand_core::RngCore;
|
||||
use risc0_groth16::ProofJson;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use k256::ProjectivePoint;
|
||||
use k256::elliptic_curve::group::prime::PrimeCurveAffine;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::input::{Input, InputProof, InputWitness};
|
||||
use crate::merkle;
|
||||
use crate::output::{Output, OutputProof, OutputWitness};
|
||||
|
||||
const MAX_INPUTS: usize = 32;
|
||||
const MAX_OUTPUTS: usize = 32;
|
||||
const MAX_INPUTS: usize = 8;
|
||||
const MAX_OUTPUTS: usize = 8;
|
||||
|
||||
/// The partial transaction commitment couples an input to a partial transaction.
|
||||
/// Prevents partial tx unbundling.
|
||||
@ -157,9 +158,9 @@ impl PartialTx {
|
||||
.all(|(o, p)| o.verify(p))
|
||||
}
|
||||
|
||||
pub fn balance(&self) -> SubgroupPoint {
|
||||
let in_sum: SubgroupPoint = self.inputs.iter().map(|i| i.balance.0).sum();
|
||||
let out_sum: SubgroupPoint = self.outputs.iter().map(|o| o.balance.0).sum();
|
||||
pub fn balance(&self) -> ProjectivePoint {
|
||||
let in_sum: ProjectivePoint = self.inputs.iter().map(|i| i.balance.0.to_curve()).sum();
|
||||
let out_sum: ProjectivePoint = self.outputs.iter().map(|o| o.balance.0.to_curve()).sum();
|
||||
|
||||
out_sum - in_sum
|
||||
}
|
||||
|
||||
@ -9,3 +9,10 @@ opt-level = 3
|
||||
[profile.release]
|
||||
debug = 1
|
||||
lto = true
|
||||
|
||||
[patch.crates-io]
|
||||
# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint
|
||||
# multiplication accelerator support for all downstream usages of the following crates.
|
||||
# sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" }
|
||||
k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.2-risczero.0" }
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
[package]
|
||||
name = "method"
|
||||
version = "0.1.0"
|
||||
|
||||
@ -2,7 +2,6 @@ use blake2::{Blake2s256, Digest};
|
||||
use risc0_zkvm::guest::env;
|
||||
use common::*;
|
||||
use cl::merkle;
|
||||
use cl::note::NoteWitness;
|
||||
use cl::input::InputWitness;
|
||||
use cl::output::OutputWitness;
|
||||
|
||||
@ -24,22 +23,29 @@ fn execute(
|
||||
mut journal: Journal,
|
||||
) {
|
||||
// verify ptx/cl preconditions
|
||||
|
||||
eprintln!("start exec: {}", env::cycle_count());
|
||||
assert_eq!(ptx_root, merkle::node(input_root, output_root));
|
||||
|
||||
eprintln!("ptx_root: {}", env::cycle_count());
|
||||
|
||||
// Glue the zone and the cl together, specifically, it verifies the note requesting
|
||||
// a transfer is included as part of the same transaction in the cl
|
||||
assert!(merkle::verify_path(merkle::leaf(&in_note.commit().to_bytes()), &in_ptx_path, input_root));
|
||||
let in_comm = in_note.commit().to_bytes();
|
||||
eprintln!("input comm: {}", env::cycle_count());
|
||||
|
||||
assert!(merkle::verify_path(merkle::leaf(&in_comm), &in_ptx_path, input_root));
|
||||
eprintln!("input merkle path: {}", env::cycle_count());
|
||||
|
||||
// check the commitments match the actual data
|
||||
let state_cm = calculate_state_hash(&state);
|
||||
let journal_cm = calculate_journal_hash(&journal);
|
||||
let state_root = merkle::node(state_cm, journal_cm);
|
||||
assert_eq!(state_root, in_note.note.state);
|
||||
eprintln!("input state root: {}", env::cycle_count());
|
||||
|
||||
// then run the state transition function
|
||||
let state = stf(state, input);
|
||||
journal.push(input);
|
||||
eprintln!("stf: {}", env::cycle_count());
|
||||
|
||||
// verifying ptx/cl postconditions
|
||||
|
||||
@ -48,11 +54,16 @@ fn execute(
|
||||
let out_state_root = merkle::node(out_state_cm, out_journal_cm);
|
||||
// TODO: verify death constraints are propagated
|
||||
assert_eq!(out_state_root, out_note.note.state);
|
||||
eprintln!("out state root: {}", env::cycle_count());
|
||||
|
||||
// Glue the zone and the cl together, specifically, it verifies an output note
|
||||
// containing the zone state is included as part of the same transaction in the cl
|
||||
// (this is done in the death condition to disallow burning)
|
||||
assert!(merkle::verify_path(merkle::leaf(&out_note.commit().to_bytes()), &out_ptx_path, output_root));
|
||||
let out_comm = out_note.commit().to_bytes();
|
||||
eprintln!("output comm: {}", env::cycle_count());
|
||||
|
||||
assert!(merkle::verify_path(merkle::leaf(&out_comm), &out_ptx_path, output_root));
|
||||
eprintln!("out merkle proof: {}", env::cycle_count());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@ -70,6 +81,7 @@ fn main() {
|
||||
let state: State = env::read();
|
||||
let journal: Journal = env::read();
|
||||
|
||||
eprintln!("parse input: {}", env::cycle_count());
|
||||
execute(ptx_root, input_root, output_root, in_ptx_path, out_ptx_path, in_note, out_note, input, state, journal);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user