Skip to content

Commit

Permalink
introduce Rust Deserializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Ouziel committed Jan 6, 2025
1 parent e1f7443 commit 1dfbb7e
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 105 deletions.
1 change: 1 addition & 0 deletions counterparty-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions counterparty-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ uuid = { version = "1.11.0", features = ["v4", "fast-rng"] }
colored = "2.1.0"
rust-crypto = "0.2.36"
time = "0.3.36"
hex = "0.4"

[build-dependencies]
vergen = { version = "8.3.1", features = [
Expand Down
230 changes: 125 additions & 105 deletions counterparty-rs/src/indexer/bitcoin_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{collections::HashMap, sync::Arc, thread::JoinHandle};
use crate::b58::b58_encode;
use crate::utils::script_to_address;
use bitcoin::{
consensus::serialize,
consensus::serialize, consensus::deserialize,
hashes::{hex::prelude::*, ripemd160, sha256, sha256d::Hash as Sha256dHash, Hash},
opcodes::all::{
OP_CHECKMULTISIG, OP_CHECKSIG, OP_EQUAL, OP_HASH160, OP_PUSHNUM_1, OP_PUSHNUM_2,
Expand All @@ -19,6 +19,7 @@ use bitcoincore_rpc::{Auth, Client, RpcApi};
use crossbeam_channel::{bounded, select, unbounded, Receiver, Sender};
use crypto::rc4::Rc4;
use crypto::symmetriccipher::SynchronousStreamCipher;
use hex;

use super::{
block::{
Expand Down Expand Up @@ -373,118 +374,137 @@ fn parse_vout(
}
}

impl ToBlock for Block {
fn to_block(&self, config: Config, height: u32) -> CrateBlock {
let mut transactions = Vec::new();
for tx in self.txdata.iter() {
let tx_bytes = serialize(tx);
let mut vins = Vec::new();
let mut segwit = false;
let mut vtxinwit: Vec<Vec<String>> = Vec::new();
let mut key = Vec::new();
for vin in tx.input.iter() {
if key.is_empty() {
key = vin.previous_output.txid.to_byte_array().to_vec();
key.reverse();
}
let hash = vin.previous_output.txid.to_string();
if !vin.witness.is_empty() {
vtxinwit.push(
vin.witness
.iter()
.map(|w| w.as_hex().to_string())
.collect::<Vec<_>>(),
);
segwit = true;
} else {
vtxinwit.push(Vec::new());
}
vins.push(Vin {
hash,
n: vin.previous_output.vout,
sequence: vin.sequence.0,
script_sig: vin.script_sig.to_bytes(),
})
}
fn create_transaction(tx: &bitcoin::blockdata::transaction::Transaction, config: &Config, height: u32, parse_vouts: bool) -> Transaction {
let tx_bytes = serialize(tx);
let mut vins = Vec::new();
let mut segwit = false;
let mut vtxinwit: Vec<Vec<String>> = Vec::new();
let mut key = Vec::new();
for vin in tx.input.iter() {
if key.is_empty() {
key = vin.previous_output.txid.to_byte_array().to_vec();
key.reverse();
}
let hash = vin.previous_output.txid.to_string();
if !vin.witness.is_empty() {
vtxinwit.push(
vin.witness
.iter()
.map(|w| w.as_hex().to_string())
.collect::<Vec<_>>(),
);
segwit = true;
} else {
vtxinwit.push(Vec::new());
}
vins.push(Vin {
hash,
n: vin.previous_output.vout,
sequence: vin.sequence.0,
script_sig: vin.script_sig.to_bytes(),
})
}

let mut vouts = Vec::new();
let mut destinations = Vec::new();
let mut fee = 0;
let mut btc_amount = 0;
let mut data = Vec::new();
let mut potential_dispensers = Vec::new();
let mut err = None;
for (vi, vout) in tx.output.iter().enumerate() {
vouts.push(Vout {
value: vout.value.to_sat(),
script_pub_key: vout.script_pubkey.to_bytes(),
});
let mut vouts = Vec::new();
let mut destinations = Vec::new();
let mut fee = 0;
let mut btc_amount = 0;
let mut data = Vec::new();
let mut potential_dispensers = Vec::new();
let mut err = None;
for (vi, vout) in tx.output.iter().enumerate() {
vouts.push(Vout {
value: vout.value.to_sat(),
script_pub_key: vout.script_pubkey.to_bytes(),
});
}
let mut parsed_vouts: Result<ParsedVouts, String> = Err("Not Parsed".to_string());
if parse_vouts {
for (vi, vout) in tx.output.iter().enumerate() {
if !config.multisig_addresses_enabled(height) {
continue;
}
for (vi, vout) in tx.output.iter().enumerate() {
if !config.multisig_addresses_enabled(height) {
continue;
let output_value = vout.value.to_sat() as i64;
fee -= output_value;
let result = parse_vout(
&config,
key.clone(),
height,
tx.compute_txid().to_string(),
vi,
vout,
);
match result {
Err(e) => {
err = Some(e);
break;
}
let output_value = vout.value.to_sat() as i64;
fee -= output_value;
let result = parse_vout(
&config,
key.clone(),
height,
tx.compute_txid().to_string(),
vi,
vout,
);
match result {
Err(e) => {
err = Some(e);
break;
}
Ok((parse_output, potential_dispenser)) => {
potential_dispensers.push(potential_dispenser);
if data.is_empty()
&& parse_output.is_destination()
&& destinations != vec![config.unspendable()]
{
if let ParseOutput::Destination(destination) = parse_output {
destinations.push(destination);
}
btc_amount += output_value;
} else if parse_output.is_destination() {
break;
} else if let ParseOutput::Data(mut new_data) = parse_output {
data.append(&mut new_data)
Ok((parse_output, potential_dispenser)) => {
potential_dispensers.push(potential_dispenser);
if data.is_empty()
&& parse_output.is_destination()
&& destinations != vec![config.unspendable()]
{
if let ParseOutput::Destination(destination) = parse_output {
destinations.push(destination);
}
btc_amount += output_value;
} else if parse_output.is_destination() {
break;
} else if let ParseOutput::Data(mut new_data) = parse_output {
data.append(&mut new_data)
}
}
}
if !config.multisig_addresses_enabled(height) {
err = Some(Error::ParseVout(
"Multisig addresses are not enabled".to_string(),
));
}
let parsed_vouts = if let Some(e) = err {
Err(e.to_string())
} else {
Ok(ParsedVouts {
destinations,
btc_amount,
fee,
data,
potential_dispensers,
})
};
transactions.push(Transaction {
version: tx.version.0,
segwit,
coinbase: tx.is_coinbase(),
lock_time: tx.lock_time.to_consensus_u32(),
tx_id: tx.compute_txid().to_string(),
tx_hash: Sha256dHash::hash(&tx_bytes).to_string(),
vtxinwit,
vin: vins,
vout: vouts,
parsed_vouts,
}
if !config.multisig_addresses_enabled(height) {
err = Some(Error::ParseVout(
"Multisig addresses are not enabled".to_string(),
));
}
parsed_vouts = if let Some(e) = err {
Err(e.to_string())
} else {
Ok(ParsedVouts {
destinations,
btc_amount,
fee,
data,
potential_dispensers,
})
};
}
Transaction {
version: tx.version.0,
segwit,
coinbase: tx.is_coinbase(),
lock_time: tx.lock_time.to_consensus_u32(),
tx_id: tx.compute_txid().to_string(),
tx_hash: Sha256dHash::hash(&tx_bytes).to_string(),
vtxinwit,
vin: vins,
vout: vouts,
parsed_vouts,
}
}

pub fn parse_transaction(tx_hex: &str, config: &Config, height: u32, parse_vouts: bool) -> Transaction {
// Décodez le hex et gérez l'erreur potentielle
let decoded_tx = hex::decode(tx_hex).expect("Failed to decode hex string");

// Désérialisez la transaction et gérez l'erreur potentielle
let transaction: bitcoin::blockdata::transaction::Transaction =
deserialize(&decoded_tx).expect("Failed to deserialize transaction");

return create_transaction(&transaction, config, height, parse_vouts);
}


impl ToBlock for Block {
fn to_block(&self, config: Config, height: u32) -> CrateBlock {
let mut transactions = Vec::new();
for tx in self.txdata.iter() {
transactions.push(create_transaction(tx, &config, height, true));
}
CrateBlock {
height,
Expand Down
20 changes: 20 additions & 0 deletions counterparty-rs/src/indexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,29 @@ impl Indexer {
}
}


#[pyclass]
pub struct Deserializer {
pub config: Config,
}

#[pymethods]
impl Deserializer {
#[new]
pub fn new(config: Config) -> PyResult<Self> {
Ok(Deserializer { config })
}

pub fn parse(&self, tx_hex: &str, height: u32, parse_vouts: bool, py: Python<'_>) -> PyResult<PyObject> {
let transaction = self::bitcoin_client::parse_transaction(tx_hex, &self.config, height, parse_vouts);
Ok(transaction.into_py(py))
}
}

pub fn register_indexer_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
let m = PyModule::new_bound(parent_module.py(), "indexer")?;
m.add_class::<Indexer>()?;
m.add_class::<Deserializer>()?;
parent_module.add_submodule(&m)?;
Ok(())
}

0 comments on commit 1dfbb7e

Please sign in to comment.