Skip to content

Commit

Permalink
feat(btc): Add tw_bitcoin_sign_psbt and tw_bitcoin_plan_psbt
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan committed Sep 20, 2024
1 parent a4385a6 commit c7d8407
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 30 deletions.
36 changes: 20 additions & 16 deletions rust/tw_tests/tests/chains/bitcoin/bitcoin_sign/psbt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ fn test_bitcoin_sign_psbt_thorchain_swap_witness() {
};

// Successfully broadcasted: https://mempool.space/tx/634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32
BitcoinPsbtSignHelper::new(&input).coin(CoinType::Bitcoin).sign_psbt(Expected {
psbt: "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d01086c02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000",
encoded: "02000000000101147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000",
txid: "634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32",
vsize: 216,
weight: 862,
fee: 1736,
});
BitcoinPsbtSignHelper::new(&input)
.coin(CoinType::Bitcoin)
.sign_psbt(Expected {
psbt: "70736274ff0100bc0200000001147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d000000000001011f6603010000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d01086c02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000",
encoded: "02000000000101147010db5fbcf619067c1090fec65c131443fbc80fb4aaeebe940e44206098c60000000000ffffffff0360ea000000000000160014f22a703617035ef7f490743d50f26ae08c30d0a70000000000000000426a403d3a474149412e41544f4d3a636f736d6f7331737377797a666d743675396a373437773537753438746778646575393573757a666c6d7175753a303a743a35303e12000000000000160014b139199ec796f36fc42e637f42da8e3e6720aa9d02483045022100b1229a008f20691639767bf925d6b8956ea957ccc633ad6b5de3618733a55e6b02205774d3320489b8a57a6f8de07f561de3e660ff8e587f6ac5422c49020cd4dc9101210306d8c664ea8fd2683eebea1d3114d90e0a5429e5783ba49b80ddabce04ff28f300000000",
txid: "634a416e82ac710166725f6a4090ac7b5db69687e86b2d2e38dcb3d91c956c32",
vsize: 216,
weight: 862,
fee: 1736,
});
}

#[test]
Expand All @@ -46,12 +48,14 @@ fn test_bitcoin_sign_psbt_thorchain_swap_non_witness() {
};

// Successfully broadcasted: https://mempool.space/tx/710e9270b57720f567ada156c6ac72177aa00a36789e2c6526fd80040fae3ce4
BitcoinPsbtSignHelper::new(&input).coin(CoinType::Bitcoin).sign_psbt(Expected {
psbt: "70736274ff01008202000000015c37bcf049b7e62dd5bfd707e0998ce86163b786e3cd45db2336cb794a8d8aa10000000000ffffffff03f82a000000000000160014bf5a13a26791a5db6406304a46952e264c2b28910000000000000000056a032b3a6291950000000000001976a9147c2c0ac72afbde13ecf52fca54368e7883b538b188ac000000000001007e0200000002714916920be4dbc87cbb8697ca9b1420d6b1e47e7d732e2d2e0e7a935087788d0000000000ffffffff326c951cd9b3dc382e2d6be88796b65d7bac90406a5f72660171ac826e414a630200000000ffffffff01efca0000000000001976a9147c2c0ac72afbde13ecf52fca54368e7883b538b188ac0000000001076a473044022057ce7a6147fd9e139df797adcec440bad60770f40cbd609363e3075b64d3eccd02200ae7dce5f7d1fa18c5e907a16c1b078fa90f537d36101447e53fbd058d2d950a0121036c3b7dfd678da989d91593e49918a6c9d8a1d37c7e9c0abeae2118c312e69b3100000000",
encoded: "02000000015c37bcf049b7e62dd5bfd707e0998ce86163b786e3cd45db2336cb794a8d8aa1000000006a473044022057ce7a6147fd9e139df797adcec440bad60770f40cbd609363e3075b64d3eccd02200ae7dce5f7d1fa18c5e907a16c1b078fa90f537d36101447e53fbd058d2d950a0121036c3b7dfd678da989d91593e49918a6c9d8a1d37c7e9c0abeae2118c312e69b31ffffffff03f82a000000000000160014bf5a13a26791a5db6406304a46952e264c2b28910000000000000000056a032b3a6291950000000000001976a9147c2c0ac72afbde13ecf52fca54368e7883b538b188ac00000000",
txid: "710e9270b57720f567ada156c6ac72177aa00a36789e2c6526fd80040fae3ce4",
vsize: 236,
weight: 944,
fee: 2662,
});
BitcoinPsbtSignHelper::new(&input)
.coin(CoinType::Bitcoin)
.sign_psbt(Expected {
psbt: "70736274ff01008202000000015c37bcf049b7e62dd5bfd707e0998ce86163b786e3cd45db2336cb794a8d8aa10000000000ffffffff03f82a000000000000160014bf5a13a26791a5db6406304a46952e264c2b28910000000000000000056a032b3a6291950000000000001976a9147c2c0ac72afbde13ecf52fca54368e7883b538b188ac000000000001007e0200000002714916920be4dbc87cbb8697ca9b1420d6b1e47e7d732e2d2e0e7a935087788d0000000000ffffffff326c951cd9b3dc382e2d6be88796b65d7bac90406a5f72660171ac826e414a630200000000ffffffff01efca0000000000001976a9147c2c0ac72afbde13ecf52fca54368e7883b538b188ac0000000001076a473044022057ce7a6147fd9e139df797adcec440bad60770f40cbd609363e3075b64d3eccd02200ae7dce5f7d1fa18c5e907a16c1b078fa90f537d36101447e53fbd058d2d950a0121036c3b7dfd678da989d91593e49918a6c9d8a1d37c7e9c0abeae2118c312e69b3100000000",
encoded: "02000000015c37bcf049b7e62dd5bfd707e0998ce86163b786e3cd45db2336cb794a8d8aa1000000006a473044022057ce7a6147fd9e139df797adcec440bad60770f40cbd609363e3075b64d3eccd02200ae7dce5f7d1fa18c5e907a16c1b078fa90f537d36101447e53fbd058d2d950a0121036c3b7dfd678da989d91593e49918a6c9d8a1d37c7e9c0abeae2118c312e69b31ffffffff03f82a000000000000160014bf5a13a26791a5db6406304a46952e264c2b28910000000000000000056a032b3a6291950000000000001976a9147c2c0ac72afbde13ecf52fca54368e7883b538b188ac00000000",
txid: "710e9270b57720f567ada156c6ac72177aa00a36789e2c6526fd80040fae3ce4",
vsize: 236,
weight: 944,
fee: 2662,
});
}
13 changes: 6 additions & 7 deletions rust/tw_tests/tests/chains/common/bitcoin/psbt_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
// Copyright © 2017 Trust Wallet.

use tw_coin_registry::coin_type::CoinType;
use tw_coin_registry::dispatcher::{coin_dispatcher, utxo_dispatcher};
use tw_memory::test_utils::tw_data_helper::TWDataHelper;
use tw_proto::BitcoinV2::Proto;
use tw_proto::{deserialize, serialize};
use wallet_core_rs::ffi::bitcoin::psbt::tw_bitcoin_plan_psbt;

pub struct BitcoinPsbtPlanHelper<'a> {
input: &'a Proto::PsbtSigningInput<'a>,
Expand All @@ -32,13 +33,11 @@ impl<'a> BitcoinPsbtPlanHelper<'a> {
.expect("'BitcoinSignHelper::coin_type' is not set");

let input = serialize(self.input).unwrap();
let input = TWDataHelper::create(input);

// TODO call `tw_bitcoin_plan_psbt` when all tests are moved to another crate.
let (ctx, _entry) = coin_dispatcher(coin_type).expect("Unknown CoinType");
let output_bytes = utxo_dispatcher(coin_type)
.expect("CoinType is not UTXO, i.e `utxo_dispatcher` failed")
.plan_psbt(&ctx, &input)
.unwrap();
let output =
TWDataHelper::wrap(unsafe { tw_bitcoin_plan_psbt(coin_type as u32, input.ptr()) });
let output_bytes = output.to_vec().unwrap();

let output: Proto::TransactionPlan = deserialize(&output_bytes).unwrap();

Expand Down
13 changes: 6 additions & 7 deletions rust/tw_tests/tests/chains/common/bitcoin/psbt_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
// Copyright © 2017 Trust Wallet.

use tw_coin_registry::coin_type::CoinType;
use tw_coin_registry::dispatcher::{coin_dispatcher, utxo_dispatcher};
use tw_encoding::hex::ToHex;
use tw_memory::test_utils::tw_data_helper::TWDataHelper;
use tw_proto::BitcoinV2::Proto;
use tw_proto::Common::Proto::SigningError;
use tw_proto::{deserialize, serialize};
use wallet_core_rs::ffi::bitcoin::psbt::tw_bitcoin_sign_psbt;

pub struct Expected {
/// Hex encoded PSBT.
Expand Down Expand Up @@ -44,13 +45,11 @@ impl<'a> BitcoinPsbtSignHelper<'a> {
.expect("'BitcoinSignHelper::coin_type' is not set");

let input = serialize(self.input).unwrap();
let input = TWDataHelper::create(input);

// TODO call `tw_bitcoin_sign_psbt` when all tests are moved to another crate.
let (ctx, _entry) = coin_dispatcher(coin_type).expect("Unknown CoinType");
let output_bytes = utxo_dispatcher(coin_type)
.expect("CoinType is not UTXO, i.e `utxo_dispatcher` failed")
.sign_psbt(&ctx, &input)
.unwrap();
let output =
TWDataHelper::wrap(unsafe { tw_bitcoin_sign_psbt(coin_type as u32, input.ptr()) });
let output_bytes = output.to_vec().unwrap();

let output: Proto::PsbtSigningOutput = deserialize(&output_bytes).unwrap();

Expand Down
2 changes: 2 additions & 0 deletions rust/wallet_core_rs/src/ffi/bitcoin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

pub mod psbt;
48 changes: 48 additions & 0 deletions rust/wallet_core_rs/src/ffi/bitcoin/psbt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#![allow(clippy::missing_safety_doc)]

use tw_coin_registry::coin_type::CoinType;
use tw_coin_registry::dispatcher::{coin_dispatcher, utxo_dispatcher};
use tw_memory::ffi::tw_data::TWData;
use tw_memory::ffi::RawPtrTrait;
use tw_misc::try_or_else;

/// Signs a PSBT (Partially Signed Bitcoin Transaction) specified by the signing input and coin type.
///
/// \param input The serialized data of a `Proto.PsbtSigningInput` protobuf message.
/// \param coin The given coin type to sign the PSBT for.
/// \return The serialized data of a `Proto.PsbtSigningOutput` protobuf message.
#[no_mangle]
pub unsafe extern "C" fn tw_bitcoin_sign_psbt(coin: u32, input: *const TWData) -> *mut TWData {
let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut);
let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut);
let utxo_dispatcher = try_or_else!(utxo_dispatcher(coin), std::ptr::null_mut);
let (coin_context, _) = try_or_else!(coin_dispatcher(coin), std::ptr::null_mut);

utxo_dispatcher
.sign_psbt(&coin_context, input_data.as_slice())
.map(|data| TWData::from(data).into_ptr())
.unwrap_or_else(|_| std::ptr::null_mut())
}

/// Plans a PSBT (Partially Signed Bitcoin Transaction).
/// Can be used to get the transaction detailed decoded from PSBT.
///
/// \param input The serialized data of a `Proto.PsbtSigningInput` protobuf message.
/// \param coin The given coin type to sign the PSBT for.
/// \return The serialized data of a `Proto.TransactionPlan` protobuf message.
#[no_mangle]
pub unsafe extern "C" fn tw_bitcoin_plan_psbt(coin: u32, input: *const TWData) -> *mut TWData {
let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut);
let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut);
let utxo_dispatcher = try_or_else!(utxo_dispatcher(coin), std::ptr::null_mut);
let (coin_context, _) = try_or_else!(coin_dispatcher(coin), std::ptr::null_mut);

utxo_dispatcher
.plan_psbt(&coin_context, input_data.as_slice())
.map(|data| TWData::from(data).into_ptr())
.unwrap_or_else(|_| std::ptr::null_mut())
}

0 comments on commit c7d8407

Please sign in to comment.