mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-06 21:34:00 -05:00
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:
6
script/research/tx-replayer/.gitignore
vendored
Normal file
6
script/research/tx-replayer/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
rustfmt.toml
|
||||
tx-replayer
|
||||
*.zst
|
||||
*.json.gz
|
||||
24
script/research/tx-replayer/Cargo.toml
Normal file
24
script/research/tx-replayer/Cargo.toml
Normal 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
|
||||
37
script/research/tx-replayer/Makefile
Normal file
37
script/research/tx-replayer/Makefile
Normal 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
|
||||
78
script/research/tx-replayer/src/main.rs
Normal file
78
script/research/tx-replayer/src/main.rs
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user