Skip to content

Commit 9961938

Browse files
committed
Save StagedLedger and block when the block application failed
When a staged ledger hash mismatch occurs, dump the state to a file so we can easily reproduce the bug in Rust and OCaml: https://github.com/openmina/openmina/blob/8e68037aafddd43842a54c8439baeafee4c6e1eb/ledger/src/staged_ledger/staged_ledger.rs#L5959
1 parent b043f82 commit 9961938

File tree

1 file changed

+68
-2
lines changed

1 file changed

+68
-2
lines changed

node/src/ledger/ledger_service.rs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use ledger::{
2929
};
3030
use mina_hasher::Fp;
3131
use mina_p2p_messages::v2::{
32-
DataHashLibStateHashStableV1, LedgerHash, MinaBaseAccountBinableArgStableV2,
32+
self, DataHashLibStateHashStableV1, LedgerHash, MinaBaseAccountBinableArgStableV2,
3333
MinaBaseLedgerHash0StableV1, MinaBaseSokMessageStableV1, MinaBaseStagedLedgerHashStableV1,
3434
MinaLedgerSyncLedgerAnswerStableV2, MinaLedgerSyncLedgerQueryStableV1,
3535
MinaStateBlockchainStateValueStableV2LedgerProofStatement, MinaStateProtocolStateValueStableV2,
@@ -530,7 +530,25 @@ impl<T: LedgerService> TransitionFrontierService for T {
530530
// TODO(binier): return error if not matching.
531531
let expected_ledger_hashes = block.staged_ledger_hashes();
532532
if &ledger_hashes != expected_ledger_hashes {
533-
panic!("staged ledger hash mismatch. found: {ledger_hashes:?}, expected: {expected_ledger_hashes:?}");
533+
let staged_ledger = self
534+
.ctx_mut()
535+
.staged_ledger_mut(&pred_block.staged_ledger_hash())
536+
.unwrap(); // We already know the ledger exists, see the same call a few lines above
537+
538+
match dump_application_to_file(staged_ledger, block.clone(), pred_block) {
539+
Ok(filename) => openmina_core::info!(
540+
openmina_core::log::system_time();
541+
kind = "LedgerService::dump - Failed application",
542+
summary = format!("StagedLedger and block saved to: {filename:?}")
543+
),
544+
Err(e) => openmina_core::error!(
545+
openmina_core::log::system_time();
546+
kind = "LedgerService::dump - Failed application",
547+
summary = format!("Failed to save block application to file: {e:?}")
548+
),
549+
}
550+
551+
panic!("staged ledger hash mismatch. found: {ledger_hashes:#?}, expected: {expected_ledger_hashes:#?}");
534552
}
535553

536554
let ledger_hash = block.staged_ledger_hash();
@@ -975,6 +993,54 @@ impl<T: LedgerService> BlockProducerVrfEvaluatorLedgerService for T {
975993
}
976994
}
977995

996+
/// Save staged ledger and block to file, when the application fail.
997+
/// So we can easily reproduce the application both in Rust and OCaml, to compare them.
998+
/// - https://github.com/openmina/openmina/blob/8e68037aafddd43842a54c8439baeafee4c6e1eb/ledger/src/staged_ledger/staged_ledger.rs#L5959
999+
/// - TODO: Find OCaml link, I remember having the same test in OCaml but I can't find where
1000+
fn dump_application_to_file(
1001+
staged_ledger: &StagedLedger,
1002+
block: ArcBlockWithHash,
1003+
pred_block: ArcBlockWithHash,
1004+
) -> std::io::Result<String> {
1005+
use mina_p2p_messages::{
1006+
binprot,
1007+
binprot::macros::{BinProtRead, BinProtWrite},
1008+
};
1009+
1010+
#[derive(BinProtRead, BinProtWrite)]
1011+
struct ApplyContext {
1012+
accounts: Vec<v2::MinaBaseAccountBinableArgStableV2>,
1013+
scan_state: v2::TransactionSnarkScanStateStableV2,
1014+
pending_coinbase: v2::MinaBasePendingCoinbaseStableV2,
1015+
pred_block: v2::MinaBlockBlockStableV2,
1016+
blocks: Vec<v2::MinaBlockBlockStableV2>,
1017+
}
1018+
1019+
let cs = &block.block.header.protocol_state.body.consensus_state;
1020+
let block_height = cs.blockchain_length.as_u32();
1021+
1022+
let apply_context = ApplyContext {
1023+
accounts: staged_ledger
1024+
.ledger()
1025+
.to_list()
1026+
.iter()
1027+
.map(v2::MinaBaseAccountBinableArgStableV2::from)
1028+
.collect::<Vec<_>>(),
1029+
scan_state: staged_ledger.scan_state().into(),
1030+
pending_coinbase: staged_ledger.pending_coinbase_collection().into(),
1031+
pred_block: (*pred_block.block).clone(),
1032+
blocks: vec![(*block.block).clone()],
1033+
};
1034+
1035+
use mina_p2p_messages::binprot::BinProtWrite;
1036+
let filename = format!("/tmp/failed_application_ctx_{}.binprot", block_height);
1037+
let mut file = std::fs::File::create(&filename)?;
1038+
apply_context.binprot_write(&mut file)?;
1039+
file.sync_all()?;
1040+
1041+
Ok(filename)
1042+
}
1043+
9781044
#[cfg(test)]
9791045
mod tests {
9801046
use mina_p2p_messages::v2::MinaBaseLedgerHash0StableV1;

0 commit comments

Comments
 (0)