remove verbosity, migrate dependencies
This commit is contained in:
parent
d87d815037
commit
6e5cfe211f
@ -6,7 +6,7 @@ homepage = "https://github.com/matterinc/bellman"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "bellman"
|
||||
repository = "https://github.com/matterinc/bellman"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.4"
|
||||
@ -17,7 +17,7 @@ num_cpus = "1"
|
||||
crossbeam = "0.3"
|
||||
pairing = { git = 'https://github.com/matterinc/pairing' }
|
||||
byteorder = "1"
|
||||
ff = { version = "0.4", features = ["derive"] }
|
||||
ff = { git = 'https://github.com/matterinc/ff', features = ["derive"] }
|
||||
pbr = "1.0.1"
|
||||
time = "0.1"
|
||||
|
||||
|
2
src/demo/.gitignore
vendored
2
src/demo/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/target
|
||||
**/*.rs.bk
|
@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "bellman-demo"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.4"
|
||||
hex = "0.3.2"
|
||||
time = "0.1"
|
||||
num-bigint = "0.2"
|
||||
ff = { version = "0.4", features = ["derive"] }
|
||||
bellman = { path = "../.." }
|
||||
pairing = { git = 'https://github.com/matterinc/pairing' }
|
@ -1,42 +0,0 @@
|
||||
# Demo circuits
|
||||
|
||||
This project contains usage demonstration for `bellman` zkSNARK proving framework.
|
||||
We use elliptic curve BN256, for which pairings can be efficiently performed in Ethereum Virtual Machine.
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
.
|
||||
│
|
||||
├── examples
|
||||
│ └── xor.rs: simple XOR circuit demo
|
||||
└── src
|
||||
└── lib.rs: demo contract rendering
|
||||
```
|
||||
|
||||
## Usage:
|
||||
|
||||
```$bash
|
||||
cargo run --example xor
|
||||
cargo run --bin circuit
|
||||
```
|
||||
|
||||
## Verification in EVM contract:
|
||||
|
||||
```$bash
|
||||
cargo run --example xor > demo.sol
|
||||
```
|
||||
|
||||
Now deploy `DemoVerifier` contract from `demo.sol` (e.g. in [remix](https://remix.ethereum.org)) and run method `verify()`.
|
||||
|
||||
## Benchmarking
|
||||
|
||||
```$bash
|
||||
BELLMAN_VERBOSE=1 cargo run --release [num_constraints]
|
||||
```
|
||||
|
||||
`num_constraints` is decimal:
|
||||
|
||||
```$bash
|
||||
BELLMAN_VERBOSE=1 cargo run --release 1000000
|
||||
```
|
@ -1,112 +0,0 @@
|
||||
extern crate bellman;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate ff;
|
||||
extern crate bellman_demo;
|
||||
|
||||
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::{Engine};
|
||||
use pairing::bn256::{Bn256, Fr};
|
||||
|
||||
trait OptionExt<T> {
|
||||
fn grab(&self) -> Result<T, SynthesisError>;
|
||||
}
|
||||
|
||||
impl<T: Copy> OptionExt<T> for Option<T> {
|
||||
fn grab(&self) -> Result<T, SynthesisError> {
|
||||
self.ok_or(SynthesisError::AssignmentMissing)
|
||||
}
|
||||
}
|
||||
|
||||
struct XorCircuit<E: Engine> {
|
||||
a: Option<E::Fr>,
|
||||
b: Option<E::Fr>,
|
||||
c: Option<E::Fr>,
|
||||
}
|
||||
|
||||
// Implementation of our circuit:
|
||||
// Given a bit `c`, prove that we know bits `a` and `b` such that `c = a xor b`
|
||||
impl<E: Engine> Circuit<E> for XorCircuit<E> {
|
||||
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
|
||||
// public input: c
|
||||
// variables (witness): a, b
|
||||
|
||||
// constraint system:
|
||||
// a * a = a
|
||||
// b * b = b
|
||||
// 2a * b = a + b - c
|
||||
|
||||
let a = cs.alloc(|| "a", || self.a.grab())?;
|
||||
|
||||
// a * a = a
|
||||
cs.enforce(|| "a is a boolean", |lc| lc + a, |lc| lc + a, |lc| lc + a);
|
||||
|
||||
let b = cs.alloc(|| "b", || self.b.grab())?;
|
||||
|
||||
// b * b = b
|
||||
cs.enforce(|| "b is a boolean", |lc| lc + b, |lc| lc + b, |lc| lc + b);
|
||||
|
||||
// c = a xor b
|
||||
let c = cs.alloc_input(|| "c", || self.c.grab())?;
|
||||
|
||||
// 2a * b = a + b - c
|
||||
cs.enforce(
|
||||
|| "xor constraint",
|
||||
|lc| lc + (E::Fr::from_str("2").unwrap(), a),
|
||||
|lc| lc + b,
|
||||
|lc| lc + a + b - c,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Create some parameters, create a proof, and verify the proof.
|
||||
fn main() {
|
||||
use rand::thread_rng;
|
||||
|
||||
use bellman::groth16::{
|
||||
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof
|
||||
};
|
||||
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let params = {
|
||||
let c = XorCircuit::<Bn256> {
|
||||
a: None,
|
||||
b: None,
|
||||
c: None,
|
||||
};
|
||||
generate_random_parameters(c, rng).unwrap()
|
||||
};
|
||||
|
||||
let pvk = prepare_verifying_key(¶ms.vk);
|
||||
|
||||
// here we allocate actual variables
|
||||
let c = XorCircuit {
|
||||
a: Some(Fr::one()),
|
||||
b: Some(Fr::zero()),
|
||||
c: Some(Fr::one()),
|
||||
};
|
||||
|
||||
// Create a groth16 proof with our parameters.
|
||||
let proof = create_random_proof(c, ¶ms, rng).unwrap();
|
||||
|
||||
// `inputs` slice contains public parameters encoded as field elements Fr
|
||||
|
||||
// incorrect input tests
|
||||
let inputs = &[Fr::from_str("5").unwrap()];
|
||||
let success = verify_proof(&pvk, &proof, inputs).unwrap();
|
||||
assert!(!success); // fails, because 5 is not 1 or 0
|
||||
|
||||
let inputs = &[Fr::zero()];
|
||||
let success = verify_proof(&pvk, &proof, inputs).unwrap();
|
||||
assert!(!success); // fails because 0 != 0 xor 1
|
||||
|
||||
// correct input test
|
||||
let inputs = &[Fr::one()];
|
||||
let success = verify_proof(&pvk, &proof, inputs).unwrap();
|
||||
assert!(success);
|
||||
println!("{}", bellman_demo::verifier_contract::generate_demo_contract(¶ms.vk, &proof, inputs, ""));
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
extern crate bellman;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate hex;
|
||||
extern crate ff;
|
||||
|
||||
pub mod verifier_contract;
|
@ -1,164 +0,0 @@
|
||||
// Library to generate a demo EVM verifier contract
|
||||
|
||||
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::{Engine, CurveAffine, EncodedPoint};
|
||||
use bellman::groth16;
|
||||
use pairing::bn256::{Bn256, Fr};
|
||||
use std::fmt;
|
||||
|
||||
pub fn generate_demo_contract<E: Engine>(vk: &groth16::VerifyingKey<E>, proof: &groth16::Proof<E>, inputs: &[E::Fr], inputs_extra: &str) -> String {
|
||||
format!("{}{}", STANDARD_VERIFIER, demo_verifier(vk, proof, inputs, inputs_extra))
|
||||
}
|
||||
|
||||
const STANDARD_VERIFIER: &str =
|
||||
r#"pragma solidity ^0.4.24;
|
||||
|
||||
// from https://github.com/HarryR/ethsnarks/blob/master/contracts/Verifier.sol
|
||||
contract Verifier {
|
||||
|
||||
function NegateY( uint256 Y ) internal pure returns (uint256) {
|
||||
uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
return q - (Y % q);
|
||||
}
|
||||
|
||||
function Verify ( uint256[14] in_vk, uint256[] vk_gammaABC, uint256[8] in_proof, uint256[] proof_inputs ) internal view returns (bool) {
|
||||
require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length );
|
||||
|
||||
// Compute the linear combination vk_x
|
||||
uint256[3] memory mul_input;
|
||||
uint256[4] memory add_input;
|
||||
bool success;
|
||||
uint m = 2;
|
||||
|
||||
// First two fields are used as the sum
|
||||
add_input[0] = vk_gammaABC[0];
|
||||
add_input[1] = vk_gammaABC[1];
|
||||
|
||||
// Performs a sum of gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ]
|
||||
for (uint i = 0; i < proof_inputs.length; i++) {
|
||||
mul_input[0] = vk_gammaABC[m++];
|
||||
mul_input[1] = vk_gammaABC[m++];
|
||||
mul_input[2] = proof_inputs[i];
|
||||
|
||||
assembly {
|
||||
// ECMUL, output to last 2 elements of `add_input`
|
||||
success := staticcall(sub(gas, 2000), 7, mul_input, 0x80, add(add_input, 0x40), 0x60)
|
||||
}
|
||||
require( success );
|
||||
|
||||
assembly {
|
||||
// ECADD
|
||||
success := staticcall(sub(gas, 2000), 6, add_input, 0xc0, add_input, 0x60)
|
||||
}
|
||||
require( success );
|
||||
}
|
||||
|
||||
uint[24] memory input = [
|
||||
// (proof.A, proof.B)
|
||||
in_proof[0], in_proof[1], // proof.A (G1)
|
||||
in_proof[2], in_proof[3], in_proof[4], in_proof[5], // proof.B (G2)
|
||||
|
||||
// (-vk.alpha, vk.beta)
|
||||
in_vk[0], NegateY(in_vk[1]), // -vk.alpha (G1)
|
||||
in_vk[2], in_vk[3], in_vk[4], in_vk[5], // vk.beta (G2)
|
||||
|
||||
// (-vk_x, vk.gamma)
|
||||
add_input[0], NegateY(add_input[1]), // -vk_x (G1)
|
||||
in_vk[6], in_vk[7], in_vk[8], in_vk[9], // vk.gamma (G2)
|
||||
|
||||
// (-proof.C, vk.delta)
|
||||
in_proof[6], NegateY(in_proof[7]), // -proof.C (G1)
|
||||
in_vk[10], in_vk[11], in_vk[12], in_vk[13] // vk.delta (G2)
|
||||
];
|
||||
|
||||
uint[1] memory out;
|
||||
assembly {
|
||||
success := staticcall(sub(gas, 2000), 8, input, 768, out, 0x20)
|
||||
}
|
||||
require(success);
|
||||
return out[0] != 0;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
fn demo_verifier<E: Engine>(vk: &groth16::VerifyingKey<E>, proof: &groth16::Proof<E>, inputs: &[E::Fr], inputs_extra: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
contract DemoVerifier is Verifier {{
|
||||
|
||||
function getVk() internal view returns (uint256[14] memory vk, uint256[] memory gammaABC) {{
|
||||
{vk}
|
||||
}}
|
||||
|
||||
function getProof() internal view returns (uint256[8] memory proof) {{
|
||||
{proof}
|
||||
}}
|
||||
|
||||
function getInputs() internal view returns (uint256[] memory inputs) {{
|
||||
{inputs}
|
||||
{inputs_extra}
|
||||
}}
|
||||
|
||||
function verify( ) public view returns (bool) {{
|
||||
var (vk, gammaABC) = getVk();
|
||||
return Verify(vk, gammaABC, getProof(), getInputs());
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
vk = hardcode_vk(vk),
|
||||
proof = hardcode_proof(proof),
|
||||
inputs = hardcode_inputs::<E>(inputs),
|
||||
inputs_extra = inputs_extra)
|
||||
}
|
||||
|
||||
fn unpack<T: CurveAffine>(t: &T) -> Vec<String>
|
||||
{
|
||||
t.into_uncompressed().as_ref().chunks(32).map(|c| "0x".to_owned() + &hex::encode(c)).collect()
|
||||
}
|
||||
|
||||
const SHIFT: &str = " ";
|
||||
|
||||
fn render_array(name: &str, allocate: bool, values: &[Vec<String>]) -> String {
|
||||
let mut out = String::new();
|
||||
out.push('\n');
|
||||
let flattened: Vec<&String> = values.into_iter().flatten().collect();
|
||||
if allocate {
|
||||
out.push_str(&format!("{}{} = new uint256[]({});\n", SHIFT, name, flattened.len()));
|
||||
}
|
||||
for (i, s) in flattened.iter().enumerate() {
|
||||
out.push_str(&format!("{}{}[{}] = {};\n", SHIFT, name, i, s));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn hardcode_vk<E: Engine>(vk: &groth16::VerifyingKey<E>) -> String {
|
||||
let mut out = String::new();
|
||||
|
||||
let values = &[
|
||||
unpack(&vk.alpha_g1),
|
||||
unpack(&vk.beta_g2),
|
||||
unpack(&vk.gamma_g2),
|
||||
unpack(&vk.delta_g2),
|
||||
];
|
||||
out.push_str(&render_array("vk", false, values));
|
||||
|
||||
let ic: Vec<Vec<String>> = vk.ic.iter().map(unpack).collect();
|
||||
out.push_str(&render_array("gammaABC", true, ic.as_slice()));
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn hardcode_proof<E: Engine>(proof: &groth16::Proof<E>) -> String {
|
||||
let values = &[
|
||||
unpack(&proof.a),
|
||||
unpack(&proof.b),
|
||||
unpack(&proof.c),
|
||||
];
|
||||
render_array("proof", false, values)
|
||||
}
|
||||
|
||||
fn hardcode_inputs<E: Engine>(inputs: &[E::Fr]) -> String {
|
||||
let values: Vec<Vec<String>> = inputs.iter().map(|i| {vec!(format!("{}", inputs[0].into_repr()))}).collect();
|
||||
render_array("inputs", true, values.as_slice())
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
extern crate time;
|
||||
|
||||
extern crate pbr;
|
||||
|
||||
use super::super::verbose_flag;
|
||||
|
||||
use self::time::PreciseTime;
|
||||
//use self::pbr::{MultiBar};
|
||||
use super::super::progress_bar::{MultiBar};
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
@ -182,8 +178,6 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
||||
}
|
||||
}
|
||||
|
||||
const MIN_STEP: u64 = 1000;
|
||||
|
||||
/// Create parameters for a circuit, given some toxic waste.
|
||||
pub fn generate_parameters<E, C>(
|
||||
circuit: C,
|
||||
@ -227,12 +221,11 @@ pub fn generate_parameters<E, C>(
|
||||
);
|
||||
}
|
||||
|
||||
if verbose {eprintln!("Making {} powers of tau", assembly.num_constraints)};
|
||||
// Create bases for blind evaluation of polynomials at tau
|
||||
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
|
||||
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
|
||||
|
||||
if verbose {eprintln!("assembly.num_constraints: {}", assembly.num_constraints)};
|
||||
|
||||
// Compute G1 window table
|
||||
let mut g1_wnaf = Wnaf::new();
|
||||
let g1_wnaf = g1_wnaf.base(g1, {
|
||||
@ -268,7 +261,6 @@ pub fn generate_parameters<E, C>(
|
||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
|
||||
{
|
||||
//let mut progress_bar = mb.create_bar(a.len() as u64);
|
||||
scope.spawn(move || {
|
||||
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
|
||||
|
||||
@ -290,14 +282,11 @@ pub fn generate_parameters<E, C>(
|
||||
let start = PreciseTime::now();
|
||||
// Compute the H query with multiple threads
|
||||
worker.scope(h.len(), |scope, chunk| {
|
||||
let mut mb = if verbose {Some(MultiBar::new())} else {None};
|
||||
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
|
||||
{
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
let mut progress_bar = if let Some(mb) = mb.as_mut() {Some(mb.create_bar(h.len() as u64))} else {None};
|
||||
scope.spawn(move || {
|
||||
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||
let mut step: u64 = 0;
|
||||
for (h, p) in h.iter_mut().zip(p.iter())
|
||||
{
|
||||
// Compute final exponent
|
||||
@ -306,29 +295,23 @@ pub fn generate_parameters<E, C>(
|
||||
|
||||
// Exponentiate
|
||||
*h = g1_wnaf.scalar(exp.into_repr());
|
||||
if let Some(progress_bar) = progress_bar.as_mut() {
|
||||
step += 1;
|
||||
if step % MIN_STEP == 0 {
|
||||
progress_bar.add(MIN_STEP);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Batch normalize
|
||||
E::G1::batch_normalization(h);
|
||||
if let Some(progress_bar) = progress_bar.as_mut() {progress_bar.finish();}
|
||||
});
|
||||
}
|
||||
if let Some(mb) = mb.as_mut() {mb.listen();}
|
||||
});
|
||||
if verbose {eprintln!("computing the H query done in {} s", start.to(PreciseTime::now()).num_milliseconds() as f64 / 1000.0);};
|
||||
}
|
||||
|
||||
if verbose {eprintln!("using inverse FFT to convert powers of tau to Lagrange coefficients...")};
|
||||
let start = PreciseTime::now();
|
||||
|
||||
// Use inverse FFT to convert powers of tau to Lagrange coefficients
|
||||
powers_of_tau.ifft(&worker);
|
||||
let powers_of_tau = powers_of_tau.into_coeffs();
|
||||
|
||||
if verbose {eprintln!("powers of tau stage 2 done in {} s", start.to(PreciseTime::now()).num_milliseconds() as f64 / 1000.0)};
|
||||
|
||||
let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
|
||||
@ -339,6 +322,7 @@ pub fn generate_parameters<E, C>(
|
||||
|
||||
if verbose {eprintln!("evaluating polynomials...")};
|
||||
let start = PreciseTime::now();
|
||||
|
||||
fn eval<E: Engine>(
|
||||
// wNAF window tables
|
||||
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
|
||||
@ -368,9 +352,8 @@ pub fn generate_parameters<E, C>(
|
||||
// Worker
|
||||
worker: &Worker
|
||||
)
|
||||
{
|
||||
let verbose = verbose_flag();
|
||||
|
||||
{
|
||||
// Sanity check
|
||||
assert_eq!(a.len(), at.len());
|
||||
assert_eq!(a.len(), bt.len());
|
||||
@ -381,7 +364,6 @@ pub fn generate_parameters<E, C>(
|
||||
|
||||
// Evaluate polynomials in multiple threads
|
||||
worker.scope(a.len(), |scope, chunk| {
|
||||
let mut mb = if verbose {Some(MultiBar::new())} else {None};
|
||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
|
||||
.zip(b_g1.chunks_mut(chunk))
|
||||
.zip(b_g2.chunks_mut(chunk))
|
||||
@ -393,9 +375,7 @@ pub fn generate_parameters<E, C>(
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
let mut g2_wnaf = g2_wnaf.shared();
|
||||
|
||||
let mut progress_bar = if let Some(mb) = mb.as_mut() {Some(mb.create_bar(a.len() as u64))} else {None};
|
||||
scope.spawn(move || {
|
||||
let mut step: u64 = 0;
|
||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
|
||||
.zip(b_g1.iter_mut())
|
||||
.zip(b_g2.iter_mut())
|
||||
@ -446,16 +426,8 @@ pub fn generate_parameters<E, C>(
|
||||
e.mul_assign(inv);
|
||||
|
||||
*ext = g1_wnaf.scalar(e.into_repr());
|
||||
if let Some(progress_bar) = progress_bar.as_mut() {
|
||||
step += 1;
|
||||
if step % MIN_STEP == 0 {
|
||||
progress_bar.add(MIN_STEP);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(progress_bar) = progress_bar.as_mut() {progress_bar.finish();}
|
||||
|
||||
// Batch normalize
|
||||
E::G1::batch_normalization(a);
|
||||
E::G1::batch_normalization(b_g1);
|
||||
@ -463,7 +435,6 @@ pub fn generate_parameters<E, C>(
|
||||
E::G1::batch_normalization(ext);
|
||||
});
|
||||
};
|
||||
if let Some(mb) = mb.as_mut() {mb.listen();}
|
||||
});
|
||||
}
|
||||
|
||||
@ -526,6 +497,8 @@ pub fn generate_parameters<E, C>(
|
||||
ic: ic.into_iter().map(|e| e.into_affine()).collect()
|
||||
};
|
||||
|
||||
println!("Has generated {} points", a.len());
|
||||
|
||||
Ok(Parameters {
|
||||
vk: vk,
|
||||
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||
|
@ -46,7 +46,7 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> {
|
||||
if self.0.len() <= self.1 {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases when adding from source").into());
|
||||
}
|
||||
|
||||
if self.0[self.1].is_zero() {
|
||||
@ -62,7 +62,7 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||
|
||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
||||
if self.0.len() <= self.1 {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases skipping from source").into());
|
||||
}
|
||||
|
||||
self.1 += amt;
|
||||
|
Loading…
Reference in New Issue
Block a user