Skip to content

(draft) feat: add from_string to Transaction #804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bdk-ffi/src/bdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ interface TransactionError {
ParseFailed();
UnsupportedSegwitFlag(u8 flag);
OtherTransactionErr();
InvalidHexString();
};

// ------------------------------------------------------------------------
Expand Down
47 changes: 47 additions & 0 deletions bdk-ffi/src/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use bdk_wallet::bitcoin::consensus::encode::serialize;
use bdk_wallet::bitcoin::consensus::Decodable;
use bdk_wallet::bitcoin::hashes::sha256::Hash as BitcoinSha256Hash;
use bdk_wallet::bitcoin::hashes::sha256d::Hash as BitcoinDoubleSha256Hash;
use bdk_wallet::bitcoin::hex::FromHex;
use bdk_wallet::bitcoin::io::Cursor;
use bdk_wallet::bitcoin::secp256k1::Secp256k1;
use bdk_wallet::bitcoin::Amount as BdkAmount;
Expand Down Expand Up @@ -333,6 +334,13 @@ impl Transaction {
Ok(Transaction(tx))
}

/// Creates a new `Transaction` instance from a hexadecimal string representation.
#[uniffi::constructor]
pub fn from_string(tx_hex: String) -> Result<Self, TransactionError> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming convention>?

let tx_bytes = Vec::from_hex(&tx_hex)?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enforce consensus transaction?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self::new(tx_bytes)
}

/// Computes the Txid.
/// Hashes the transaction excluding the segwit data (i.e. the marker, flag bytes, and the witness fields themselves).
pub fn compute_txid(&self) -> Arc<Txid> {
Expand Down Expand Up @@ -677,6 +685,8 @@ impl_hash_like!(TxMerkleNode, BitcoinDoubleSha256Hash);
mod tests {
use crate::bitcoin::Address;
use crate::bitcoin::Network;
use crate::bitcoin::Transaction;
use crate::error::TransactionError;

#[test]
fn test_is_valid_for_network() {
Expand Down Expand Up @@ -1032,4 +1042,41 @@ mod tests {
let segwit_data = segwit.to_address_data();
println!("Segwit data: {:#?}", segwit_data);
}

#[test]
fn test_transaction_from_string() {
// A simple transaction hex (mainnet coinbase transaction)
let tx_hex = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";

// Test successful parsing
let tx = Transaction::from_string(tx_hex.to_string()).unwrap();

// Verify the transaction was parsed correctly
assert_eq!(tx.version(), 1);
assert_eq!(tx.input().len(), 1);
assert_eq!(tx.output().len(), 1);
assert!(tx.is_coinbase());

// Test that serializing and re-parsing gives the same result
let serialized = tx.serialize();
let tx2 = Transaction::new(serialized).unwrap();
assert_eq!(
tx.compute_txid().to_string(),
tx2.compute_txid().to_string()
);

// Test invalid hex string
let invalid_hex = "invalid_hex_string";
let result = Transaction::from_string(invalid_hex.to_string());
assert!(result.is_err());
match result.unwrap_err() {
TransactionError::InvalidHexString => {}
_ => panic!("Expected InvalidHexString error"),
}

// Test hex string with invalid transaction data
let invalid_tx_hex = "deadbeef";
let result = Transaction::from_string(invalid_tx_hex.to_string());
assert!(result.is_err());
}
}
11 changes: 10 additions & 1 deletion bdk-ffi/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bdk_wallet::bitcoin::amount::ParseAmountError as BdkParseAmountError;
use bdk_wallet::bitcoin::bip32::Error as BdkBip32Error;
use bdk_wallet::bitcoin::consensus::encode::Error as BdkEncodeError;
use bdk_wallet::bitcoin::hashes::hex::HexToArrayError as BdkHexToArrayError;
use bdk_wallet::bitcoin::hex::DisplayHex;
use bdk_wallet::bitcoin::hex::{DisplayHex, HexToBytesError};
use bdk_wallet::bitcoin::psbt::Error as BdkPsbtError;
use bdk_wallet::bitcoin::psbt::ExtractTxError as BdkExtractTxError;
use bdk_wallet::bitcoin::psbt::PsbtParseError as BdkPsbtParseError;
Expand Down Expand Up @@ -768,6 +768,9 @@ pub enum TransactionError {
// This is required because the bdk::bitcoin::consensus::encode::Error is non-exhaustive
#[error("other transaction error")]
OtherTransactionErr,

#[error("invalid hex string")]
InvalidHexString,
}

#[derive(Debug, thiserror::Error, uniffi::Error)]
Expand Down Expand Up @@ -1512,6 +1515,12 @@ impl From<BdkEncodeError> for TransactionError {
}
}

impl From<HexToBytesError> for TransactionError {
fn from(_: HexToBytesError) -> Self {
TransactionError::InvalidHexString
}
}

#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum HashParseError {
#[error("invalid hash: expected length 32 bytes, got {len} bytes")]
Expand Down
Loading