فهرست منبع

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

oars 7 ماه پیش
والد
کامیت
ff477eb2f0

+ 6 - 0
script/research/tx-replayer/.gitignore

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

+ 24 - 0
script/research/tx-replayer/Cargo.toml

@@ -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 - 0
script/research/tx-replayer/Makefile

@@ -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 - 0
script/research/tx-replayer/src/main.rs

@@ -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)
+}