diff --git a/.gitignore b/.gitignore index e69de29..03b2709 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +btc_mine_simulator/target/ diff --git a/btc_mine_simulator/.gitignore b/btc_mine_simulator/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/btc_mine_simulator/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/btc_mine_simulator/Cargo.lock b/btc_mine_simulator/Cargo.lock new file mode 100644 index 0000000..250d632 --- /dev/null +++ b/btc_mine_simulator/Cargo.lock @@ -0,0 +1,89 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "btc_mine_simulator" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/btc_mine_simulator/Cargo.toml b/btc_mine_simulator/Cargo.toml new file mode 100644 index 0000000..eca64dc --- /dev/null +++ b/btc_mine_simulator/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "btc_mine_simulator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/btc_mine_simulator/src/main.rs b/btc_mine_simulator/src/main.rs new file mode 100644 index 0000000..883e70f --- /dev/null +++ b/btc_mine_simulator/src/main.rs @@ -0,0 +1,73 @@ +use std::fs; +use std::io; +use std::path::Path; + +mod tx; +mod validate; +use tx::Transaction; +use validate::validate_transaction; + +fn read_transactions_from_dir(dir: &Path) -> io::Result<(Vec, usize, usize)> { + let mut transactions = Vec::new(); + let mut total_files = 0; + let mut failed_parses = 0; + + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + total_files += 1; + match fs::read_to_string(&path) { + Ok(data) => { + match serde_json::from_str::(&data) { + Ok(transaction) => transactions.push(transaction), + Err(e) => { // Capture the parse error + failed_parses += 1; + println!("Failed to parse file: {:?}, Reason: {}", path.display(), e); + // Prints out the specific reason for the parse failure + }, + } + }, + Err(e) => { // Capture the file read error + println!("Failed to read file: {:?}, Reason: {}", path.display(), e); + // This case handles file read errors + } + } + } + } + for tx in &transactions { + // println!("{:?}", tx.locktime); + if tx.locktime != 0 { + println!("wow {:?}", tx.locktime); + } + } + + println!("Total files processed: {}", total_files); + println!("Failed to parse: {}", failed_parses); + Ok((transactions, total_files, failed_parses)) +} + +fn main() { + let dir = Path::new("../mempool"); + let txs = match read_transactions_from_dir(dir) { + Ok((transactions, total_files, failed_parses)) => { + println!("Successfully parsed transactions: {}", transactions.len()); + println!("Total files: {}", total_files); + println!("Failed parses: {}", failed_parses); + transactions + }, + Err(e) => panic!("Error reading transactions: {}", e), + }; + let mut res = 0; + for tx in &txs { + if let Err(e) = validate_transaction(tx) { + println!("{:?}", tx.); + res += 1; + println!("error: {:?}", e); + + } + + } + println!("{:?}", res); + +} diff --git a/btc_mine_simulator/src/tx.rs b/btc_mine_simulator/src/tx.rs new file mode 100644 index 0000000..d4d1f8b --- /dev/null +++ b/btc_mine_simulator/src/tx.rs @@ -0,0 +1,40 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Transaction { + pub version: i32, + pub locktime: u32, + pub vin: Vec, + pub vout: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Input { + pub txid: String, + pub vout: u32, + pub prevout: PrevOut, + pub scriptsig: String, + pub scriptsig_asm: String, + pub witness: Option>, + pub is_coinbase: bool, + pub sequence: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct PrevOut { + pub scriptpubkey: String, + pub scriptpubkey_asm: String, + pub scriptpubkey_type: String, + pub scriptpubkey_address: String, + pub value: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Output { + pub scriptpubkey: String, + pub scriptpubkey_asm: String, + pub scriptpubkey_type: String, + pub scriptpubkey_address: Option, + pub value: u64, +} + diff --git a/btc_mine_simulator/src/validate.rs b/btc_mine_simulator/src/validate.rs new file mode 100644 index 0000000..e918d7d --- /dev/null +++ b/btc_mine_simulator/src/validate.rs @@ -0,0 +1,44 @@ +use serde::{Deserialize, Serialize}; + +use crate::tx::Transaction; + +// Assuming the structs Transaction, Input, Output, PrevOut are defined as above + +pub fn validate_transaction(tx: &Transaction) -> Result<(), String> { + if tx.version < 1 || tx.version > 2 { + return Err("Unsupported transaction version".to_string()); + } + + if tx.vin.is_empty() { + return Err("Transaction has no inputs".to_string()); + } + + if tx.vout.is_empty() { + return Err("Transaction has no outputs".to_string()); + } + + for input in &tx.vin { + if input.is_coinbase && tx.vin.len() > 1 { + return Err("Coinbase transaction has more than one input".to_string()); + } + + if !input.is_coinbase && input.txid.is_empty() { + return Err("Input txid is empty".to_string()); + } + + if let Some(witness) = &input.witness { + if witness.is_empty() { + return Err("Witness is present but empty".to_string()); + } + } + } + + // let total_output_value: u64 = tx.vout.iter().map(|output| output.value).sum(); + // if total_output_value == 0 { + // return Err("Total output value is 0".to_string()); + // } + + // Further checks can include scriptsig and scriptpubkey validation, which are complex and require executing the scripts + + Ok(()) +} diff --git a/btc_mine_simulator/todo b/btc_mine_simulator/todo new file mode 100644 index 0000000..4dfe629 --- /dev/null +++ b/btc_mine_simulator/todo @@ -0,0 +1,6 @@ +1. clean up invalid tx +2. compute size and fee for each tx +3. build dep graph, tx1 <- tx2 if tx2 can only be valid after tx1 executed +4. try out some algo, simple one would be greedy with pq, consistently choose the max fee tx that can be selected, but this would def miss some some large fee tx with long dep of tx before it +5. build block with selected tx, find the hash +6. export result to file \ No newline at end of file diff --git a/run.sh b/run.sh old mode 100644 new mode 100755 diff --git a/test.sh b/test.sh old mode 100644 new mode 100755