script/research/tx-replayer: a tool to replay a transaction by resetting the blockchain database to a height before the transaction was added at

This commit is contained in:
oars
2025-12-15 12:00:32 +03:00
parent 12a18503a2
commit ff477eb2f0
4 changed files with 145 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
/target
Cargo.lock
rustfmt.toml
tx-replayer
*.zst
*.json.gz

View File

@@ -0,0 +1,24 @@
[package]
name = "tx-replayer"
version = "0.1.0"
description = "CLI-utility to replay transactions for analysis"
authors = ["Dyne.org foundation <foundation@dyne.org>"]
repository = "https://codeberg.org/darkrenaissance/darkfi"
license = "AGPL-3.0-only"
edition = "2024"
[workspace]
[dependencies]
darkfi = {path = "../../../", features = ["validator"]}
darkfi-sdk = {path = "../../../src/sdk"}
sled-overlay = {version = "0.1.10"}
smol = {version = "2.0.2"}
clap = {version = "4.4.11", features = ["derive"]}
[patch.crates-io]
halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v031"}
halo2_gadgets = {git="https://github.com/parazyd/halo2", branch="v031"}
[profile.release]
debug = true

View File

@@ -0,0 +1,37 @@
.POSIX:
# Install prefix
PREFIX = $(HOME)/.cargo
# Cargo binary
CARGO = cargo
# Compile target
RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2)
# Uncomment when doing musl static builds
#RUSTFLAGS = -C target-feature=+crt-static -C link-self-contained=yes
SRC = \
Cargo.toml \
$(shell find src -type f -name '*.rs') \
BIN = $(shell grep '^name = ' Cargo.toml | cut -d' ' -f3 | tr -d '"')
all: $(BIN)
$(BIN): $(SRC)
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package $@
cp -f target/$(RUST_TARGET)/release/$@ $@
fmt:
$(CARGO) +nightly fmt --all
clippy:
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clippy --target=$(RUST_TARGET) \
--release --all-features --workspace --tests
clean:
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clean --target=$(RUST_TARGET) --release --package $(BIN)
rm -f $(BIN)
.PHONY: all fmt clippy clean

View File

@@ -0,0 +1,78 @@
use clap::Parser;
use darkfi::{
blockchain::{Blockchain, BlockchainOverlay, BlockchainOverlayPtr},
cli_desc,
util::path::expand_path,
validator::verification::verify_transaction,
zk::VerifyingKey,
};
use darkfi_sdk::{crypto::MerkleTree, tx::TransactionHash};
use std::collections::HashMap;
#[derive(Parser)]
#[command(about = cli_desc!())]
struct Args {
#[arg(short, long)]
database_path: String,
#[arg(short, long)]
tx_hash: String,
}
fn main() {
smol::block_on(async {
let args = Args::parse();
replay_tx(args).await;
});
}
async fn replay_tx(args: Args) {
let db_path = expand_path(&args.database_path).unwrap();
let sled_db = sled_overlay::sled::open(&db_path).unwrap();
let blockchain = Blockchain::new(&sled_db).unwrap();
let txh: TransactionHash = args.tx_hash.parse().unwrap();
let tx = blockchain.transactions.get(&[txh], true).unwrap().first().unwrap().clone().unwrap();
let (overlay, new_height) = rollback_database(&blockchain, txh).await;
let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
for call in &tx.calls {
vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
}
let result =
verify_transaction(&overlay, new_height, 2, &tx, &mut MerkleTree::new(1), &mut vks, true)
.await
.unwrap();
println!("Verify Transaction Result: {:?}", result);
}
/// Resets the blockchain in memory to a height before the transaction.
async fn rollback_database(
blockchain: &Blockchain,
txh: TransactionHash,
) -> (BlockchainOverlayPtr, u32) {
let (tx_height, _) =
blockchain.transactions.get_location(&[txh], true).unwrap().first().unwrap().unwrap();
let new_height = tx_height - 1;
println!("Rolling back database to Height: {new_height}");
let (last, _) = blockchain.last().unwrap();
let heights: Vec<u32> = (new_height + 1..=last).rev().collect();
let inverse_diffs = blockchain.blocks.get_state_inverse_diff(&heights, true).unwrap();
let overlay = BlockchainOverlay::new(blockchain).unwrap();
let overlay_lock = overlay.lock().unwrap();
let mut lock = overlay_lock.overlay.lock().unwrap();
for inverse_diff in inverse_diffs {
let inverse_diff = inverse_diff.unwrap();
lock.add_diff(&inverse_diff).unwrap();
}
drop(lock);
drop(overlay_lock);
(overlay, new_height)
}