Skip to content

Commit 10a5575

Browse files
committed
Add more debug infos
1 parent 324069f commit 10a5575

File tree

2 files changed

+200
-8
lines changed

2 files changed

+200
-8
lines changed

ledger/src/proofs/transaction.rs

Lines changed: 184 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3914,6 +3914,8 @@ fn get_rng() -> rand::rngs::OsRng {
39143914
pub enum ProofError {
39153915
#[error("kimchi error: {0:?}")]
39163916
ProvingError(#[from] kimchi::error::ProverError),
3917+
#[error("kimchi error with context: {0:?}")]
3918+
ProvingErrorWithContext(#[from] debug::KimchiProofError),
39173919
#[error("constraint not satisfield: {0}")]
39183920
ConstraintsNotSatisfied(String),
39193921
#[error("invalid bigint")]
@@ -3959,12 +3961,11 @@ pub(super) fn create_proof<C: ProofConstants, F: FieldWitness>(
39593961
let prover_index: &ProverIndex<F> = &prover.index;
39603962

39613963
// public input
3962-
let public_input = computed_witness[0][0..prover_index.cs.public].to_vec();
3964+
let public_input = computed_witness[0][..prover_index.cs.public].to_vec();
39633965

39643966
if only_verify_constraints {
3965-
let public = &computed_witness[0][0..prover_index.cs.public];
39663967
prover_index
3967-
.verify(&computed_witness, public)
3968+
.verify(&computed_witness, &public_input)
39683969
.map_err(|e| {
39693970
ProofError::ConstraintsNotSatisfied(format!("incorrect witness: {:?}", e))
39703971
})?;
@@ -3984,10 +3985,38 @@ pub(super) fn create_proof<C: ProofConstants, F: FieldWitness>(
39843985
computed_witness,
39853986
&[],
39863987
prover_index,
3987-
prev_challenges,
3988+
prev_challenges.clone(),
39883989
None,
39893990
&mut rng,
39903991
)
3992+
.map_err(|e| {
3993+
use kimchi::groupmap::GroupMap;
3994+
3995+
let prev_challenges_hash = debug::hash_prev_challenge::<F>(&prev_challenges);
3996+
let witness_primary_hash = debug::hash_slice(&w.primary);
3997+
let witness_aux_hash = debug::hash_slice(w.aux());
3998+
let group_map_hash = debug::hash_slice(&group_map.composition());
3999+
4000+
dbg!(
4001+
&prev_challenges_hash,
4002+
&witness_primary_hash,
4003+
&witness_aux_hash,
4004+
&group_map_hash
4005+
);
4006+
4007+
let context = debug::KimchiProofError {
4008+
inner_error: e.to_string(),
4009+
witness_primary: w.primary.iter().map(|f| (*f).into()).collect(),
4010+
witness_aux: w.aux().iter().map(|f| (*f).into()).collect(),
4011+
// prev_challenges,
4012+
witness_primary_hash,
4013+
witness_aux_hash,
4014+
prev_challenges_hash,
4015+
group_map_hash,
4016+
};
4017+
4018+
ProofError::ProvingErrorWithContext(context)
4019+
})
39914020
.context("create_recursive")?;
39924021

39934022
eprintln!("proof_elapsed={:?}", now.elapsed());
@@ -3998,6 +4027,157 @@ pub(super) fn create_proof<C: ProofConstants, F: FieldWitness>(
39984027
})
39994028
}
40004029

4030+
pub mod debug {
4031+
use super::*;
4032+
4033+
use mina_p2p_messages::bigint::BigInt;
4034+
use mina_p2p_messages::binprot;
4035+
use sha2::Digest;
4036+
4037+
fn hash_field<F: FieldWitness>(state: &mut sha2::Sha256, f: &F) {
4038+
for limb in f.montgomery_form_ref() {
4039+
state.update(limb.to_le_bytes());
4040+
}
4041+
}
4042+
4043+
fn hash_field_slice<F: FieldWitness>(state: &mut sha2::Sha256, slice: &[F]) {
4044+
state.update(slice.len().to_le_bytes());
4045+
for f in slice.iter().flat_map(|f| f.montgomery_form_ref()) {
4046+
state.update(f.to_le_bytes());
4047+
}
4048+
}
4049+
4050+
pub(super) fn hash_slice<F: FieldWitness>(slice: &[F]) -> String {
4051+
let mut hasher = sha2::Sha256::new();
4052+
hash_field_slice(&mut hasher, slice);
4053+
hex::encode(hasher.finalize())
4054+
}
4055+
4056+
pub(super) fn hash_prev_challenge<F: FieldWitness>(
4057+
prevs: &[RecursionChallenge<F::OtherCurve>],
4058+
) -> String {
4059+
use poly_commitment::commitment::CommitmentCurve;
4060+
use sha2::Digest;
4061+
let mut hasher = sha2::Sha256::new();
4062+
for RecursionChallenge { chals, comm } in prevs {
4063+
hash_field_slice(&mut hasher, chals);
4064+
let poly_commitment::PolyComm { elems } = comm;
4065+
for elem in elems {
4066+
match elem.to_coordinates() {
4067+
None => {
4068+
hasher.update([0]);
4069+
}
4070+
Some((c1, c2)) => {
4071+
hasher.update([1]);
4072+
hash_field(&mut hasher, &c1);
4073+
hash_field(&mut hasher, &c2);
4074+
}
4075+
}
4076+
}
4077+
}
4078+
hex::encode(hasher.finalize())
4079+
}
4080+
4081+
#[derive(Clone)]
4082+
pub struct KimchiProofError {
4083+
pub inner_error: String,
4084+
pub witness_primary: Vec<BigInt>,
4085+
pub witness_aux: Vec<BigInt>,
4086+
// pub prev_challenges: Vec<RecursionChallenge<F::OtherCurve>>,
4087+
// Store hashes in case there is a de/serialization bug
4088+
pub witness_primary_hash: String,
4089+
pub witness_aux_hash: String,
4090+
pub prev_challenges_hash: String,
4091+
pub group_map_hash: String,
4092+
}
4093+
4094+
// Manual implementation because String does not implement binprot traits (because unbounded)
4095+
impl binprot::BinProtWrite for KimchiProofError {
4096+
fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
4097+
let Self {
4098+
inner_error,
4099+
witness_primary,
4100+
witness_aux,
4101+
witness_primary_hash,
4102+
witness_aux_hash,
4103+
prev_challenges_hash,
4104+
group_map_hash,
4105+
} = self;
4106+
let inner_error: &[u8] = inner_error.as_bytes();
4107+
let witness_primary_hash: &[u8] = witness_primary_hash.as_bytes();
4108+
let witness_aux_hash: &[u8] = witness_aux_hash.as_bytes();
4109+
let prev_challenges_hash: &[u8] = prev_challenges_hash.as_bytes();
4110+
let group_map_hash: &[u8] = group_map_hash.as_bytes();
4111+
binprot::BinProtWrite::binprot_write(&inner_error, w)?;
4112+
binprot::BinProtWrite::binprot_write(witness_primary, w)?;
4113+
binprot::BinProtWrite::binprot_write(witness_aux, w)?;
4114+
binprot::BinProtWrite::binprot_write(&witness_primary_hash, w)?;
4115+
binprot::BinProtWrite::binprot_write(&witness_aux_hash, w)?;
4116+
binprot::BinProtWrite::binprot_write(&prev_challenges_hash, w)?;
4117+
binprot::BinProtWrite::binprot_write(&group_map_hash, w)?;
4118+
Ok(())
4119+
}
4120+
}
4121+
// Manual implementation because String does not implement binprot traits (because unbounded)
4122+
impl binprot::BinProtRead for KimchiProofError {
4123+
fn binprot_read<R: std::io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
4124+
where
4125+
Self: Sized,
4126+
{
4127+
let to_string = |bytes: Vec<u8>| -> String { String::from_utf8(bytes).unwrap() };
4128+
let inner_error: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4129+
let witness_primary: Vec<BigInt> = binprot::BinProtRead::binprot_read(r)?;
4130+
let witness_aux: Vec<BigInt> = binprot::BinProtRead::binprot_read(r)?;
4131+
let witness_primary_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4132+
let witness_aux_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4133+
let prev_challenges_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4134+
let group_map_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4135+
Ok(Self {
4136+
inner_error: to_string(inner_error),
4137+
witness_primary,
4138+
witness_aux,
4139+
witness_primary_hash: to_string(witness_primary_hash),
4140+
witness_aux_hash: to_string(witness_aux_hash),
4141+
prev_challenges_hash: to_string(prev_challenges_hash),
4142+
group_map_hash: to_string(group_map_hash),
4143+
})
4144+
}
4145+
}
4146+
4147+
impl core::fmt::Display for KimchiProofError {
4148+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4149+
f.write_fmt(format_args!("{:?}", self))
4150+
}
4151+
}
4152+
4153+
impl std::error::Error for KimchiProofError {}
4154+
4155+
impl core::fmt::Debug for KimchiProofError {
4156+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4157+
let Self {
4158+
inner_error,
4159+
witness_primary,
4160+
witness_aux,
4161+
witness_primary_hash,
4162+
witness_aux_hash,
4163+
prev_challenges_hash,
4164+
group_map_hash,
4165+
} = self;
4166+
4167+
// Print witness lengths, not the whole vectors
4168+
f.debug_struct("KimchiProofError")
4169+
.field("inner_error", inner_error)
4170+
.field("witness_primary", &witness_primary.len())
4171+
.field("witness_aux", &witness_aux.len())
4172+
.field("witness_primary_hash", &witness_primary_hash)
4173+
.field("witness_aux_hash", &witness_aux_hash)
4174+
.field("prev_challenges_hash", &prev_challenges_hash)
4175+
.field("group_map_hash", &group_map_hash)
4176+
.finish()
4177+
}
4178+
}
4179+
}
4180+
40014181
#[derive(Clone)]
40024182
pub struct Prover<F: FieldWitness> {
40034183
/// Constants to each kind of proof

node/common/src/service/block_producer/mod.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ mod vrf_evaluator;
22

33
use std::sync::Arc;
44

5-
use ledger::proofs::{block::BlockParams, generate_block_proof, provers::BlockProver};
5+
use ledger::proofs::{
6+
block::BlockParams, generate_block_proof, provers::BlockProver,
7+
transaction::debug::KimchiProofError,
8+
};
69
use mina_p2p_messages::{
710
bigint::BigInt,
811
binprot::{self, BinProtWrite},
@@ -101,7 +104,7 @@ fn prover_loop(
101104
) {
102105
while let Some(msg) = rx.blocking_recv() {
103106
let (provers, block_hash, mut input) = msg.0;
104-
let res = prove(provers, &mut input, &keypair, false).map_err(|err| format!("{err:?}"));
107+
let res = prove(provers, &mut input, &keypair, false);
105108
if let Err(error) = &res {
106109
openmina_core::error!(message = "Block proof failed", error = format!("{error}"));
107110
if let Err(error) = dump_failed_block_proof_input(block_hash.clone(), input, error) {
@@ -111,6 +114,7 @@ fn prover_loop(
111114
);
112115
}
113116
}
117+
let res = res.map_err(|err| err.to_string());
114118
let _ = event_sender.send(BlockProducerEvent::BlockProve(block_hash, res).into());
115119
}
116120
}
@@ -180,8 +184,9 @@ impl node::service::BlockProducerService for crate::NodeService {
180184
fn dump_failed_block_proof_input(
181185
block_hash: StateHash,
182186
mut input: Box<ProverExtendBlockchainInputStableV2>,
183-
error: &str,
187+
error: &anyhow::Error,
184188
) -> std::io::Result<()> {
189+
use ledger::proofs::transaction::ProofError;
185190
use rsa::Pkcs1v15Encrypt;
186191

187192
const PUBLIC_KEY: &str = "-----BEGIN RSA PUBLIC KEY-----
@@ -198,6 +203,7 @@ kGqG7QLzSPjAtP/YbUponwaD+t+A0kBg0hV4hhcJOkPeA2NOi04K93bz3HuYCVRe
198203
input: Box<ProverExtendBlockchainInputStableV2>,
199204
key: Vec<u8>,
200205
error: Vec<u8>,
206+
kimchi_error_with_context: Option<KimchiProofError>,
201207
}
202208

203209
let producer_private_key = {
@@ -221,10 +227,16 @@ kGqG7QLzSPjAtP/YbUponwaD+t+A0kBg0hV4hhcJOkPeA2NOi04K93bz3HuYCVRe
221227
// IMPORTANT: Make sure that `input` doesn't leak the private key.
222228
input.prover_state.producer_private_key = v2::SignatureLibPrivateKeyStableV1(BigInt::one());
223229

230+
let error_str = error.to_string();
231+
224232
let input = DumpBlockProof {
225233
input,
226234
key: encrypted_producer_private_key,
227-
error: error.as_bytes().to_vec(),
235+
error: error_str.as_bytes().to_vec(),
236+
kimchi_error_with_context: match error.downcast_ref::<ProofError>() {
237+
Some(ProofError::ProvingErrorWithContext(context)) => Some(context.clone()),
238+
_ => None,
239+
},
228240
};
229241

230242
let debug_dir = openmina_core::get_debug_dir();

0 commit comments

Comments
 (0)