From 1e918a69d98d7417faf892ad0bfb8a86c2774db1 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Fri, 11 Apr 2025 21:26:33 +0330 Subject: [PATCH 01/12] v6.2.0 - Update dependencies. - Support PSBT for BCH - Support for BIP-173 - Support for BCH schnorr signing --- CHANGELOG.md | 7 + README.md | 12 +- .../lib/bitcoin_cash/burn_token_example.dart | 2 +- .../create_cash_token_example.dart | 2 +- .../lib/bitcoin_cash/create_nft_example.dart | 2 +- .../lib/bitcoin_cash/make_vout0_example.dart | 2 +- .../lib/bitcoin_cash/minting_nft_example.dart | 2 +- .../old_examples/old_example.dart | 12 +- .../bitcoin_cash/p2sh32_spend_example.dart | 2 +- .../bitcoin_cash/send_ft_token_example.dart | 2 +- .../bitcoin_cash/transfer_bch_example.dart | 2 +- example/lib/global/bch_example.dart | 2 +- ...amples_multisig_taproot_legacy_uncomp.dart | 4 +- .../global/old_examples/bitcoin_example.dart | 16 +- .../old_examples/dash_example/dash.dart | 4 +- .../doge_example/doge_example.dart | 12 +- .../litecoin_example/litecoin_example.dart | 20 +- .../spending_builders.dart | 14 +- .../spending_single_type.dart | 30 +- .../multi_sig_transactions.dart | 4 +- .../transaction_builder_example.dart | 4 +- ...er_from_7_account_to_6_accout_example.dart | 4 +- .../global/transfer_to_8_account_example.dart | 4 +- example/lib/main.dart | 12 +- example/lib/musig/musig_example.dart | 4 +- example/pubspec.lock | 80 +++--- example/pubspec.yaml | 4 +- lib/src/bitcoin/address/core.dart | 7 + lib/src/bitcoin/address/legacy_address.dart | 27 +- lib/src/bitcoin/address/network_address.dart | 52 ++++ lib/src/bitcoin/address/segwit_address.dart | 6 +- .../bitcoin/address/utils/address_utils.dart | 11 +- lib/src/bitcoin/script/op_code/constant.dart | 205 -------------- lib/src/bitcoin/script/utils.dart | 57 ++-- lib/src/bitcoin/taproot/utils/utils.dart | 1 - lib/src/bitcoin_cash/bcmr.dart | 3 + lib/src/crypto/keypair/ec_private.dart | 160 ++++++++--- lib/src/crypto/keypair/ec_public.dart | 192 +++++++++++-- lib/src/models/network.dart | 12 + lib/src/provider/models/multisig_script.dart | 3 +- lib/src/provider/models/utxo_details.dart | 11 +- lib/src/psbt/psbt_builder/impl/signer.dart | 1 - .../psbt_builder/types/internal_types.dart | 25 +- lib/src/psbt/signer/signer.dart | 8 +- lib/src/psbt/types/types/inputs.dart | 7 +- lib/src/psbt/utils/utils.dart | 256 ++++++++++++------ .../forked_transaction_builder.dart | 8 +- lib/src/utils/btc_utils.dart | 6 + pubspec.yaml | 6 +- test/deserialize_test.dart | 18 +- test/keys_test.dart | 56 +++- test/p2kh_tr_test.dart | 24 +- test/p2sh_test.dart | 6 +- test/p2tr_test.dart | 8 +- test/p2wpkh_test.dart | 28 +- test/p2wsh_test.dart | 14 +- 56 files changed, 890 insertions(+), 593 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7012651..c199f95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 6.2.0 + +- Update dependencies. +- Support PSBT for BCH +- Support for BIP-173 +- Support for BCH schnorr signing + ## 6.1.0 - Fix der signature validation. diff --git a/README.md b/README.md index 9c16cf0..22879f0 100644 --- a/README.md +++ b/README.md @@ -141,10 +141,10 @@ We have integrated three APIs—Mempool, BlockCypher, and Electrum—into the pl final publicKey = privateKey.getPublic(); // Sign an input using the private key. - final signSegwitV0OrLagacy = privateKey.signInput(); + final signSegwitV0OrLagacy = privateKey.signECDSA(); // Sign a Taproot transaction using the private key. - final signSegwitV1TapprotTransaction = privateKey.signTapRoot(); + final signSegwitV1TapprotTransaction = privateKey.signBip340(); // Convert the private key to a WIF (Wallet Import Format) encoded string. // The boolean argument specifies whether to use the compressed format. @@ -432,9 +432,9 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li final transaction = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { if (utxo.utxo.isP2tr()) { - return privateKey.signTapRoot(trDigest, sighash: sighash); + return privateKey.signBip340(trDigest, sighash: sighash); } - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// get tx id @@ -569,7 +569,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID @@ -645,7 +645,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li extFlags: 0, ); - // sign transaction using `signTapRoot` method of thransaction + // sign transaction using `signBip340` method of thransaction final signedTx = sign(txDigit, utxo[i].public().toHex(), SIGHASH_ALL); // add witness for current index diff --git a/example/lib/bitcoin_cash/burn_token_example.dart b/example/lib/bitcoin_cash/burn_token_example.dart index 31fc72c..b49fa02 100644 --- a/example/lib/bitcoin_cash/burn_token_example.dart +++ b/example/lib/bitcoin_cash/burn_token_example.dart @@ -128,7 +128,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/create_cash_token_example.dart b/example/lib/bitcoin_cash/create_cash_token_example.dart index 0b07035..9edbd44 100644 --- a/example/lib/bitcoin_cash/create_cash_token_example.dart +++ b/example/lib/bitcoin_cash/create_cash_token_example.dart @@ -121,7 +121,7 @@ void main() async { outputOrdering: BitcoinOrdering.none); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/create_nft_example.dart b/example/lib/bitcoin_cash/create_nft_example.dart index e9af101..64efa27 100644 --- a/example/lib/bitcoin_cash/create_nft_example.dart +++ b/example/lib/bitcoin_cash/create_nft_example.dart @@ -109,7 +109,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/make_vout0_example.dart b/example/lib/bitcoin_cash/make_vout0_example.dart index 4f1730c..b3f5746 100644 --- a/example/lib/bitcoin_cash/make_vout0_example.dart +++ b/example/lib/bitcoin_cash/make_vout0_example.dart @@ -65,7 +65,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/minting_nft_example.dart b/example/lib/bitcoin_cash/minting_nft_example.dart index 621c241..4e74ac7 100644 --- a/example/lib/bitcoin_cash/minting_nft_example.dart +++ b/example/lib/bitcoin_cash/minting_nft_example.dart @@ -165,7 +165,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/old_examples/old_example.dart b/example/lib/bitcoin_cash/old_examples/old_example.dart index 15403f5..1c52cd9 100644 --- a/example/lib/bitcoin_cash/old_examples/old_example.dart +++ b/example/lib/bitcoin_cash/old_examples/old_example.dart @@ -152,13 +152,13 @@ void _spendFrom2P2SHAnd2P2PKHAddress() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -353,13 +353,13 @@ void _spendFrom2P2SHAnd1P2PKHAddress() async { ]); final tr = b.buildTransaction((trDigest, utxo, publicKey, int sighash) { if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); diff --git a/example/lib/bitcoin_cash/p2sh32_spend_example.dart b/example/lib/bitcoin_cash/p2sh32_spend_example.dart index 0081118..d621909 100644 --- a/example/lib/bitcoin_cash/p2sh32_spend_example.dart +++ b/example/lib/bitcoin_cash/p2sh32_spend_example.dart @@ -98,7 +98,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/send_ft_token_example.dart b/example/lib/bitcoin_cash/send_ft_token_example.dart index cfbfc2b..0b589f5 100644 --- a/example/lib/bitcoin_cash/send_ft_token_example.dart +++ b/example/lib/bitcoin_cash/send_ft_token_example.dart @@ -124,7 +124,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/bitcoin_cash/transfer_bch_example.dart b/example/lib/bitcoin_cash/transfer_bch_example.dart index a51ac29..324ff80 100644 --- a/example/lib/bitcoin_cash/transfer_bch_example.dart +++ b/example/lib/bitcoin_cash/transfer_bch_example.dart @@ -84,7 +84,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/global/bch_example.dart b/example/lib/global/bch_example.dart index 73b5952..f6caefe 100644 --- a/example/lib/global/bch_example.dart +++ b/example/lib/global/bch_example.dart @@ -81,7 +81,7 @@ void main() async { ); final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID diff --git a/example/lib/global/btc_examples_multisig_taproot_legacy_uncomp.dart b/example/lib/global/btc_examples_multisig_taproot_legacy_uncomp.dart index 617fa11..41e331a 100644 --- a/example/lib/global/btc_examples_multisig_taproot_legacy_uncomp.dart +++ b/example/lib/global/btc_examples_multisig_taproot_legacy_uncomp.dart @@ -125,9 +125,9 @@ void main() async { bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { final pk = ECPublic.fromHex(publicKey); if (utxo.utxo.isP2tr) { - return keys[pk.toHex()]!.signTapRoot(trDigest, sighash: sighash); + return keys[pk.toHex()]!.signBip340(trDigest, sighash: sighash); } - return keys[pk.toHex()]!.signInput(trDigest, sigHash: sighash); + return keys[pk.toHex()]!.signECDSA(trDigest, sighash: sighash); }); final transactionRaw = transaaction.toHex(); diff --git a/example/lib/global/old_examples/bitcoin_example.dart b/example/lib/global/old_examples/bitcoin_example.dart index 4084aa1..2f64098 100644 --- a/example/lib/global/old_examples/bitcoin_example.dart +++ b/example/lib/global/old_examples/bitcoin_example.dart @@ -167,9 +167,9 @@ void _spendFromP2pkhTo10DifferentType() async { if (publicKey == examplePublicKey2.toHex()) { if (utxo.utxo.isP2tr) { - return examplePrivateKey.signTapRoot(trDigest); + return examplePrivateKey.signBip340(trDigest); } - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -401,21 +401,21 @@ void _spendFrom10DifferentTypeToP2pkh() async { /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { if (utxo.utxo.isP2tr) { - return childKey1PrivateKey.signTapRoot(trDigest, sighash: sighash); + return childKey1PrivateKey.signBip340(trDigest, sighash: sighash); } - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { if (utxo.utxo.isP2tr) { - return childKey2PrivateKey.signTapRoot(trDigest, sighash: sighash); + return childKey2PrivateKey.signBip340(trDigest, sighash: sighash); } - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { if (utxo.utxo.isP2tr) { - return examplePrivateKey.signTapRoot(trDigest, sighash: sighash); + return examplePrivateKey.signBip340(trDigest, sighash: sighash); } - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); diff --git a/example/lib/global/old_examples/dash_example/dash.dart b/example/lib/global/old_examples/dash_example/dash.dart index 0479f3d..d6a8880 100644 --- a/example/lib/global/old_examples/dash_example/dash.dart +++ b/example/lib/global/old_examples/dash_example/dash.dart @@ -126,7 +126,7 @@ void _spendFromTwoP2shAndOneP2PKH() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -225,7 +225,7 @@ void _spendP2SH() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); }); diff --git a/example/lib/global/old_examples/doge_example/doge_example.dart b/example/lib/global/old_examples/doge_example/doge_example.dart index 1ff568c..6eab54b 100644 --- a/example/lib/global/old_examples/doge_example/doge_example.dart +++ b/example/lib/global/old_examples/doge_example/doge_example.dart @@ -129,13 +129,13 @@ void _spendFrom3P2shAddress() async { ]); final tr = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -280,13 +280,13 @@ void _spendFromP2pkhAndP2sh() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); diff --git a/example/lib/global/old_examples/litecoin_example/litecoin_example.dart b/example/lib/global/old_examples/litecoin_example/litecoin_example.dart index f225cb3..eb55185 100644 --- a/example/lib/global/old_examples/litecoin_example/litecoin_example.dart +++ b/example/lib/global/old_examples/litecoin_example/litecoin_example.dart @@ -129,7 +129,7 @@ void _spendLTCP2pkhAddress() async { /// and sign the transaction digest to construct the unlocking script. if (publicKey == pub.toHex()) { /// sign the transaction input using specified sighash or default to SIGHASH_ALL - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -269,13 +269,13 @@ void _spendFrom2P2shAddressAndOneMultiSigP2shAddress() async { /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -381,13 +381,13 @@ void _spendFromNestedSegwitP2WPKHInP2SH() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -501,13 +501,13 @@ void _spendFromSegwitP2WPKHAddress() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); diff --git a/example/lib/global/old_examples/spending_with_scripts/spending_builders.dart b/example/lib/global/old_examples/spending_with_scripts/spending_builders.dart index 0f49320..f3eff8c 100644 --- a/example/lib/global/old_examples/spending_with_scripts/spending_builders.dart +++ b/example/lib/global/old_examples/spending_with_scripts/spending_builders.dart @@ -2,7 +2,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; BtcTransaction buildP2wpkTransaction({ required List receiver, - required String Function(List, String publicKey, int sigHash) sign, + required String Function(List, String publicKey, int sighash) sign, required List utxo, }) { // We define transaction inputs by specifying the transaction ID and index. @@ -66,7 +66,7 @@ BtcTransaction buildP2wpkTransaction({ BtcTransaction buildP2WSHTransaction({ required List receiver, - required String Function(List, String publicKey, int sigHash) sign, + required String Function(List, String publicKey, int sighash) sign, required List utxo, }) { // We define transaction inputs by specifying the transaction ID and index. @@ -132,7 +132,7 @@ BtcTransaction buildP2WSHTransaction({ BtcTransaction buildP2pkhTransaction({ required List receiver, - required String Function(List, String publicKey, int sigHash) sign, + required String Function(List, String publicKey, int sighash) sign, required List utxo, }) { // We define transaction inputs by specifying the transaction ID and index. @@ -178,7 +178,7 @@ BtcTransaction buildP2pkhTransaction({ BtcTransaction buildP2shNoneSegwitTransaction({ required List receiver, - required String Function(List, String publicKey, int sigHash) sign, + required String Function(List, String publicKey, int sighash) sign, required List utxo, }) { // We define transaction inputs by specifying the transaction ID and index. @@ -238,7 +238,7 @@ BtcTransaction buildP2shNoneSegwitTransaction({ BtcTransaction buildP2SHSegwitTransaction({ required List receiver, - required String Function(List, String publicKey, int sigHash) sign, + required String Function(List, String publicKey, int sighash) sign, required List utxo, }) { // We define transaction inputs by specifying the transaction ID and index. @@ -323,7 +323,7 @@ BtcTransaction buildP2SHSegwitTransaction({ BtcTransaction buildP2trTransaction({ required List receiver, - required String Function(List, String publicKey, int sigHash) sign, + required String Function(List, String publicKey, int sighash) sign, required List utxo, }) { // We define transaction inputs by specifying the transaction ID and index. @@ -373,7 +373,7 @@ BtcTransaction buildP2trTransaction({ sighash: BitcoinOpCodeConst.sighashDefault, ); - // sign transaction using `signTapRoot` method of thransaction + // sign transaction using `signBip340` method of thransaction final signedTx = sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); diff --git a/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart b/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart index 1c7acee..9e51c71 100644 --- a/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart +++ b/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart @@ -83,11 +83,11 @@ Future spendingP2WPKH(ECPrivate sWallet, ECPrivate rWallet) async { // You can refer to this method to learn how to create a transaction. final transaction = buildP2wpkTransaction( receiver: outPutWithValue, - sign: (p0, publicKey, sigHash) { + sign: (p0, publicKey, sighash) { // Here, we find the corresponding private key based on the public key and proceed to sign the transaction." - // Note that to sign Taproot transactions, you must use the 'signTapRoot' method for signing. + // Note that to sign Taproot transactions, you must use the 'signBip340' method for signing. // Below is a method for spending Taproot transactions that you can review. - return prive.signInput(p0, sigHash: sigHash); + return prive.signECDSA(p0, sighash: sighash); }, utxo: utxo, ); @@ -143,8 +143,8 @@ Future spendingP2WSH(ECPrivate sWallet, ECPrivate rWallet) async { .toList(); final transaction = buildP2WSHTransaction( receiver: outPutWithValue, - sign: (p0, publicKey, sigHash) { - return prive.signInput(p0, sigHash: sigHash); + sign: (p0, publicKey, sighash) { + return prive.signECDSA(p0, sighash: sighash); }, utxo: utxo, ); @@ -192,8 +192,8 @@ Future spendingP2PKH(ECPrivate sWallet, ECPrivate rWallet) async { final transaction = buildP2pkhTransaction( receiver: outPutWithValue, - sign: (p0, publicKey, sigHash) { - return prive.signInput(p0, sigHash: sigHash); + sign: (p0, publicKey, sighash) { + return prive.signECDSA(p0, sighash: sighash); }, utxo: utxo, ); @@ -243,8 +243,8 @@ Future spendingP2SHNoneSegwit( .toList(); final transaction = buildP2shNoneSegwitTransaction( receiver: outPutWithValue, - sign: (p0, publicKey, sigHash) { - return prive.signInput(p0, sigHash: sigHash); + sign: (p0, publicKey, sighash) { + return prive.signECDSA(p0, sighash: sighash); }, utxo: utxo, ); @@ -296,8 +296,8 @@ Future spendingP2shSegwit(ECPrivate sWallet, ECPrivate rWallet) async { // return; final transaction = buildP2SHSegwitTransaction( receiver: outPutWithValue, - sign: (p0, publicKey, sigHash) { - return prive.signInput(p0, sigHash: sigHash); + sign: (p0, publicKey, sighash) { + return prive.signECDSA(p0, sighash: sighash); }, utxo: utxo, ); @@ -311,7 +311,7 @@ Future spendingP2TR(ECPrivate sWallet, ECPrivate rWallet) async { // All the steps are the same as in the first tutorial; // the only difference is the transaction input type, // and we use method `buildP2trTransaction` to create the transaction. - // we use `signTapRoot` of ECPrivate for signing taproot transaction + // we use `signBip340` of ECPrivate for signing taproot transaction final addr = sWallet.getPublic(); // P2TR address final sender = addr.toTaprootAddress(); @@ -346,9 +346,9 @@ Future spendingP2TR(ECPrivate sWallet, ECPrivate rWallet) async { final transaction = buildP2trTransaction( receiver: outPutWithValue, - sign: (p0, publicKey, sigHash) { - // Use signTapRoot instead of signInput for the taproot transaction input. - return prive.signTapRoot(p0, sighash: sigHash, tweak: true); + sign: (p0, publicKey, sighash) { + // Use signBip340 instead of signECDSA for the taproot transaction input. + return prive.signBip340(p0, sighash: sighash, tweak: true); }, utxo: utxo, ); diff --git a/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart b/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart index e9a5a85..2320f5d 100644 --- a/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart +++ b/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart @@ -269,10 +269,10 @@ void main() async { // yes is p2tr utxo and now we use SignTaprootTransaction(Schnorr sign) // for now this transaction builder support only tweak transaction // If you want to spend a Taproot script-path spending, you must create your own transaction builder. - return key.signTapRoot(trDigest, sighash: sighash); + return key.signBip340(trDigest, sighash: sighash); } else { // is seqwit(v0) or lagacy address we use SingInput (ECDSA) - return key.signInput(trDigest, sigHash: sighash); + return key.signECDSA(trDigest, sighash: sighash); } }); diff --git a/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart b/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart index ecfd778..49fd392 100644 --- a/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart +++ b/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart @@ -216,10 +216,10 @@ void main() async { // yes is p2tr utxo and now we use SignTaprootTransaction(Schnorr sign) // for now this transaction builder support only tweak transaction // If you want to spend a Taproot script-path spending, you must create your own transaction builder. - return key.signTapRoot(trDigest, sighash: sighash); + return key.signBip340(trDigest, sighash: sighash); } else { // is seqwit(v0) or lagacy address we use SingInput (ECDSA) - return key.signInput(trDigest, sigHash: sighash); + return key.signECDSA(trDigest, sighash: sighash); } }); diff --git a/example/lib/global/transfer_from_7_account_to_6_accout_example.dart b/example/lib/global/transfer_from_7_account_to_6_accout_example.dart index bdfcaee..da2876f 100644 --- a/example/lib/global/transfer_from_7_account_to_6_accout_example.dart +++ b/example/lib/global/transfer_from_7_account_to_6_accout_example.dart @@ -159,9 +159,9 @@ void main() async { final transaction = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { if (utxo.utxo.isP2tr) { - return privateKey.signTapRoot(trDigest, sighash: sighash); + return privateKey.signBip340(trDigest, sighash: sighash); } - return privateKey.signInput(trDigest, sigHash: sighash); + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// get tx id diff --git a/example/lib/global/transfer_to_8_account_example.dart b/example/lib/global/transfer_to_8_account_example.dart index 3469d37..b0c869c 100644 --- a/example/lib/global/transfer_to_8_account_example.dart +++ b/example/lib/global/transfer_to_8_account_example.dart @@ -138,9 +138,9 @@ void main() async { final transaction = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { if (utxo.utxo.isP2tr) { - return examplePrivateKey2.signTapRoot(trDigest, sighash: sighash); + return examplePrivateKey2.signBip340(trDigest, sighash: sighash); } - return examplePrivateKey2.signInput(trDigest, sigHash: sighash); + return examplePrivateKey2.signECDSA(trDigest, sighash: sighash); }); /// get tx id diff --git a/example/lib/main.dart b/example/lib/main.dart index 5e85d9f..0775f1f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -152,13 +152,13 @@ void _spendFrom2P2SHAnd2P2PKHAddress() async { /// For each input in the transaction, locate the corresponding private key /// and sign the transaction digest to construct the unlocking script. if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); @@ -353,13 +353,13 @@ void _spendFrom2P2SHAnd1P2PKHAddress() async { ]); final tr = b.buildTransaction((trDigest, utxo, publicKey, int sighash) { if (publicKey == childKey1PublicKey.toHex()) { - return childKey1PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey1PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey.toHex()) { - return childKey2PrivateKey.signInput(trDigest, sigHash: sighash); + return childKey2PrivateKey.signECDSA(trDigest, sighash: sighash); } if (publicKey == examplePublicKey2.toHex()) { - return examplePrivateKey.signInput(trDigest, sigHash: sighash); + return examplePrivateKey.signECDSA(trDigest, sighash: sighash); } throw UnimplementedError(); diff --git a/example/lib/musig/musig_example.dart b/example/lib/musig/musig_example.dart index fdbf52f..4dcab65 100644 --- a/example/lib/musig/musig_example.dart +++ b/example/lib/musig/musig_example.dart @@ -143,8 +143,8 @@ BtcTransaction _spendWithLeafC( existsUtxosScriptPubKeys.map((e) => e.toScriptPubKey()).toList(), amounts: existsUtxosAmounts) .asImmutableBytes; - final sig1 = key1.signTapRoot(digest, tweak: false); - final sig2 = key2.signTapRoot(digest, tweak: false); + final sig1 = key1.signBip340(digest, tweak: false); + final sig2 = key2.signBip340(digest, tweak: false); final controlBlock = TaprootControlBlock.generate( xOnlyOrInternalPubKey: internalKey.toXOnly(), leafScript: leafC, diff --git a/example/pubspec.lock b/example/pubspec.lock index b6caa97..ef10dfa 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,57 +5,57 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" bitcoin_base: dependency: "direct main" description: path: ".." relative: true source: path - version: "6.0.0" + version: "6.2.0" blockchain_utils: dependency: "direct main" description: name: blockchain_utils - sha256: "2f0e9bed57e55a0bdb8dfa7471aab41d83c488f7865c0bb8a55ce67b4f87f486" + sha256: "104f5212ade36b01a67511b074feed2ece513876d45c8daace277818cdc16873" url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.4.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -68,10 +68,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -110,18 +110,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -142,10 +142,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -158,18 +158,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -179,50 +179,50 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" typed_data: dependency: transitive description: @@ -243,10 +243,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" web: dependency: transitive description: @@ -256,5 +256,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 30eb12e..49619de 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -38,8 +38,8 @@ dependencies: bitcoin_base: path: ../ # blockchain_utils: - # path: ../../../blockchain_utils - blockchain_utils: ^4.3.0 + # path: ../../blockchain_utils + blockchain_utils: ^4.4.0 http: ^1.2.0 dev_dependencies: diff --git a/lib/src/bitcoin/address/core.dart b/lib/src/bitcoin/address/core.dart index 72b3231..2a1ab47 100644 --- a/lib/src/bitcoin/address/core.dart +++ b/lib/src/bitcoin/address/core.dart @@ -20,6 +20,13 @@ abstract class BitcoinAddressType implements Enumerate { int get hashLength; bool get isP2sh32 => isP2sh && hashLength == 32; + bool get supportBip137 => switch (this) { + P2pkhAddressType.p2pkh || P2pkhAddressType.p2pkhwt => true, + P2shAddressType.p2wpkhInP2sh => true, + SegwitAddressType.p2wpkh => true, + _ => false + }; + // Enum values as a list for iteration static const List values = [ P2pkhAddressType.p2pkh, diff --git a/lib/src/bitcoin/address/legacy_address.dart b/lib/src/bitcoin/address/legacy_address.dart index 0b7627d..ec189ca 100644 --- a/lib/src/bitcoin/address/legacy_address.dart +++ b/lib/src/bitcoin/address/legacy_address.dart @@ -65,7 +65,8 @@ class P2shAddress extends LegacyAddress { } return P2shAddress.fromHash160( addrHash: BytesUtils.toHexString( - QuickCrypto.sha256DoubleHash(script.toBytes()))); + QuickCrypto.sha256DoubleHash(script.toBytes())), + type: addressType); } P2shAddress.fromScript( {required super.script, this.type = P2shAddressType.p2pkInP2sh}) @@ -95,10 +96,18 @@ class P2shAddress extends LegacyAddress { /// Returns the scriptPubKey (P2SH) that corresponds to this address @override Script toScriptPubKey() { - if (addressProgram.length == 64) { - return Script(script: ['OP_HASH256', addressProgram, 'OP_EQUAL']); + if (addressProgram.length == QuickCrypto.sha256DigestSize * 2) { + return Script(script: [ + BitcoinOpcode.opHash256, + addressProgram, + BitcoinOpcode.opEqual + ]); } - return Script(script: ['OP_HASH160', addressProgram, 'OP_EQUAL']); + return Script(script: [ + BitcoinOpcode.opHash160, + addressProgram, + BitcoinOpcode.opEqual + ]); } @override @@ -129,11 +138,11 @@ class P2pkhAddress extends LegacyAddress { @override Script toScriptPubKey() { return Script(script: [ - 'OP_DUP', - 'OP_HASH160', + BitcoinOpcode.opDup, + BitcoinOpcode.opHash160, addressProgram, - 'OP_EQUALVERIFY', - 'OP_CHECKSIG' + BitcoinOpcode.opEqualVerify, + BitcoinOpcode.opCheckSig ]); } @@ -154,7 +163,7 @@ class P2pkAddress extends LegacyAddress { @override Script toScriptPubKey() { - return Script(script: [publicKey, 'OP_CHECKSIG']); + return Script(script: [publicKey, BitcoinOpcode.opCheckSig]); } @override diff --git a/lib/src/bitcoin/address/network_address.dart b/lib/src/bitcoin/address/network_address.dart index 62770b4..3147dee 100644 --- a/lib/src/bitcoin/address/network_address.dart +++ b/lib/src/bitcoin/address/network_address.dart @@ -6,6 +6,58 @@ abstract class BitcoinNetworkAddress { {required this.address, required this.network, required this.baseAddress}); + static ADDRESS parse
( + {required String address, required BasedUtxoNetwork network}) { + BitcoinNetworkAddress baseAddress; + switch (network) { + case BitcoinSVNetwork.mainnet: + case BitcoinSVNetwork.testnet: + baseAddress = + BitcoinSVAddress(address, network: network as BitcoinSVNetwork); + case BitcoinNetwork.mainnet: + case BitcoinNetwork.testnet: + case BitcoinNetwork.testnet4: + baseAddress = + BitcoinAddress(address, network: network as BitcoinNetwork); + case LitecoinNetwork.mainnet: + case LitecoinNetwork.testnet: + baseAddress = + LitecoinAddress(address, network: network as LitecoinNetwork); + case DashNetwork.mainnet: + case DashNetwork.testnet: + baseAddress = DashAddress(address, network: network as DashNetwork); + case DogecoinNetwork.mainnet: + case DogecoinNetwork.testnet: + baseAddress = DogeAddress(address, network: network as DogecoinNetwork); + case BitcoinCashNetwork.mainnet: + case BitcoinCashNetwork.testnet: + baseAddress = + BitcoinCashAddress(address, network: network as BitcoinCashNetwork); + case PepeNetwork.mainnet: + baseAddress = PepeAddress(address, network: network as PepeNetwork); + case ElectraProtocolNetwork.mainnet: + case ElectraProtocolNetwork.testnet: + baseAddress = ElectraProtocolAddress(address, + network: network as ElectraProtocolNetwork); + default: + throw DartBitcoinPluginException("Unknown network. ${network.value}"); + } + if (baseAddress is! ADDRESS) { + throw DartBitcoinPluginException( + "Invalid cast: expected ${ADDRESS.runtimeType}, but found ${baseAddress.runtimeType}.", + ); + } + return baseAddress; + } + + static ADDRESS? tryParse
( + {required String address, required BasedUtxoNetwork network}) { + try { + return parse(address: address, network: network); + } catch (_) { + return null; + } + } /// The underlying Bitcoin base address. final BitcoinBaseAddress baseAddress; diff --git a/lib/src/bitcoin/address/segwit_address.dart b/lib/src/bitcoin/address/segwit_address.dart index 20a2eda..0e56559 100644 --- a/lib/src/bitcoin/address/segwit_address.dart +++ b/lib/src/bitcoin/address/segwit_address.dart @@ -71,7 +71,7 @@ class P2wpkhAddress extends SegwitAddress { /// returns the scriptPubKey of a P2WPKH witness script @override Script toScriptPubKey() { - return Script(script: ['OP_0', addressProgram]); + return Script(script: [BitcoinOpcode.op0, addressProgram]); } /// returns the type of address @@ -102,7 +102,7 @@ class P2trAddress extends SegwitAddress { /// returns the scriptPubKey of a P2TR witness script @override Script toScriptPubKey() { - return Script(script: ['OP_1', addressProgram]); + return Script(script: [BitcoinOpcode.op1, addressProgram]); } /// returns the type of address @@ -123,7 +123,7 @@ class P2wshAddress extends SegwitAddress { /// Returns the scriptPubKey of a P2WPKH witness script @override Script toScriptPubKey() { - return Script(script: ['OP_0', addressProgram]); + return Script(script: [BitcoinOpcode.op0, addressProgram]); } /// Returns the type of address diff --git a/lib/src/bitcoin/address/utils/address_utils.dart b/lib/src/bitcoin/address/utils/address_utils.dart index a3df388..f564e86 100644 --- a/lib/src/bitcoin/address/utils/address_utils.dart +++ b/lib/src/bitcoin/address/utils/address_utils.dart @@ -3,10 +3,10 @@ part of 'package:bitcoin_base/src/bitcoin/address/address.dart'; /// Utility class for working with Bitcoin addresses and related operations. class _BitcoinAddressUtils { /// Length of a script hash in bytes. - static const int scriptHashLenght = 32; + static const int scriptHashLenght = QuickCrypto.sha256DigestSize; /// Length of a hash160 digest in bytes. - static const int hash160DigestLength = 20; + static const int hash160DigestLength = QuickCrypto.hash160DigestSize; /// Segregated Witness version 0. static const int segwitV0 = 0; @@ -158,7 +158,7 @@ class _BitcoinAddressUtils { } baseAddress ??= toLegacy(address, network); if (baseAddress == null) { - throw const DartBitcoinPluginException('Invalid Bitcoin address'); + throw const DartBitcoinPluginException('Invalid Bitcoin address.'); } return validateAddress(baseAddress, network); } @@ -178,8 +178,7 @@ class _BitcoinAddressUtils { if (toBytes.length == addressType.hashLength) { return StringUtils.strip0x(hash160.toLowerCase()); } - // ignore: empty_catches - } catch (e) {} + } catch (_) {} throw const DartBitcoinPluginException( 'Invalid Bitcoin address program length (program length should be 32 or 20 bytes)'); } @@ -205,7 +204,7 @@ class _BitcoinAddressUtils { final version = decode.item1; return _validateBchScriptBytes( network: network, scriptBytes: scriptBytes, version: version); - } catch (e) { + } catch (_) { return null; } } diff --git a/lib/src/bitcoin/script/op_code/constant.dart b/lib/src/bitcoin/script/op_code/constant.dart index d761839..59ac86e 100644 --- a/lib/src/bitcoin/script/op_code/constant.dart +++ b/lib/src/bitcoin/script/op_code/constant.dart @@ -127,210 +127,6 @@ class BitcoinOpCodeConst { byte == BitcoinOpCodeConst.opPushData4; } - // static const Map> OP_CODES = { - // 'OP_0': [0x00], - // 'OP_FALSE': [0x00], - // 'OP_PUSHDATA1': [0x4c], - // 'OP_PUSHDATA2': [0x4d], - // 'OP_PUSHDATA4': [0x4e], - // 'OP_1NEGATE': [0x4f], - // 'OP_1': [0x51], - // 'OP_TRUE': [0x51], - // 'OP_2': [0x52], - // 'OP_3': [0x53], - // 'OP_4': [0x54], - // 'OP_5': [0x55], - // 'OP_6': [0x56], - // 'OP_7': [0x57], - // 'OP_8': [0x58], - // 'OP_9': [0x59], - // 'OP_10': [0x5a], - // 'OP_11': [0x5b], - // 'OP_12': [0x5c], - // 'OP_13': [0x5d], - // 'OP_14': [0x5e], - // 'OP_15': [0x5f], - // 'OP_16': [0x60], - - // /// flow control - // 'OP_NOP': [0x61], - // 'OP_IF': [0x63], - // 'OP_NOTIF': [0x64], - // 'OP_ELSE': [0x67], - // 'OP_ENDIF': [0x68], - // 'OP_VERIFY': [0x69], - // 'OP_RETURN': [0x6a], - - // /// stack - // 'OP_TOALTSTACK': [0x6b], - // 'OP_FROMALTSTACK': [0x6c], - // 'OP_IFDUP': [0x73], - // 'OP_DEPTH': [0x74], - // 'OP_DROP': [0x75], - // 'OP_DUP': [0x76], - // 'OP_NIP': [0x77], - // 'OP_OVER': [0x78], - // 'OP_PICK': [0x79], - // 'OP_ROLL': [0x7a], - // 'OP_ROT': [0x7b], - // 'OP_SWAP': [0x7c], - // 'OP_TUCK': [0x7d], - // 'OP_2DROP': [0x6d], - // 'OP_2DUP': [0x6e], - // 'OP_3DUP': [0x6f], - // 'OP_2OVER': [0x70], - // 'OP_2ROT': [0x71], - // 'OP_2SWAP': [0x72], - // 'OP_SIZE': [0x82], - // 'OP_EQUAL': [0x87], - // 'OP_EQUALVERIFY': [0x88], - - // /// arithmetic - // 'OP_1ADD': [0x8b], - // 'OP_1SUB': [0x8c], - // 'OP_NEGATE': [0x8f], - // 'OP_ABS': [0x90], - // 'OP_NOT': [0x91], - // 'OP_0NOTEQUAL': [0x92], - // 'OP_ADD': [0x93], - // 'OP_SUB': [0x94], - // 'OP_BOOLAND': [0x9a], - // 'OP_BOOLOR': [0x9b], - // 'OP_NUMEQUAL': [0x9c], - // 'OP_NUMEQUALVERIFY': [0x9d], - // 'OP_NUMNOTEQUAL': [0x9e], - // 'OP_LESSTHAN': [0x9f], - // 'OP_GREATERTHAN': [0xa0], - // 'OP_LESSTHANOREQUAL': [0xa1], - // 'OP_GREATERTHANOREQUAL': [0xa2], - // 'OP_MIN': [0xa3], - // 'OP_MAX': [0xa4], - // 'OP_WITHIN': [0xa5], - - // /// crypto - // 'OP_RIPEMD160': [0xa6], - // 'OP_SHA1': [0xa7], - // 'OP_SHA256': [0xa8], - // 'OP_HASH160': [0xa9], - // 'OP_HASH256': [0xaa], - // 'OP_CODESEPARATOR': [0xab], - // 'OP_CHECKSIG': [0xac], - // 'OP_CHECKSIGVERIFY': [0xad], - // 'OP_CHECKMULTISIG': [0xae], - // 'OP_CHECKMULTISIGVERIFY': [0xaf], - // "OP_CHECKSIGADD": [0xba], - - // /// locktime - // // 'OP_NOP2': [0xb1], - // 'OP_CHECKLOCKTIMEVERIFY': [0xb1], - // // 'OP_NOP3': [0xb2], - // 'OP_CHECKSEQUENCEVERIFY': [0xb2], - // }; - - // static final Map CODE_OPS = { - // /// constants - // 0: 'OP_0', - // // 0: 'OP_FALSE', - // 76: 'OP_PUSHDATA1', - // 77: 'OP_PUSHDATA2', - // 78: 'OP_PUSHDATA4', - // 79: 'OP_1NEGATE', - // 81: 'OP_1', - // 82: 'OP_2', - // 83: 'OP_3', - // 84: 'OP_4', - // 85: 'OP_5', - // 86: 'OP_6', - // 87: 'OP_7', - // 88: 'OP_8', - // 89: 'OP_9', - // 90: 'OP_10', - // 91: 'OP_11', - // 92: 'OP_12', - // 93: 'OP_13', - // 94: 'OP_14', - // 95: 'OP_15', - // 96: 'OP_16', - - // /// flow control - // 97: 'OP_NOP', - // 99: 'OP_IF', - // 100: 'OP_NOTIF', - // 103: 'OP_ELSE', - // 104: 'OP_ENDIF', - // 105: 'OP_VERIFY', - // 106: 'OP_RETURN', - - // /// stack - // 107: 'OP_TOALTSTACK', - // 108: 'OP_FROMALTSTACK', - // 115: 'OP_IFDUP', - // 116: 'OP_DEPTH', - // 117: 'OP_DROP', - // 118: 'OP_DUP', - // 119: 'OP_NIP', - // 120: 'OP_OVER', - // 121: 'OP_PICK', - // 122: 'OP_ROLL', - // 123: 'OP_ROT', - // 124: 'OP_SWAP', - // 125: 'OP_TUCK', - // 109: 'OP_2DROP', - // 110: 'OP_2DUP', - // 111: 'OP_3DUP', - // 112: 'OP_2OVER', - // 113: 'OP_2ROT', - // 114: 'OP_2SWAP', - - // /// splice - // 130: 'OP_SIZE', - - // /// bitwise logic - // 135: 'OP_EQUAL', - // 136: 'OP_EQUALVERIFY', - - // /// arithmetic - // 139: 'OP_1ADD', - // 140: 'OP_1SUB', - // 143: 'OP_NEGATE', - // 144: 'OP_ABS', - // 145: 'OP_NOT', - // 146: 'OP_0NOTEQUAL', - // 147: 'OP_ADD', - // 148: 'OP_SUB', - // 154: 'OP_BOOLAND', - // 155: 'OP_BOOLOR', - // 156: 'OP_NUMEQUAL', - // 157: 'OP_NUMEQUALVERIFY', - // 158: 'OP_NUMNOTEQUAL', - // 159: 'OP_LESSTHAN', - // 160: 'OP_GREATERTHAN', - // 161: 'OP_LESSTHANOREQUAL', - // 162: 'OP_GREATERTHANOREQUAL', - // 163: 'OP_MIN', - // 164: 'OP_MAX', - // 165: 'OP_WITHIN', - - // /// crypto - // 166: 'OP_RIPEMD160', - // 167: 'OP_SHA1', - // 168: 'OP_SHA256', - // 169: 'OP_HASH160', - // 170: 'OP_HASH256', - // 171: 'OP_CODESEPARATOR', - // 172: 'OP_CHECKSIG', - // 173: 'OP_CHECKSIGVERIFY', - // 174: 'OP_CHECKMULTISIG', - // 175: 'OP_CHECKMULTISIGVERIFY', - // 0xba: "OP_CHECKSIGADD", - - // /// locktime - // // 177: 'OP_NOP2', - // // 178: 'OP_NOP3', - // 177: 'OP_CHECKLOCKTIMEVERIFY', - // 178: 'OP_CHECKSEQUENCEVERIFY', - // }; - static const int sighashSingle = 0x03; static const int sighashAnyoneCanPay = 0x80; static const int sighashAll = 0x01; @@ -369,7 +165,6 @@ class BitcoinOpCodeConst { static const int sighashByteLength = 4; static const String opReturn = "OP_RETURN"; static const String opTrue = "OP_TRUE"; - static const String opCheckSig = "OP_CHECKSIG"; static const String opCheckMultiSig = "OP_CHECKMULTISIG"; static const String opCheckMultiSigVerify = "OP_CHECKMULTISIGVERIFY"; static const String opCheckSigAdd = "OP_CHECKSIGADD"; diff --git a/lib/src/bitcoin/script/utils.dart b/lib/src/bitcoin/script/utils.dart index 19c673b..07af9c8 100644 --- a/lib/src/bitcoin/script/utils.dart +++ b/lib/src/bitcoin/script/utils.dart @@ -22,7 +22,7 @@ enum ScriptPubKeyType { this == ScriptPubKeyType.p2wsh || this == ScriptPubKeyType.p2tr; bool get isP2tr => this == ScriptPubKeyType.p2tr; - bool get isP2sh => this == ScriptPubKeyType.p2sh; + bool get isP2sh => isP2sh32 || this == ScriptPubKeyType.p2sh; bool get isP2sh32 => this == ScriptPubKeyType.p2sh32; } @@ -33,7 +33,11 @@ class BitcoinScriptUtils { for (int i = 0; i < script.script.length; i++) { final element = elements[i]; if (element != null) { - if (script.script[i] != element) { + if (element is BitcoinOpcode) { + if (script.script[i] != element.name) { + return false; + } + } else if (script.script[i] != element) { return false; } } @@ -43,11 +47,11 @@ class BitcoinScriptUtils { static bool isP2pkh(Script script) { if (scriptContains(script: script, elements: [ - 'OP_DUP', - 'OP_HASH160', + BitcoinOpcode.opDup, + BitcoinOpcode.opHash160, null, - 'OP_EQUALVERIFY', - 'OP_CHECKSIG' + BitcoinOpcode.opEqualVerify, + BitcoinOpcode.opCheckSig ])) { final addressProgram = script.script[2]; return (addressProgram is String && addressProgram.length == 40); @@ -57,7 +61,8 @@ class BitcoinScriptUtils { static bool isP2sh(Script script) { if (scriptContains( - script: script, elements: ['OP_HASH160', null, 'OP_EQUAL'])) { + script: script, + elements: [BitcoinOpcode.opHash160, null, BitcoinOpcode.opEqual])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[1]); return pubKeyBytes?.length == P2shAddressType.p2pkInP2sh.hashLength; } @@ -66,7 +71,8 @@ class BitcoinScriptUtils { static bool isP2sh32(Script script) { if (scriptContains( - script: script, elements: ['OP_HASH256', null, 'OP_EQUAL'])) { + script: script, + elements: [BitcoinOpcode.opHash256, null, BitcoinOpcode.opEqual])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[1]); return pubKeyBytes?.length == P2shAddressType.p2pkhInP2sh32.hashLength; } @@ -74,7 +80,8 @@ class BitcoinScriptUtils { } static bool isP2pk(Script script) { - if (scriptContains(script: script, elements: [null, "OP_CHECKSIG"])) { + if (scriptContains( + script: script, elements: [null, BitcoinOpcode.opCheckSig])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[0]); return pubKeyBytes?.length == EcdsaKeysConst.pubKeyCompressedByteLen || pubKeyBytes?.length == EcdsaKeysConst.pubKeyUncompressedByteLen; @@ -83,7 +90,7 @@ class BitcoinScriptUtils { } static bool isP2tr(Script script) { - if (scriptContains(script: script, elements: ['OP_1', null])) { + if (scriptContains(script: script, elements: [BitcoinOpcode.op1, null])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[1]); return pubKeyBytes?.length == SegwitAddressType.p2tr.hashLength; } @@ -91,7 +98,7 @@ class BitcoinScriptUtils { } static bool isP2wpkh(Script script) { - if (scriptContains(script: script, elements: ['OP_0', null])) { + if (scriptContains(script: script, elements: [BitcoinOpcode.op0, null])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[1]); return pubKeyBytes?.length == SegwitAddressType.p2wpkh.hashLength; } @@ -100,7 +107,8 @@ class BitcoinScriptUtils { static bool isRipemd160(Script script) { if (scriptContains( - script: script, elements: ['OP_RIPEMD160', null, "OP_EQUAL"])) { + script: script, + elements: [BitcoinOpcode.opRipemd160, null, BitcoinOpcode.opEqual])) { final toBytes = BytesUtils.tryFromHexString(script.script[1]); return toBytes?.length == QuickCrypto.hash160DigestSize; } @@ -109,7 +117,8 @@ class BitcoinScriptUtils { static bool isSha256(Script script) { if (scriptContains( - script: script, elements: ['OP_SHA256', null, "OP_EQUAL"])) { + script: script, + elements: [BitcoinOpcode.opSha256, null, BitcoinOpcode.opEqual])) { final toBytes = BytesUtils.tryFromHexString(script.script[1]); return toBytes?.length == QuickCrypto.sha256DigestSize; } @@ -118,7 +127,8 @@ class BitcoinScriptUtils { static bool isHash256(Script script) { if (scriptContains( - script: script, elements: ['OP_HASH256', null, "OP_EQUAL"])) { + script: script, + elements: [BitcoinOpcode.opHash256, null, BitcoinOpcode.opEqual])) { final toBytes = BytesUtils.tryFromHexString(script.script[1]); return toBytes?.length == QuickCrypto.sha256DigestSize; } @@ -127,7 +137,8 @@ class BitcoinScriptUtils { static bool isHash160(Script script) { if (scriptContains( - script: script, elements: ['OP_HASH160', null, "OP_EQUAL"])) { + script: script, + elements: [BitcoinOpcode.opHash160, null, BitcoinOpcode.opEqual])) { final toBytes = BytesUtils.tryFromHexString(script.script[1]); return toBytes?.length == QuickCrypto.hash160DigestSize; } @@ -135,7 +146,7 @@ class BitcoinScriptUtils { } static bool isP2wsh(Script script) { - if (scriptContains(script: script, elements: ['OP_0', null])) { + if (scriptContains(script: script, elements: [BitcoinOpcode.op0, null])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[1]); return pubKeyBytes?.length == SegwitAddressType.p2wsh.hashLength; } @@ -143,7 +154,8 @@ class BitcoinScriptUtils { } static bool isXOnlyOpChecksig(Script script) { - if (scriptContains(script: script, elements: [null, "OP_CHECKSIG"])) { + if (scriptContains( + script: script, elements: [null, BitcoinOpcode.opCheckSig])) { final xOnlyKey = BytesUtils.tryFromHexString(script.script[0]); return xOnlyKey?.length == EcdsaKeysConst.pointCoordByteLen; } @@ -181,7 +193,8 @@ class BitcoinScriptUtils { } static bool isPubKeyOpCheckSig(Script script) { - if (scriptContains(script: script, elements: [null, "OP_CHECKSIG"])) { + if (scriptContains( + script: script, elements: [null, BitcoinOpcode.opCheckSig])) { final pubKeyBytes = BytesUtils.tryFromHexString(script.script[0]); return pubKeyBytes?.length == EcdsaKeysConst.pubKeyCompressedByteLen || pubKeyBytes?.length == EcdsaKeysConst.pubKeyUncompressedByteLen; @@ -225,8 +238,8 @@ class BitcoinScriptUtils { static bool isMultisigScript(Script script) { final opCodes = script.script; if (opCodes.length < 4) return false; - if (opCodes.last != 'OP_CHECKMULTISIG' && - opCodes.last != "OP_CHECKMULTISIGVERIFY") { + if (opCodes.last != BitcoinOpcode.opCheckMultiSig.name && + opCodes.last != BitcoinOpcode.opCheckMultiSigVerify.name) { return false; } final int? threshold = decodeOpN(opCodes.first.toString()); @@ -244,8 +257,8 @@ class BitcoinScriptUtils { static MultiSignatureAddress? parseMultisigScript(Script script) { final opCodes = script.script; if (opCodes.length < 4) return null; - if (opCodes.last != 'OP_CHECKMULTISIG' && - opCodes.last != "OP_CHECKMULTISIGVERIFY") { + if (opCodes.last != BitcoinOpcode.opCheckMultiSig.name && + opCodes.last != BitcoinOpcode.opCheckMultiSigVerify.name) { return null; } final int? threshold = decodeOpN(opCodes.first.toString()); diff --git a/lib/src/bitcoin/taproot/utils/utils.dart b/lib/src/bitcoin/taproot/utils/utils.dart index 2e05424..e49cc29 100644 --- a/lib/src/bitcoin/taproot/utils/utils.dart +++ b/lib/src/bitcoin/taproot/utils/utils.dart @@ -73,7 +73,6 @@ class TaprootUtils { if (xKey.length == EcdsaKeysConst.pubKeyCompressedByteLen) { xKey = xKey.sublist(1); } - final tweak = calculateTweek(xKey, treeScript: treeScript, merkleRoot: merkleRoot); return tweakInternalKey(xKey, tweak); diff --git a/lib/src/bitcoin_cash/bcmr.dart b/lib/src/bitcoin_cash/bcmr.dart index 9d97032..3998290 100644 --- a/lib/src/bitcoin_cash/bcmr.dart +++ b/lib/src/bitcoin_cash/bcmr.dart @@ -42,4 +42,7 @@ class BCMR implements BitcoinScriptOutput { /// output value. always zero @override final BigInt value = BigInt.zero; + + @override + CashToken? get token => null; } diff --git a/lib/src/crypto/keypair/ec_private.dart b/lib/src/crypto/keypair/ec_private.dart index aaa506b..2b2ee7f 100644 --- a/lib/src/crypto/keypair/ec_private.dart +++ b/lib/src/crypto/keypair/ec_private.dart @@ -6,6 +6,35 @@ import 'package:blockchain_utils/blockchain_utils.dart'; import 'ec_public.dart'; +enum BIP137Mode { + p2pkhUncompressed(0), + p2pkhCompressed(4), + p2shP2wpkh(8), + p2wpkh(12); + + const BIP137Mode(this.header); + static BIP137Mode fromValue(int? header) { + return values.firstWhere((e) => e.header == header, + orElse: () => throw DartBitcoinPluginException( + "No BIP137Mode found for the given header value")); + } + + final int header; + static BIP137Mode findMode(int header) { + if (header < 27 || header > 42) { + throw DartBitcoinPluginException("Header byte out of range"); + } + if (header >= 39) { + return BIP137Mode.p2wpkh; + } else if (header >= 35) { + return BIP137Mode.p2shP2wpkh; + } else if (header >= 31) { + return BIP137Mode.p2pkhCompressed; + } + return BIP137Mode.p2pkhUncompressed; + } +} + /// Represents an ECDSA private key. class ECPrivate { final Bip32PrivateKey prive; @@ -28,7 +57,7 @@ class ECPrivate { ECPublic.fromHex(BytesUtils.toHexString(prive.publicKey.compressed)); /// creates an object from a WIF of WIFC format (string) - factory ECPrivate.fromWif(String wif, {required List? netVersion}) { + factory ECPrivate.fromWif(String wif, {List? netVersion}) { final decode = WifDecoder.decode(wif, netVer: netVersion ?? BitcoinNetwork.mainnet.wifNetVer); return ECPrivate.fromBytes(decode.item1); @@ -47,34 +76,88 @@ class ECPrivate { return prive.raw; } + /// returns the key's as hex String toHex() { return BytesUtils.toHexString(prive.raw); } - /// Returns a Bitcoin compact signature in hex + /// Signs a message using BIP-137 format for standardized Bitcoin message signing. + /// + /// This method produces a compact ECDSA signature with a modified recovery ID + /// based on the specified BIP-137 signing mode. + /// + /// - [message]: The raw message to be signed. + /// - [messagePrefix]: The prefix used for Bitcoin's message signing + /// (default is `BitcoinSignerUtils.signMessagePrefix`). + /// - [mode]: The BIP-137 mode specifying the key type (e.g., P2PKH uncompressed, compressed, SegWit, etc.). + /// - [extraEntropy]: Optional extra entropy to modify the signature (default is an empty list). + /// + /// The recovery ID (first byte of the signature) is adjusted based on the + /// BIP-137 mode's header value. The final signature is encoded in Base64. + String signBip137( + List message, { + String messagePrefix = BitcoinSignerUtils.signMessagePrefix, + BIP137Mode mode = BIP137Mode.p2pkhUncompressed, + List extraEntropy = const [], + }) { + final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); + final signature = btcSigner.signMessage( + message: message, + messagePrefix: messagePrefix, + extraEntropy: extraEntropy); + int rId = signature[0] + mode.header; + return StringUtils.decode([rId, ...signature.sublist(1)], + type: StringEncoding.base64); + } + + /// Signs a message using Bitcoin's message signing format. + /// + /// This method produces a compact ECDSA signature for a given message, following + /// the Bitcoin Signed Message standard. + /// + /// - [message]: The raw message to be signed. + /// - [messagePrefix]: The prefix used for Bitcoin's message signing. + /// - [extraEntropy]: Optional extra entropy to modify the signature. String signMessage(List message, - {String messagePrefix = '\x18Bitcoin Signed Message:\n'}) { - final btcSigner = BitcoinSigner.fromKeyBytes(toBytes()); - final signature = btcSigner.signMessage(message, messagePrefix); - return BytesUtils.toHexString(signature); + {String messagePrefix = BitcoinSignerUtils.signMessagePrefix, + List extraEntropy = const []}) { + final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); + final signature = btcSigner.signMessage( + message: message, + messagePrefix: messagePrefix, + extraEntropy: extraEntropy); + return BytesUtils.toHexString(signature.sublist(1)); } - /// sign transaction digest and returns the signature. - String signInput(List txDigest, - {int? sigHash = BitcoinOpCodeConst.sighashAll}) { - final btcSigner = BitcoinSigner.fromKeyBytes(toBytes()); - List signature = btcSigner.signTransaction(txDigest); - if (sigHash != null) { - signature = [...signature, sigHash]; + /// Signs the given transaction digest using ECDSA (DER-encoded). + /// + /// - [txDigest]: The transaction digest (message) to sign. + /// - [sighash]: The sighash flag to append (default is SIGHASH_ALL). + String signECDSA(List txDigest, + {int? sighash = BitcoinOpCodeConst.sighashAll, + List extraEntropy = const []}) { + final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); + List signature = + btcSigner.signECDSADer(txDigest, extraEntropy: extraEntropy); + if (sighash != null) { + signature = [...signature, sighash]; } return BytesUtils.toHexString(signature); } + /// Signs the given transaction digest using Schnorr signature (old style). + /// + /// This method is primarily useful for networks like Bitcoin Cash (BCH) that + /// support Schnorr signatures in a legacy format. + /// In BCH OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY, will not be upgraded to allow Schnorr signatures. + /// https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-05-15-schnorr.md + /// - [txDigest]: The transaction digest (message) to sign. + /// - [sighash]: The sighash flag to append (default is SIGHASH_DEFAULT). String signSchnorr(List txDigest, - {int sighash = BitcoinOpCodeConst.sighashDefault}) { - final btcSigner = BitcoinSigner.fromKeyBytes(toBytes()); - var signature = btcSigner.signSchnorrTransaction(txDigest, - tapScripts: [], tweak: false); + {int sighash = BitcoinOpCodeConst.sighashDefault, + List extraEntropy = const []}) { + final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); + var signature = btcSigner.signSchnorr(txDigest, extraEntropy: extraEntropy); if (sighash != BitcoinOpCodeConst.sighashDefault) { signature = [...signature, sighash]; } @@ -88,21 +171,36 @@ class ECPrivate { /// - [treeScript]: Taproot script tree for Tweaking with public key. /// - [merkleRoot]: Merkle root for the Taproot tree. If provided, this overrides the default computation of the Merkle root from [treeScript]. /// - [tweak]: If `true`, the internal key is tweaked, either with or without [treeScript] or [merkleRoot], before signing. - String signTapRoot(List txDigest, + /// - [tapTweakHash]: If provided, it will be used directly instead of tweaking with the internal key. + String signBip340(List txDigest, {int sighash = BitcoinOpCodeConst.sighashDefault, TaprootTree? treeScript, List? merkleRoot, + List? tapTweakHash, + List? aux, bool tweak = true}) { - if (!tweak && treeScript != null) { + if (!tweak && + (treeScript != null || merkleRoot != null || tapTweakHash != null)) { + throw DartBitcoinPluginException( + "Invalid parameters: 'tweak' must be true when specifying 'treeScript', 'merkleRoot', or 'tapTweakHash'."); + } + if (merkleRoot != null && treeScript != null) { throw DartBitcoinPluginException( - "Invalid parameters: 'tweak' must be true when using 'treeScript'."); + "Use either merkleRoot or treeScript to generate merkle, not both."); } - final btcSigner = BitcoinSigner.fromKeyBytes(toBytes()); - List signature = btcSigner.signSchnorrTx(txDigest, - tweak: tweak - ? TaprootUtils.calculateTweek(getPublic().toXOnly(), - treeScript: merkleRoot != null ? null : treeScript, - merkleRoot: merkleRoot) + if (tapTweakHash != null && (treeScript != null || merkleRoot != null)) { + throw DartBitcoinPluginException( + "Use either tapTweakHash or (treeScript/merkleRoot), not both."); + } + final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); + List signature = btcSigner.signBip340( + digest: txDigest, + aux: aux, + tapTweakHash: tweak + ? tapTweakHash ?? + TaprootUtils.calculateTweek(getPublic().toXOnly(), + treeScript: merkleRoot != null ? null : treeScript, + merkleRoot: merkleRoot) : null); if (sighash != BitcoinOpCodeConst.sighashDefault) { signature = [...signature, sighash]; @@ -110,16 +208,6 @@ class ECPrivate { return BytesUtils.toHexString(signature); } - /// Signs a Taproot transaction digest and returns the signature. - /// - /// - [txDigest]: The transaction digest to be signed. - /// - [tweak]: Optional public key tweak to be applied when signing. - List signBtcSchnorr(List txDigest, {List? tweak}) { - final btcSigner = BitcoinSigner.fromKeyBytes(toBytes()); - List signature = btcSigner.signSchnorrTx(txDigest, tweak: tweak); - return signature; - } - static ECPrivate random() { final secret = QuickCrypto.generateRandom(); return ECPrivate.fromBytes(secret); diff --git a/lib/src/crypto/keypair/ec_public.dart b/lib/src/crypto/keypair/ec_public.dart index e2d3272..f33d3dc 100644 --- a/lib/src/crypto/keypair/ec_public.dart +++ b/lib/src/crypto/keypair/ec_public.dart @@ -1,4 +1,6 @@ +import 'package:bitcoin_base/bitcoin_base.dart' show BIP137Mode; import 'package:bitcoin_base/src/bitcoin/address/address.dart'; +import 'package:bitcoin_base/src/bitcoin/script/op_code/constant.dart'; import 'package:bitcoin_base/src/bitcoin/script/script.dart'; import 'package:bitcoin_base/src/bitcoin/taproot/taproot.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; @@ -75,14 +77,15 @@ class ECPublic { /// toRedeemScript generates a redeem script from the ECPublic key. /// If 'compressed' is true, the key is in compressed format. Script toRedeemScript({PublicKeyType mode = PublicKeyType.compressed}) { - return Script(script: [toHex(mode: mode), 'OP_CHECKSIG']); + return Script(script: [toHex(mode: mode), BitcoinOpcode.opCheckSig]); } /// toP2pkhInP2sh generates a P2SH (Pay-to-Script-Hash) address /// wrapping a P2PK (Pay-to-Public-Key) script derived from the ECPublic key. /// If 'compressed' is true, the key is in compressed format. P2shAddress toP2pkhInP2sh( - {PublicKeyType mode = PublicKeyType.compressed, useBCHP2sh32 = false}) { + {PublicKeyType mode = PublicKeyType.compressed, + bool useBCHP2sh32 = false}) { final addr = toAddress(mode: mode); final script = addr.toScriptPubKey(); if (useBCHP2sh32) { @@ -134,7 +137,12 @@ class ECPublic { /// toP2wshScript generates a P2WSH (Pay-to-Witness-Script-Hash) script /// derived from the ECPublic key. If 'compressed' is true, the key is in compressed format. Script toP2wshScript() { - return Script(script: ['OP_1', toHex(), 'OP_1', 'OP_CHECKMULTISIG']); + return Script(script: [ + BitcoinOpcode.op1, + toHex(), + BitcoinOpcode.op1, + BitcoinOpcode.opCheckMultiSig + ]); } /// toP2wshAddress generates a P2WSH (Pay-to-Witness-Script-Hash) address @@ -179,30 +187,168 @@ class ECPublic { return BytesUtils.toHexString(toXOnly()); } - /// returns true if the message was signed with this public key's - bool verify(List message, List signature, - {String messagePrefix = '\x18Bitcoin Signed Message:\n'}) { - final verifyKey = BitcoinVerifier.fromKeyBytes(toBytes()); - return verifyKey.verifyMessage(message, messagePrefix, signature); + /// Verifies a Bitcoin signed message using the provided signature. + /// + /// This method checks if the given signature is valid for the specified message, + /// following Bitcoin's message signing format. + /// + /// - [message]: The original message that was signed. + /// - [signature]: The compact ECDSA signature to verify. + /// - [messagePrefix]: The prefix used in Bitcoin's message signing. + bool verify( + {required List message, + required List signature, + String messagePrefix = BitcoinSignerUtils.signMessagePrefix}) { + final verifyKey = BitcoinSignatureVerifier.fromKeyBytes(toBytes()); + return verifyKey.verifyMessageSignature( + message: message, messagePrefix: messagePrefix, signature: signature); + } + + /// Recovers the BIP-137 public key from a signed message and signature. + /// + /// This method extracts the public key from a Bitcoin-signed message using the + /// BIP-137 standard, which allows for signature-based public key recovery. + /// + /// - [message]: The original message that was signed. + /// - [signature]: The Base64-encoded signature. + /// - [messagePrefix]: The prefix used in Bitcoin's message signing. + ECPublic getBip137PublicKey( + {required List message, + required String signature, + String messagePrefix = BitcoinSignerUtils.signMessagePrefix}) { + final signatureBytes = + StringUtils.encode(signature, type: StringEncoding.base64); + final ecdsaPubKey = BitcoinSignatureVerifier.recoverPublicKey( + message: message, + signature: signatureBytes, + messagePrefix: messagePrefix); + return ECPublic.fromBytes(ecdsaPubKey.toBytes()); } - /// returns true if the message was signed with this public key's - bool verifyTransaactionSignature(List message, List signature) { - final verifyKey = BitcoinVerifier.fromKeyBytes(toBytes()); - return verifyKey.verifyTransaction(message, signature); + /// Recovers the BIP-137 address from a signed message and signature. + /// + /// This method extracts the public key from a Bitcoin-signed message using the + /// BIP-137 standard, and then derives the appropriate Bitcoin address based on + /// the signature's recovery mode (e.g., P2PKH, P2WPKH, P2SH-P2WPKH). + /// + /// - [message]: The original message that was signed. + /// - [signature]: The Base64-encoded signature. + /// - [messagePrefix]: The prefix used in Bitcoin's message signing + /// (default is `BitcoinSignerUtils.signMessagePrefix`). + /// + /// Returns the corresponding Bitcoin address derived from the recovered public key. + /// The address type is determined by the recovery mode of the signature (e.g., + /// uncompressed, compressed, SegWit, or P2SH-wrapped SegWit). + BitcoinBaseAddress getBip137Address( + {required List message, + required String signature, + String messagePrefix = BitcoinSignerUtils.signMessagePrefix}) { + final signatureBytes = + StringUtils.encode(signature, type: StringEncoding.base64); + final ecdsaPubKey = BitcoinSignatureVerifier.recoverPublicKey( + message: message, + signature: signatureBytes, + messagePrefix: messagePrefix); + final publicKey = ECPublic.fromBytes(ecdsaPubKey.toBytes()); + final mode = BIP137Mode.findMode(signatureBytes[0]); + return switch (mode) { + BIP137Mode.p2pkhUncompressed => + publicKey.toAddress(mode: PubKeyModes.uncompressed), + BIP137Mode.p2pkhCompressed => publicKey.toAddress(), + BIP137Mode.p2wpkh => publicKey.toSegwitAddress(), + BIP137Mode.p2shP2wpkh => publicKey.toP2wpkhInP2sh() + }; + } + + /// Verifies that a BIP-137 signature matches the expected Bitcoin address. + /// + /// This method checks whether the address derived from the BIP-137 signature + /// matches the provided address by comparing the corresponding scriptPubKey. + /// + /// - [message]: The original message that was signed. + /// - [signature]: The Base64-encoded signature to verify. + /// - [address]: The expected Bitcoin address to compare against. + /// - [messagePrefix]: The prefix used in Bitcoin's message signing + bool verifyBip137Address( + {required List message, + required String signature, + required BitcoinBaseAddress address, + String messagePrefix = BitcoinSignerUtils.signMessagePrefix}) { + final signerAddress = getBip137Address( + message: message, signature: signature, messagePrefix: messagePrefix); + return address.toScriptPubKey() == signerAddress.toScriptPubKey(); + } + + /// Verifies an ECDSA DER-encoded signature against a given digest. + /// + /// This method checks whether the provided DER-encoded signature is valid for + /// the given digest using the public key. + /// + /// - [digest]: The hash or message digest that was signed. + /// - [signature]: The DER-encoded ECDSA signature to verify. + /// + /// Returns `true` if the signature is valid for the given digest, otherwise `false`. + bool verifyDerSignature( + {required List digest, required List signature}) { + final verifyKey = BitcoinSignatureVerifier.fromKeyBytes(toBytes()); + return verifyKey.verifyECDSADerSignature( + digest: digest, signature: signature); + } + + /// Verifies a BIP-340 Taproot signature for a given message. + /// + /// - [digest]: The original message or transaction digest that was signed. + /// - [signature]: The BIP-340 signature to verify. + /// - [treeScript]: Taproot script tree for Tweaking with public key. + /// - [merkleRoot]: Merkle root for the Taproot tree. If provided, this overrides the default computation of the Merkle root from [treeScript]. + /// - [tweak]: If `true`, the internal key is tweaked, either with or without [treeScript] or [merkleRoot], before verifying. + /// - [tapTweakHash]: If provided, it will be used directly instead of tweaking with the internal key. + bool verifyBip340Signature( + {required List digest, + required List signature, + TaprootTree? treeScript, + List? merkleRoot, + List? tapTweakHash, + bool tweak = true}) { + if (!tweak && + (treeScript != null || merkleRoot != null || tapTweakHash != null)) { + throw DartBitcoinPluginException( + "Invalid parameters: 'tweak' must be true when specifying 'treeScript', 'merkleRoot', or 'tapTweakHash'."); + } + if (merkleRoot != null && treeScript != null) { + throw DartBitcoinPluginException( + "Use either merkleRoot or treeScript to generate merkle, not both."); + } + if (tapTweakHash != null && (treeScript != null || merkleRoot != null)) { + throw DartBitcoinPluginException( + "Use either tapTweakHash or (treeScript/merkleRoot), not both."); + } + final verifyKey = BitcoinSignatureVerifier.fromKeyBytes(toBytes()); + return verifyKey.verifyBip340Signature( + digest: digest, + signature: signature, + tapTweakHash: tweak + ? tapTweakHash ?? + TaprootUtils.calculateTweek(toXOnly(), + treeScript: merkleRoot != null ? null : treeScript, + merkleRoot: merkleRoot) + : null); } - /// returns true if the message was signed with this public key's - bool verifySchnorrTransactionSignature(List message, List signature, - {List> tapleafScripts = const [], bool isTweak = true}) { - final verifyKey = BitcoinVerifier.fromKeyBytes(toBytes()); - final tapScriptBytes = !isTweak - ? [] - : tapleafScripts - .map((e) => e.map((e) => e.toBytes()).toList()) - .toList(); - return verifyKey.verifySchnorr(message, signature, - tapleafScripts: tapScriptBytes, isTweak: isTweak); + /// Verifies a Schnorr(old style) signature for a given digest. + /// + /// This method checks whether the provided Schnorr signature is valid for + /// the given digest using the public key. + /// + /// - [digest]: The hash or message digest that was signed. + /// - [signature]: The Schnorr signature to verify. + /// + /// Returns `true` if the signature is valid for the given digest, otherwise `false`. + bool verifySchnorrSignature( + {required List digest, required List signature}) { + final verifyKey = BitcoinSignatureVerifier.fromKeyBytes(toBytes()); + return verifyKey.verifySchnorrSignature( + digest: digest, signature: signature); } @override diff --git a/lib/src/models/network.dart b/lib/src/models/network.dart index 4a863cf..440d5e5 100644 --- a/lib/src/models/network.dart +++ b/lib/src/models/network.dart @@ -3,6 +3,18 @@ import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:bitcoin_base/src/utils/enumerate.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +class MessagePrefixes { + static const String bitcoinMainnet = "\x18Bitcoin Signed Message:\n"; + static const String litecoinMainnet = "\x19Litecoin Signed Message:\n"; + static const String dashMainnet = "\x19DarkCoin Signed Message:\n"; + static const String dogecoinMainnet = "\x19Dogecoin Signed Message:\n"; + static const String bitcoinCashMainnet = "\x1cBitcoin Cash Signed Message:\n"; + static const String bitcoinSVMainnet = "\x18Bitcoin Signed Message:\n"; + static const String pepeMainnet = "\x15Pepe Signed Message:\n"; + static const String electraProtocolMainnet = + "\x20Electra Protocol Signed Message:\n"; +} + /// Abstract class representing a base for UTXO-based cryptocurrency networks. abstract class BasedUtxoNetwork implements Enumerate { /// List of version bytes for Wallet Import Format (WIF). diff --git a/lib/src/provider/models/multisig_script.dart b/lib/src/provider/models/multisig_script.dart index bff9f50..a1542da 100644 --- a/lib/src/provider/models/multisig_script.dart +++ b/lib/src/provider/models/multisig_script.dart @@ -1,4 +1,5 @@ import 'package:bitcoin_base/src/bitcoin/address/address.dart'; +import 'package:bitcoin_base/src/bitcoin/script/op_code/constant.dart'; import 'package:bitcoin_base/src/bitcoin/script/script.dart'; import 'package:bitcoin_base/src/bitcoin/taproot/taproot.dart'; import 'package:bitcoin_base/src/crypto/crypto.dart'; @@ -211,7 +212,7 @@ class P2trMultiSignatureAddress { for (var w = 0; w < signer.weight; w++) { if (i == 0 && w == 0) { multiSigScript.add(signer.xOnly); - multiSigScript.add("OP_CHECKSIG"); + multiSigScript.add(BitcoinOpcode.opCheckSig.name); continue; } multiSigScript.add(signer.xOnly); diff --git a/lib/src/provider/models/utxo_details.dart b/lib/src/provider/models/utxo_details.dart index 79238c3..5a0bc28 100644 --- a/lib/src/provider/models/utxo_details.dart +++ b/lib/src/provider/models/utxo_details.dart @@ -96,6 +96,10 @@ abstract class BitcoinBaseOutput { TxOutput get toOutput; } +abstract class BCHBaseOutput extends BitcoinOutput { + BCHBaseOutput({required super.address, required super.value}); +} + /// Abstract class representing a spendable Bitcoin output, extending BitcoinBaseOutput. abstract class BitcoinSpendableBaseOutput implements BitcoinBaseOutput { /// The Bitcoin address associated with this output. @@ -130,12 +134,15 @@ class BitcoinScriptOutput implements BitcoinBaseOutput { /// The value (amount) of the Bitcoin output. final BigInt value; - const BitcoinScriptOutput({required this.script, required this.value}); + + final CashToken? token; + const BitcoinScriptOutput( + {required this.script, required this.value, this.token}); /// Convert the custom script output to a standard TxOutput. @override TxOutput get toOutput => - TxOutput(amount: value, scriptPubKey: script, cashToken: null); + TxOutput(amount: value, scriptPubKey: script, cashToken: token); } /// BitcoinTokenOutput represents details about a Bitcoin cash transaction with cash token output, including diff --git a/lib/src/psbt/psbt_builder/impl/signer.dart b/lib/src/psbt/psbt_builder/impl/signer.dart index 1bc7a8c..36e29ed 100644 --- a/lib/src/psbt/psbt_builder/impl/signer.dart +++ b/lib/src/psbt/psbt_builder/impl/signer.dart @@ -146,7 +146,6 @@ mixin PsbtSignerImpl on PsbtBuilderImpl { await signer.btcSignInputAsync(digest.createRequest(signer)); final psbtSignature = digest.createSignature(signature, signer); final sighash = digest.getPsbtSigHash(); - print("come here ${sighash?.sighash}"); _psbt.input .updateInputs(index, [psbtSignature, if (sighash != null) sighash]); } diff --git a/lib/src/psbt/psbt_builder/types/internal_types.dart b/lib/src/psbt/psbt_builder/types/internal_types.dart index 93e6440..8ff64ba 100644 --- a/lib/src/psbt/psbt_builder/types/internal_types.dart +++ b/lib/src/psbt/psbt_builder/types/internal_types.dart @@ -150,10 +150,11 @@ class PsbtGeneratedTransactionDigest { partialSignature: PsbtUtils.validateMusigPartialSignature( signature: signature.signature, index: taprootParams.index)); } - final schnorrSignature = PsbtUtils.validateSchnorrSignature( + final schnorrSignature = PsbtUtils.validateSignature( signature: signature.signature, index: params.index, - expectedSighash: sighashType); + expectedSighash: sighashType, + type: params.type); if (leafScript == null) { return PsbtInputTaprootKeySpendSignature(schnorrSignature); } @@ -163,10 +164,11 @@ class PsbtGeneratedTransactionDigest { leafHash: leafScript!.leafScript.hash(), xOnlyPubKey: signature.signerPublicKey.toXOnly()); } - final ecdsaSignature = PsbtUtils.validateEcdsaSignature( + final ecdsaSignature = PsbtUtils.validateSignature( signature: signature.signature, index: params.index, - expectedSighash: sighashType); + expectedSighash: sighashType, + type: params.type); return PsbtInputPartialSig( signature: ecdsaSignature, publicKey: signature.signerPublicKey.toBytes( @@ -325,7 +327,13 @@ class PsbtGeneratedTransactionDigest { bool verifyEcdsaSignature(PsbtInputPartialSig sig) { try { - return sig.publicKey.verifyTransaactionSignature(digest, sig.signature); + if (CryptoSignatureUtils.isValidSchnorrSignature(sig.signature)) { + return sig.publicKey + .verifySchnorrSignature(digest: digest, signature: sig.signature); + } + + return sig.publicKey + .verifyDerSignature(digest: digest, signature: sig.signature); } catch (_) { return false; } @@ -418,8 +426,11 @@ class PsbtGeneratedTransactionDigest { {required List xOnly, required List signature}) { try { final tweak = generateTaprootTweak(); - return BitcoinVerifier.verifySchnorrSignature( - xOnly: xOnly, message: digest, signature: signature, tweak: tweak); + return BitcoinSignatureVerifier.verifyBip340SignatureUsingXOnly( + xOnly: xOnly, + digest: digest, + signature: signature, + tapTweakHash: tweak); } catch (_) { return false; } diff --git a/lib/src/psbt/signer/signer.dart b/lib/src/psbt/signer/signer.dart index 14e60c8..84cf21d 100644 --- a/lib/src/psbt/signer/signer.dart +++ b/lib/src/psbt/signer/signer.dart @@ -78,12 +78,14 @@ class PsbtDefaultSigner SignInputResponse btcSignInput(PsbtSigningInputDigest digest) { if (digest.isTaproot) { return SignInputResponse( - signature: - privateKey.signBtcSchnorr(digest.digest, tweak: digest.tweak), + signature: BytesUtils.fromHexString(privateKey.signBip340( + digest.digest, + tapTweakHash: digest.tweak, + tweak: digest.tweak != null)), signerPublicKey: signerPublicKey); } final signature = - privateKey.signInput(digest.digest, sigHash: digest.sighash); + privateKey.signECDSA(digest.digest, sighash: digest.sighash); return SignInputResponse( signature: BytesUtils.fromHexString(signature), signerPublicKey: signerPublicKey); diff --git a/lib/src/psbt/types/types/inputs.dart b/lib/src/psbt/types/types/inputs.dart index 24d84d0..5ba8dcd 100644 --- a/lib/src/psbt/types/types/inputs.dart +++ b/lib/src/psbt/types/types/inputs.dart @@ -9,7 +9,6 @@ import 'package:bitcoin_base/src/crypto/crypto.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:bitcoin_base/src/psbt/psbt_builder/types/types.dart'; import 'package:bitcoin_base/src/psbt/types/types/psbt.dart'; -import 'package:bitcoin_base/src/psbt/utils/utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; enum PsbtInputTypes { @@ -450,7 +449,8 @@ class PsbtInputPartialSig extends PsbtInputDataSignature { super._(type: PsbtInputTypes.partialSignature); factory PsbtInputPartialSig( {required List signature, required List publicKey}) { - if (PsbtUtils.isValidBitcoinDERSignature(signature)) { + if (CryptoSignatureUtils.isValidBitcoinDERSignature(signature) || + CryptoSignatureUtils.isValidSchnorrSignature(signature)) { final pubKey = ECPublic.fromBytes(publicKey); final mode = publicKey.length == EcdsaKeysConst.pubKeyCompressedByteLen ? PubKeyModes.compressed @@ -473,7 +473,8 @@ class PsbtInputPartialSig extends PsbtInputDataSignature { throw DartBitcoinPluginException( "Invalid PSBT Partial Signature type flag."); } - if (PsbtUtils.isValidBitcoinDERSignature(keypair.value.data)) { + if (CryptoSignatureUtils.isValidBitcoinDERSignature(keypair.value.data) || + CryptoSignatureUtils.isValidSchnorrSignature(keypair.value.data)) { try { final mode = keypair.key.extraData?.length == EcdsaKeysConst.pubKeyCompressedByteLen diff --git a/lib/src/psbt/utils/utils.dart b/lib/src/psbt/utils/utils.dart index 9547733..96648b6 100644 --- a/lib/src/psbt/utils/utils.dart +++ b/lib/src/psbt/utils/utils.dart @@ -113,10 +113,15 @@ class PsbtUtils { } if (!isSegwit && p2shRedeemScript != null) { redeemScript = p2shRedeemScript; - address = P2shAddress.fromScript(script: redeemScript); + if (BitcoinScriptUtils.isP2sh32(scriptPubKey!)) { + address = P2shAddress.fromScript32(script: redeemScript); + } else { + address = P2shAddress.fromScript(script: redeemScript); + } + if (address.toScriptPubKey() != scriptPubKey) { throw DartBitcoinPluginException( - "ScriptPubKey does not match the one generated from p2sh redeem script."); + "ScriptPubKey does not match the one generated from p2sh redeem script. ${address.toScriptPubKey()}"); } } @@ -849,40 +854,54 @@ class PsbtUtils { index: index, input: input, sighashType: sighashType); - PsbtInputTaprootLeafScript? tapleafScript; - final digest = switch (params.type) { - PsbtTxType.legacy => () { - final legacyInput = params.cast(); - return unsignedTx.getTransactionDigest( - txInIndex: index, - script: legacyInput.redeemScript, - sighash: sighashType!); - }(), - PsbtTxType.witnessV0 => () { - final v0Input = params.cast(); - return unsignedTx.getTransactionSegwitDigit( - txInIndex: index, - script: v0Input.redeemScript, - sighash: sighashType!, - amount: params.amount); - }(), - PsbtTxType.witnessV1 => () { - final tapInput = params.cast(); - if (tapInput.tapleafScripts != null) { - tapleafScript = PsbtUtils.findCorrectLeafScript( - tapLeafScripts: tapInput.tapleafScripts!, - tapleafHash: tapleafHash, - index: index); - } - return unsignedTx.getTransactionTaprootDigset( - txIndex: index, - scriptPubKeys: tapInput.allScriptPubKeys, - sighash: sighashType!, - amounts: tapInput.allAmounts, - tapleafScript: tapleafScript?.leafScript); - }(), - }; + List digest; + if (isSighashForked(sighashType)) { + if (params.type == PsbtTxType.witnessV1) { + throw DartBitcoinPluginException( + "Invalid sighash type: Forked sighash types are not compatible with witness v1 transactions."); + } + final v0Input = params.cast(); + digest = unsignedTx.getTransactionSegwitDigit( + txInIndex: index, + script: v0Input.redeemScript, + sighash: sighashType, + amount: params.amount); + } else { + digest = switch (params.type) { + PsbtTxType.legacy => () { + final legacyInput = params.cast(); + return unsignedTx.getTransactionDigest( + txInIndex: index, + script: legacyInput.redeemScript, + sighash: sighashType!); + }(), + PsbtTxType.witnessV0 => () { + final v0Input = params.cast(); + return unsignedTx.getTransactionSegwitDigit( + txInIndex: index, + script: v0Input.redeemScript, + sighash: sighashType!, + amount: params.amount); + }(), + PsbtTxType.witnessV1 => () { + final tapInput = params.cast(); + if (tapInput.tapleafScripts != null) { + tapleafScript = PsbtUtils.findCorrectLeafScript( + tapLeafScripts: tapInput.tapleafScripts!, + tapleafHash: tapleafHash, + index: index); + } + return unsignedTx.getTransactionTaprootDigset( + txIndex: index, + scriptPubKeys: tapInput.allScriptPubKeys, + sighash: sighashType!, + amounts: tapInput.allAmounts, + tapleafScript: tapleafScript?.leafScript); + }(), + }; + } + return PsbtGeneratedTransactionDigest( digest: digest, sighashType: sighashType, @@ -905,47 +924,32 @@ class PsbtUtils { "Invalid Psbt input: Cannot mix height-based and time-based locktimes in a PSBT."); } - /// must be move to blockchain_utils package - static bool isValidBitcoinDERSignature(List signature) { - if (signature.length < 9 || signature.length > 73) { - return false; - } - if (signature[0] != 0x30) { - return false; - } - if (signature[1] != signature.length - 3) { - return false; - } - int lenR = signature[3]; - if (5 + lenR >= signature.length) { - return false; - } - int lenS = signature[5 + lenR]; - if ((7 + lenR + lenS) != signature.length) { - return false; - } - if (signature[4] & 0x80 != 0) { - return false; + static List _validateSchnorrSignature( + {required List signature, + required int index, + required int expectedSighash}) { + if (!CryptoSignatureUtils.isValidSchnorrSignature(signature)) { + throw DartBitcoinPluginException( + "Invalid Schnorr signature at input $index. Signature may be malformed or improperly formatted."); } - if (lenR > 1 && (signature[4] == 0x00) && (signature[5] & 0x80) == 0) { - return false; + if (signature.length == CryptoSignerConst.schnoorSginatureLength) { + if (expectedSighash == BitcoinOpCodeConst.sighashDefault) { + return signature; + } + return [...signature, expectedSighash]; } - if (signature[lenR + 4] != 0x02) return false; - if (lenS == 0) return false; - if ((signature[lenR + 6] & 0x80) != 0) return false; - if (lenS > 1 && - (signature[lenR + 6] == 0x00) && - (signature[lenR + 7] & 0x80) == 0) { - return false; + if (signature.last != expectedSighash) { + throw DartBitcoinPluginException( + "Signature mismatch at input $index: Expected sighash $expectedSighash, but got ${signature.last}. The Schnorr signature may be malformed or improperly formatted."); } - return true; + return signature; } - static List validateEcdsaSignature( + static List _validateEcdsaSignature( {required List signature, required int index, required int expectedSighash}) { - if (!isValidBitcoinDERSignature(signature)) { + if (!CryptoSignatureUtils.isValidBitcoinDERSignature(signature)) { throw DartBitcoinPluginException( "Invalid DER-encoded signature at input $index. Signature may be malformed or improperly formatted."); } @@ -956,32 +960,67 @@ class PsbtUtils { return signature; } - static List validateMusigPartialSignature( - {required List signature, required int index}) { - if (!MuSig2Utils.isValidPartialSignature(signature)) { - throw DartBitcoinPluginException( - "Invalid Musig2 Schnorr partial signature at input $index. Signature may be malformed or improperly formatted."); + static bool verifyBchSchnorrSignature( + List digest, List signature, ECPublic pubKey) { + if (digest.length != 32) { + throw const ArgumentException("The message must be a 32-byte array."); } - return signature; + if (signature.length != 64) { + throw const ArgumentException("Invalid signature length."); + } + + final BigInt order = Curves.generatorSecp256k1.order!; + final List rBytes = signature.sublist(0, 32); + final List sBytes = signature.sublist(32, 64); + + final BigInt rX = BigintUtils.fromBytes(rBytes); + final BigInt s = BigintUtils.fromBytes(sBytes); + + if (s >= order) { + return false; + } + + final P = pubKey.point; + BigInt e = BigintUtils.fromBytes(QuickCrypto.sha256Hash([ + ...rBytes, + ...pubKey.toBytes(mode: PubKeyModes.compressed), + ...digest + ])) % + order; + final sp = Curves.generatorSecp256k1 * s; + + if (P.y.isEven) { + e = Curves.generatorSecp256k1.order! - e; + } + final ProjectiveECCPoint eP = P * e; + + final R = sp + eP; + if (R.isInfinity) return false; + if (R.y.isOdd || R.x != rX) { + return false; + } + return true; } - static List validateSchnorrSignature( + static List validateSignature( {required List signature, required int index, - required int expectedSighash}) { - if (!CryptoSignatureUtils.isValidSchnorrSignature(signature)) { - throw DartBitcoinPluginException( - "Invalid Schnorr signature at input $index. Signature may be malformed or improperly formatted."); - } - if (signature.length == CryptoSignerConst.schnoorSginatureLength) { - if (expectedSighash == BitcoinOpCodeConst.sighashDefault) { - return signature; - } - return [...signature, expectedSighash]; - } - if (signature.last != expectedSighash) { + required int expectedSighash, + required PsbtTxType type}) { + if (type.isP2tr || + CryptoSignatureUtils.isValidSchnorrSignature(signature)) { + return _validateSchnorrSignature( + signature: signature, index: index, expectedSighash: expectedSighash); + } + return _validateEcdsaSignature( + signature: signature, index: index, expectedSighash: expectedSighash); + } + + static List validateMusigPartialSignature( + {required List signature, required int index}) { + if (!MuSig2Utils.isValidPartialSignature(signature)) { throw DartBitcoinPluginException( - "Signature mismatch at input $index: Expected sighash $expectedSighash, but got ${signature.last}. The Schnorr signature may be malformed or improperly formatted."); + "Invalid Musig2 Schnorr partial signature at input $index. Signature may be malformed or improperly formatted."); } return signature; } @@ -1017,6 +1056,15 @@ class PsbtUtils { return sighashInfos; } + static int? getInputSigHash(PsbtInput psbtInput, int index) { + final sighashType = + psbtInput.getInput(index, PsbtInputTypes.sighashType); + if (sighashType != null) { + return sighashType.sighash; + } + return null; + } + static void validateCanAddOrUpdateOutput( {required Psbt psbt, int? outputIndex, bool isUpdate = true}) { final sighashes = @@ -1155,7 +1203,39 @@ class PsbtUtils { return (sighash & 0x1F) == type; } + static bool isSighashForked(int sighash) { + return (sighash & BitcoinOpCodeConst.sighashForked == + BitcoinOpCodeConst.sighashForked) || + (sighash & 0x1F) == BitcoinOpCodeConst.sighashForked; + } + static bool isAnyoneCanPay(int sighash) { return (sighash & BitcoinOpCodeConst.sighashAnyoneCanPay) != 0; } + + // static BigInt bchModifierRFC6979( + // {required List privateKey, + // required List digest, + // List algo16 = const [], + // List ndata = const []}) { + // final List blob = [...privateKey, ...digest, ...ndata, ...algo16]; + // List V = List.filled(32, 0x01); + // List K = CryptoOpsConst.zero.clone(); + // K = QuickCrypto.hmacsha256Hash(K, [...V, 0x00, ...blob]); + // V = QuickCrypto.hmacsha256Hash(K, V); + // K = QuickCrypto.hmacsha256Hash(K, [...V, 0x01, ...blob]); + // V = QuickCrypto.hmacsha256Hash(K, V); + // BigInt k = BigInt.zero; + // while (true) { + // V = QuickCrypto.hmacsha256Hash(K, V); + // List T = V.clone(); + // k = BigintUtils.fromBytes(T); + // if (k > BigInt.zero && k < Curves.generatorSecp256k1.order!) { + // break; + // } + // K = QuickCrypto.hmacsha256Hash(K, [...V, 0x00]); + // V = QuickCrypto.hmacsha256Hash(V, []); + // } + // return k; + // } } diff --git a/lib/src/transaction_builder/forked_transaction_builder.dart b/lib/src/transaction_builder/forked_transaction_builder.dart index 010f87a..a3c2973 100644 --- a/lib/src/transaction_builder/forked_transaction_builder.dart +++ b/lib/src/transaction_builder/forked_transaction_builder.dart @@ -72,7 +72,7 @@ class ForkedTransactionBuilder implements BasedBitcoinTransacationBuilder { static int estimateTransactionSize( {required List utxos, required List outputs, - required BitcoinCashNetwork network, + required BasedUtxoNetwork network, String? memo, bool enableRBF = false}) { final transactionBuilder = ForkedTransactionBuilder( @@ -319,9 +319,9 @@ be retrieved by anyone who examines the blockchain's history. /// Total token amount to spend. Map _sumTokenOutputAmounts(List outputs) { final tokens = {}; - for (final utxo in outputs) { - if (utxo.cashToken == null) continue; - final token = utxo.cashToken!; + for (final out in outputs) { + if (out.cashToken == null) continue; + final token = out.cashToken!; if (!token.hasAmount) continue; if (tokens.containsKey(token.category)) { tokens[token.category] = tokens[token.category]! + token.amount; diff --git a/lib/src/utils/btc_utils.dart b/lib/src/utils/btc_utils.dart index afe34ec..bfa05b0 100644 --- a/lib/src/utils/btc_utils.dart +++ b/lib/src/utils/btc_utils.dart @@ -33,4 +33,10 @@ class BtcUtils { dec = dec * BigRational(BigInt.from(10).pow(8)); return dec.toBigInt(); } + + static String toBtc(BigInt amount) { + BigRational dec = BigRational(amount); + dec = dec / BigRational(BigInt.from(10).pow(8)); + return dec.toDecimal(digits: 8); + } } diff --git a/pubspec.yaml b/pubspec.yaml index ba7968b..f8820bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: bitcoin_base description: A versatile library for Bitcoin, Dogecoin, Litecoin, Dash, BSV, and BCH. Supports P2PKH, P2SH, P2WPKH, P2WSH, P2TR, with advanced creation, signing, and spending capabilities. -version: 6.1.0 +version: 6.2.0 homepage: "https://github.com/mrtnetwork/bitcoin_base" repository: "https://github.com/mrtnetwork/bitcoin_base" Author: mrhaydari.t@gmail.com @@ -16,10 +16,10 @@ environment: dependencies: - blockchain_utils: ^4.3.0 + blockchain_utils: ^4.4.0 # blockchain_utils: - # path: ../../blockchain_utils + # path: ../blockchain_utils dev_dependencies: diff --git a/test/deserialize_test.dart b/test/deserialize_test.dart index 8594bf3..f713fd6 100644 --- a/test/deserialize_test.dart +++ b/test/deserialize_test.dart @@ -1,7 +1,21 @@ -import 'package:bitcoin_base/src/bitcoin/script/transaction.dart'; -import 'package:blockchain_utils/utils/binary/utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:test/test.dart'; +ECPrivate getPublicKey({String path = "m/84'/1'/0'/0/1"}) { + final key = getKey(path: path); + return ECPrivate.fromBytes(key.privateKey.raw); +} + +Bip32Slip10Secp256k1 getKey( + {String key = + "tprv8ZgxMBicQKsPdgBQV2Y9EVPSjAGhyZXArhwSzHwnV3FytzPRr8KCR8EKEpLeHbANAncgbc31a6QoXjBTARQiZ2h1Z2NgSCjFYeTqKpAN5Gc", + String path = "m/86'/1'/0'/0/1"}) { + Bip32Slip10Secp256k1 secp = Bip32Slip10Secp256k1.fromExtendedKey( + key, Bip44Coins.bitcoinTestnet.conf.keyNetVer); + return secp.derivePath(path) as Bip32Slip10Secp256k1; +} + void main() { _decodeTx(); } diff --git a/test/keys_test.dart b/test/keys_test.dart index eb985d2..de39bd8 100644 --- a/test/keys_test.dart +++ b/test/keys_test.dart @@ -161,17 +161,63 @@ void main() { netVersion: BitcoinNetwork.mainnet.wifNetVer); final pub = priv.getPublic(); - test('test1', () { + test('sign/verify message', () { final sign = priv.signMessage(StringUtils.encode(message)); - pub.verify(StringUtils.encode(message), BytesUtils.fromHexString(sign)); - expect( pub.verify( - StringUtils.encode(message), BytesUtils.fromHexString(sign)), + message: StringUtils.encode(message), + signature: BytesUtils.fromHexString(sign)), true); }); - test('test2', () {}); + test('sign/verify bip137 p2pkh uncompressed', () { + final signature = priv.signBip137(StringUtils.encode(message)); + + expect( + pub.verifyBip137Address( + message: StringUtils.encode(message), + signature: signature, + address: + priv.getPublic().toAddress(mode: PubKeyModes.uncompressed)), + true); + }); + + test('sign/verify bip137 p2pkh compressed', () { + final signature = priv.signBip137(StringUtils.encode(message), + mode: BIP137Mode.p2pkhCompressed); + + expect( + pub.verifyBip137Address( + message: StringUtils.encode(message), + signature: signature, + address: + priv.getPublic().toAddress(mode: PubKeyModes.compressed)), + true); + }); + + test('sign/verify bip137 p2wpkh', () { + final signature = + priv.signBip137(StringUtils.encode(message), mode: BIP137Mode.p2wpkh); + + expect( + pub.verifyBip137Address( + message: StringUtils.encode(message), + signature: signature, + address: priv.getPublic().toSegwitAddress()), + true); + }); + + test('sign/verify bip137 p2wpkh/p2sh', () { + final signature = priv.signBip137(StringUtils.encode(message), + mode: BIP137Mode.p2shP2wpkh); + + expect( + pub.verifyBip137Address( + message: StringUtils.encode(message), + signature: signature, + address: priv.getPublic().toP2wpkhInP2sh()), + true); + }); }); group('TestP2pkhAddresses', () { diff --git a/test/p2kh_tr_test.dart b/test/p2kh_tr_test.dart index eb45ad4..c394850 100644 --- a/test/p2kh_tr_test.dart +++ b/test/p2kh_tr_test.dart @@ -118,7 +118,7 @@ void main() { BitcoinOpcode.opEqualVerify, BitcoinOpcode.opCheckSig, ])); - final sig = sk.signInput(digit); + final sig = sk.signECDSA(digit); txin.scriptSig = Script(script: [sig, sk.getPublic().toHex()]); expect(tx.serialize(), coreTxSignedResult); }); @@ -127,7 +127,7 @@ void main() { BtcTransaction(inputs: [txin], outputs: [txout, changeLowSTxout]); final digit = tx.getTransactionDigest( txInIndex: 0, script: fromAddr.toScriptPubKey()); - final sig = sk.signInput(digit); + final sig = sk.signECDSA(digit); txin.scriptSig = Script(script: [sig, sk.getPublic().toHex()]); expect(tx.serialize(), coreTxSignedLowSSigallResult); }); @@ -138,7 +138,7 @@ void main() { txInIndex: 0, script: fromAddr.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashNone); - final sig = sk.signInput(digit, sigHash: BitcoinOpCodeConst.sighashNone); + final sig = sk.signECDSA(digit, sighash: BitcoinOpCodeConst.sighashNone); txin.scriptSig = Script(script: [sig, sk.getPublic().toHex()]); expect(tx.serialize(), coreTxSignedLowSSignoneResult); expect(tx.txId(), coreTxSignedLowSSignoneTxid); @@ -151,7 +151,7 @@ void main() { script: sigFromAddr1.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashSingle); final sig = - sigSk1.signInput(digit, sigHash: BitcoinOpCodeConst.sighashSingle); + sigSk1.signECDSA(digit, sighash: BitcoinOpCodeConst.sighashSingle); sigTxin1.scriptSig = Script(script: [sig, sigSk1.getPublic().toHex()]); expect(tx.serialize(), sigSighashSingleResult); }); @@ -163,13 +163,13 @@ void main() { script: sigFromAddr1.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashAll); final sig = - sigSk1.signInput(digit, sigHash: BitcoinOpCodeConst.sighashAll); + sigSk1.signECDSA(digit, sighash: BitcoinOpCodeConst.sighashAll); final digit2 = tx.getTransactionDigest( txInIndex: 1, script: sigFromAddr2.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashAll); final sig2 = - sigSk2.signInput(digit2, sigHash: BitcoinOpCodeConst.sighashAll); + sigSk2.signECDSA(digit2, sighash: BitcoinOpCodeConst.sighashAll); sigTxin1.scriptSig = Script(script: [sig, sigSk1.getPublic().toHex()]); sigTxin2.scriptSig = Script(script: [sig2, sigSk2.getPublic().toHex()]); expect(tx.serialize(), signSighashAll2in2outResult); @@ -182,13 +182,13 @@ void main() { script: sigFromAddr1.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashNone); final sig = - sigSk1.signInput(digit, sigHash: BitcoinOpCodeConst.sighashNone); + sigSk1.signECDSA(digit, sighash: BitcoinOpCodeConst.sighashNone); final digit2 = tx.getTransactionDigest( txInIndex: 1, script: sigFromAddr2.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashNone); final sig2 = - sigSk2.signInput(digit2, sigHash: BitcoinOpCodeConst.sighashNone); + sigSk2.signECDSA(digit2, sighash: BitcoinOpCodeConst.sighashNone); sigTxin1.scriptSig = Script(script: [sig, sigSk1.getPublic().toHex()]); sigTxin2.scriptSig = Script(script: [sig2, sigSk2.getPublic().toHex()]); expect(tx.serialize(), signSighashNone2in2outResult); @@ -202,8 +202,8 @@ void main() { sighash: BitcoinOpCodeConst.sighashAll | BitcoinOpCodeConst.sighashAnyoneCanPay); - final sig = sigSk1.signInput(digit, - sigHash: BitcoinOpCodeConst.sighashAll | + final sig = sigSk1.signECDSA(digit, + sighash: BitcoinOpCodeConst.sighashAll | BitcoinOpCodeConst.sighashAnyoneCanPay); final digit2 = tx.getTransactionDigest( @@ -211,8 +211,8 @@ void main() { script: sigFromAddr2.toScriptPubKey(), sighash: BitcoinOpCodeConst.sighashSingle | BitcoinOpCodeConst.sighashAnyoneCanPay); - final sig2 = sigSk2.signInput(digit2, - sigHash: BitcoinOpCodeConst.sighashSingle | + final sig2 = sigSk2.signECDSA(digit2, + sighash: BitcoinOpCodeConst.sighashSingle | BitcoinOpCodeConst.sighashAnyoneCanPay); sigTxin1.scriptSig = Script(script: [sig, sigSk1.getPublic().toHex()]); sigTxin2.scriptSig = Script(script: [sig2, sigSk2.getPublic().toHex()]); diff --git a/test/p2sh_test.dart b/test/p2sh_test.dart index 122efe8..dd64279 100644 --- a/test/p2sh_test.dart +++ b/test/p2sh_test.dart @@ -59,7 +59,7 @@ void main() { final tx = BtcTransaction(inputs: [txin], outputs: [txout]); final digit = tx.getTransactionDigest( txInIndex: 0, script: fromAddr.toScriptPubKey()); - final sig = sk.signInput(digit); + final sig = sk.signECDSA(digit); txin.scriptSig = Script(script: [sig, sk.getPublic().toHex()]); expect(tx.serialize(), createP2shAndSendResult); }); @@ -68,7 +68,7 @@ void main() { final tx = BtcTransaction(inputs: [txinSpend], outputs: [txout2]); final digit = tx.getTransactionDigest(txInIndex: 0, script: p2pkRedeemScript); - final sig = p2pkSk.signInput(digit); + final sig = p2pkSk.signECDSA(digit); txinSpend.scriptSig = Script(script: [sig, p2pkRedeemScript.toHex()]); expect(tx.serialize(), spendP2shResult); }); @@ -90,7 +90,7 @@ void main() { final tx = BtcTransaction(inputs: [txinSeq], outputs: [txout1]); final digit = tx.getTransactionDigest(txInIndex: 0, script: redeemScript); - final sig = skCsvP2pkh.signInput(digit); + final sig = skCsvP2pkh.signECDSA(digit); txinSeq.scriptSig = Script( script: [sig, skCsvP2pkh.getPublic().toHex(), redeemScript.toHex()]); diff --git a/test/p2tr_test.dart b/test/p2tr_test.dart index 5880a8d..e6f7d13 100644 --- a/test/p2tr_test.dart +++ b/test/p2tr_test.dart @@ -108,7 +108,7 @@ void main() { scriptPubKeys: [scriptPubKey2], amounts: [BigInt.from(3500)], sighash: signHash); - final signatur = fromPriv2.signTapRoot(txDigit, + final signatur = fromPriv2.signBip340(txDigit, treeScript: TaprootLeaf(script: trScriptP2pk1), sighash: signHash); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [signatur]) @@ -130,7 +130,7 @@ void main() { txIndex: 0, tapleafScript: TaprootLeaf(script: trScriptP2pk1), ); - final sig = privkeyTrScript1.signTapRoot(digit, + final sig = privkeyTrScript1.signBip340(digit, sighash: BitcoinOpCodeConst.sighashDefault, tweak: false); final controlBlock = TaprootControlBlock.generate( xOnlyOrInternalPubKey: fromPub2.toXOnly(), @@ -207,7 +207,7 @@ void main() { tapleafScript: TaprootLeaf(script: trScriptP2pkA), ); - final sign = privkeyTrScriptA.signTapRoot( + final sign = privkeyTrScriptA.signBip340( txDigit, tweak: false, ); @@ -311,7 +311,7 @@ void main() { scriptPubKeys: allUtxosScriptPubkeys.map((e) => e).toList(), tapleafScript: TaprootLeaf(script: trScriptP2pkB), amounts: allAmounts.map((e) => e).toList()); - final sig = privkeyTrScriptB.signTapRoot(digit, tweak: false); + final sig = privkeyTrScriptB.signBip340(digit, tweak: false); final controlBlock = TaprootControlBlock.generate( xOnlyOrInternalPubKey: fromPub.toXOnly(), scriptTree: TaprootBranch( diff --git a/test/p2wpkh_test.dart b/test/p2wpkh_test.dart index be0aad8..4110d77 100644 --- a/test/p2wpkh_test.dart +++ b/test/p2wpkh_test.dart @@ -135,7 +135,7 @@ void main() { final tx = BtcTransaction(inputs: [txin1], outputs: [txout1]); final digit = tx.getTransactionDigest( txInIndex: 0, script: p2pkhAddr.toScriptPubKey()); - final sig = sk.signInput(digit); + final sig = sk.signECDSA(digit); txin1.scriptSig = Script(script: [sig, sk.getPublic().toHex()]); expect(tx.serialize(), createSendToP2wpkhResult); }); @@ -143,7 +143,7 @@ void main() { var tx = BtcTransaction(inputs: [txinSpend], outputs: [txout2]); final digit = tx.getTransactionSegwitDigit( txInIndex: 0, script: p2pkhRedeemScript, amount: txinSpendAmount); - final sig = sk.signInput(digit); + final sig = sk.signECDSA(digit); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [sig, sk.getPublic().toHex()]) ]); @@ -156,13 +156,13 @@ void main() { ); final digit = tx.getTransactionDigest( txInIndex: 0, script: p2pkhAddr.toScriptPubKey()); - final sig = sk.signInput(digit); + final sig = sk.signECDSA(digit); txinSpendP2pkh.scriptSig = Script(script: [sig, sk.getPublic().toHex()]); final segwitDigit = tx.getTransactionSegwitDigit( amount: txinSpendP2wpkhAmount, script: p2pkhRedeemScript, txInIndex: 1); - final sig2 = sk.signInput(segwitDigit); + final sig2 = sk.signECDSA(segwitDigit); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: []), TxWitnessInput(stack: [sig2, sk.getPublic().toHex()]) @@ -176,7 +176,7 @@ void main() { script: p2pkhRedeemScript, amount: txin1SignoneAmount, sighash: BitcoinOpCodeConst.sighashNone); - final sig = sk.signInput(digit, sigHash: BitcoinOpCodeConst.sighashNone); + final sig = sk.signECDSA(digit, sighash: BitcoinOpCodeConst.sighashNone); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [sig, sk.getPublic().toHex()]) ]); @@ -195,7 +195,7 @@ void main() { amount: txin1SigsingleAmount, sighash: BitcoinOpCodeConst.sighashSingle); final sig = - sk.signInput(digit, sigHash: BitcoinOpCodeConst.sighashSingle); + sk.signECDSA(digit, sighash: BitcoinOpCodeConst.sighashSingle); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [sig, sk.getPublic().toHex()]) ]); @@ -214,8 +214,8 @@ void main() { amount: txin1SiganyonecanpayAllAmount, sighash: BitcoinOpCodeConst.sighashAll | BitcoinOpCodeConst.sighashAnyoneCanPay); - final sig = sk.signInput(digit, - sigHash: BitcoinOpCodeConst.sighashAll | + final sig = sk.signECDSA(digit, + sighash: BitcoinOpCodeConst.sighashAll | BitcoinOpCodeConst.sighashAnyoneCanPay); tx = tx.copyWith(inputs: [...tx.inputs, txin2SiganyonecanpayAll]); @@ -224,7 +224,7 @@ void main() { script: p2pkhRedeemScript, amount: txin2SiganyonecanpayAllAmount, sighash: BitcoinOpCodeConst.sighashAll); - final sig2 = sk.signInput(digit2, sigHash: BitcoinOpCodeConst.sighashAll); + final sig2 = sk.signECDSA(digit2, sighash: BitcoinOpCodeConst.sighashAll); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [sig, sk.getPublic().toHex()]), TxWitnessInput(stack: [sig2, sk.getPublic().toHex()]) @@ -244,8 +244,8 @@ void main() { amount: txin1SiganyonecanpayNoneAmount, sighash: BitcoinOpCodeConst.sighashNone | BitcoinOpCodeConst.sighashAnyoneCanPay); - final sig = sk.signInput(digit, - sigHash: BitcoinOpCodeConst.sighashNone | + final sig = sk.signECDSA(digit, + sighash: BitcoinOpCodeConst.sighashNone | BitcoinOpCodeConst.sighashAnyoneCanPay); tx = tx.copyWith(inputs: [...tx.inputs, txin2SiganyonecanpayNone]); tx = tx.copyWith(outputs: [...tx.outputs, txout2SiganyonecanpayNone]); @@ -254,7 +254,7 @@ void main() { script: p2pkhRedeemScript, amount: txin2SiganyonecanpayNoneAmount, sighash: BitcoinOpCodeConst.sighashAll); - final sig2 = sk.signInput(digit2, sigHash: BitcoinOpCodeConst.sighashAll); + final sig2 = sk.signECDSA(digit2, sighash: BitcoinOpCodeConst.sighashAll); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [sig, sk.getPublic().toHex()]), TxWitnessInput(stack: [sig2, sk.getPublic().toHex()]) @@ -274,8 +274,8 @@ void main() { amount: txin1SiganyonecanpaySingleAmount, sighash: BitcoinOpCodeConst.sighashSingle | BitcoinOpCodeConst.sighashAnyoneCanPay); - final sig = sk.signInput(digit, - sigHash: BitcoinOpCodeConst.sighashSingle | + final sig = sk.signECDSA(digit, + sighash: BitcoinOpCodeConst.sighashSingle | BitcoinOpCodeConst.sighashAnyoneCanPay); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: [sig, sk.getPublic().toHex()]) diff --git a/test/p2wsh_test.dart b/test/p2wsh_test.dart index 27229e5..91e6c03 100644 --- a/test/p2wsh_test.dart +++ b/test/p2wsh_test.dart @@ -73,7 +73,7 @@ void main() { final tx = BtcTransaction(inputs: [txin1], outputs: [txout1]); final digit = tx.getTransactionDigest( txInIndex: 0, script: p2pkhAddr.toScriptPubKey()); - final sig = sk1.signInput(digit); + final sig = sk1.signECDSA(digit); txin1.scriptSig = Script(script: [sig, sk1.getPublic().toHex()]); expect(tx.serialize(), createSendToP2pkhResult); }); @@ -84,8 +84,8 @@ void main() { ); final digit1 = tx.getTransactionSegwitDigit( txInIndex: 0, script: p2wshRedeemScript, amount: txinSpendAmount); - final sig1 = sk1.signInput(digit1); - final sig2 = sk2.signInput(digit1); + final sig1 = sk1.signECDSA(digit1); + final sig2 = sk2.signECDSA(digit1); final pk = p2wshRedeemScript.toHex(); // tx.addWitnesses(TxWitnessInput(stack: ['', sig1, sig2, pk])); tx = tx.copyWith(witnesses: [ @@ -100,13 +100,13 @@ void main() { ); final digit1 = tx.getTransactionDigest( txInIndex: 0, script: p2pkhAddr.toScriptPubKey()); - final sig1 = sk1.signInput(digit1); + final sig1 = sk1.signECDSA(digit1); txin1Multiple.scriptSig = Script(script: [sig1, sk1.getPublic().toHex()]); // tx.addWitnesses(TxWitnessInput(stack: [])); final seqwitDigit = tx.getTransactionSegwitDigit( txInIndex: 1, script: p2wshRedeemScript, amount: txin2MultipleAmount); - final sigP2sh1 = sk1.signInput(seqwitDigit); - final sigP2sh2 = sk2.signInput(seqwitDigit); + final sigP2sh1 = sk1.signECDSA(seqwitDigit); + final sigP2sh2 = sk2.signECDSA(seqwitDigit); // tx.addWitnesses(TxWitnessInput( // stack: ['', sigP2sh1, sigP2sh2, p2wshRedeemScript.toHex()])); @@ -114,7 +114,7 @@ void main() { txInIndex: 2, script: p2pkhAddr.toScriptPubKey(), amount: txin3MultipleAmount); - final sig3 = sk1.signInput(segwitDigitIndex2); + final sig3 = sk1.signECDSA(segwitDigitIndex2); // tx.addWitnesses(TxWitnessInput(stack: [sig3, sk1.getPublic().toHex()])); tx = tx.copyWith(witnesses: [ TxWitnessInput(stack: []), From 6c4017b096119f3732f0908e0bc09cf968c3e336 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Thu, 24 Apr 2025 20:49:29 +0330 Subject: [PATCH 02/12] V6.3.0 - Update dependencies. - Add estimate size for psbt - Add more script validation in psbt --- CHANGELOG.md | 6 + example/lib/musig/methods.dart | 1 - lib/src/bitcoin/address/network_address.dart | 49 ++++++ .../bitcoin/address/utils/address_utils.dart | 1 - lib/src/bitcoin/script/output.dart | 5 +- lib/src/bitcoin/script/transaction.dart | 6 +- lib/src/bitcoin/script/utils.dart | 7 + lib/src/models/network.dart | 73 ++++++++ lib/src/provider/providers/explorer.dart | 4 +- .../psbt/psbt_builder/core/psbt_builder.dart | 51 ++++++ lib/src/psbt/psbt_builder/impl/input.dart | 1 + .../psbt_builder/types/internal_types.dart | 8 + lib/src/psbt/psbt_builder/types/types.dart | 24 +-- lib/src/psbt/psbt_builder/versiones/v1.dart | 26 +-- lib/src/psbt/types/types/global.dart | 3 +- lib/src/psbt/utils/utils.dart | 156 ++++++++++++------ pubspec.yaml | 2 +- 17 files changed, 332 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c199f95..b327ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.3.0 + +- Update dependencies. +- Add estimate size for psbt +- Add more script validation in psbt + ## 6.2.0 - Update dependencies. diff --git a/example/lib/musig/methods.dart b/example/lib/musig/methods.dart index d67b119..e641a18 100644 --- a/example/lib/musig/methods.dart +++ b/example/lib/musig/methods.dart @@ -73,7 +73,6 @@ Future> getPsbtUtxo( merkleProof: request.merkleProof, treeScript: request.treeScript, merkleRoot: request.merkleRoot, - privateKeys: request.privateKeys, xOnlyOrInternalPubKey: request.xOnlyOrInternalPubKey, muSig2ParticipantPublicKeys: request.muSig2ParticipantPublicKeys, hash160: request.hash160, diff --git a/lib/src/bitcoin/address/network_address.dart b/lib/src/bitcoin/address/network_address.dart index 3147dee..a451bf7 100644 --- a/lib/src/bitcoin/address/network_address.dart +++ b/lib/src/bitcoin/address/network_address.dart @@ -6,6 +6,55 @@ abstract class BitcoinNetworkAddress { {required this.address, required this.network, required this.baseAddress}); + + static ADDRESS fromBaseAddress
( + {required BitcoinBaseAddress address, + required BasedUtxoNetwork network}) { + BitcoinNetworkAddress baseAddress; + switch (network) { + case BitcoinSVNetwork.mainnet: + case BitcoinSVNetwork.testnet: + baseAddress = BitcoinSVAddress.fromBaseAddress(address, + network: network as BitcoinSVNetwork); + case BitcoinNetwork.mainnet: + case BitcoinNetwork.testnet: + case BitcoinNetwork.testnet4: + baseAddress = BitcoinAddress.fromBaseAddress(address, + network: network as BitcoinNetwork); + case LitecoinNetwork.mainnet: + case LitecoinNetwork.testnet: + baseAddress = LitecoinAddress.fromBaseAddress(address, + network: network as LitecoinNetwork); + case DashNetwork.mainnet: + case DashNetwork.testnet: + baseAddress = DashAddress.fromBaseAddress(address, + network: network as DashNetwork); + case DogecoinNetwork.mainnet: + case DogecoinNetwork.testnet: + baseAddress = DogeAddress.fromBaseAddress(address, + network: network as DogecoinNetwork); + case BitcoinCashNetwork.mainnet: + case BitcoinCashNetwork.testnet: + baseAddress = BitcoinCashAddress.fromBaseAddress(address, + network: network as BitcoinCashNetwork); + case PepeNetwork.mainnet: + baseAddress = PepeAddress.fromBaseAddress(address, + network: network as PepeNetwork); + case ElectraProtocolNetwork.mainnet: + case ElectraProtocolNetwork.testnet: + baseAddress = ElectraProtocolAddress.fromBaseAddress(address, + network: network as ElectraProtocolNetwork); + default: + throw DartBitcoinPluginException("Unknown network. ${network.value}"); + } + if (baseAddress is! ADDRESS) { + throw DartBitcoinPluginException( + "Invalid cast: expected ${ADDRESS.runtimeType}, but found ${baseAddress.runtimeType}.", + ); + } + return baseAddress; + } + static ADDRESS parse
( {required String address, required BasedUtxoNetwork network}) { BitcoinNetworkAddress baseAddress; diff --git a/lib/src/bitcoin/address/utils/address_utils.dart b/lib/src/bitcoin/address/utils/address_utils.dart index f564e86..784b8c3 100644 --- a/lib/src/bitcoin/address/utils/address_utils.dart +++ b/lib/src/bitcoin/address/utils/address_utils.dart @@ -292,7 +292,6 @@ class _BitcoinAddressUtils { final version = decode.item2; final addrBytes = decode.item1; final scriptHex = BytesUtils.toHexString(addrBytes); - switch (type) { case P2pkhAddressType.p2pkh: if (BytesUtils.bytesEqual(version, network.p2pkhNetVer)) { diff --git a/lib/src/bitcoin/script/output.dart b/lib/src/bitcoin/script/output.dart index 4654f56..552f3f5 100644 --- a/lib/src/bitcoin/script/output.dart +++ b/lib/src/bitcoin/script/output.dart @@ -25,12 +25,11 @@ class TxOutput { CashToken? cashToken}) { try { return TxOutput._( - amount: amount.asUint64, + amount: amount.asInt64, scriptPubKey: scriptPubKey, cashToken: cashToken); } catch (_) { - throw DartBitcoinPluginException( - "Invalid output amount: must be a non-negative 64-bit integer."); + throw DartBitcoinPluginException("Invalid output amount."); } } final CashToken? cashToken; diff --git a/lib/src/bitcoin/script/transaction.dart b/lib/src/bitcoin/script/transaction.dart index 849e8de..9159229 100644 --- a/lib/src/bitcoin/script/transaction.dart +++ b/lib/src/bitcoin/script/transaction.dart @@ -65,9 +65,9 @@ class BtcTransaction { List? version, }) { return BtcTransaction( - inputs: inputs ?? this.inputs, - outputs: outputs ?? this.outputs, - witnesses: witnesses ?? this.witnesses, + inputs: inputs ?? this.inputs.map((e) => e.clone()).toList(), + outputs: outputs ?? this.outputs.map((e) => e.clone()).toList(), + witnesses: witnesses ?? this.witnesses.map((e) => e.clone()).toList(), locktime: locktime ?? this.locktime, version: version ?? this.version); } diff --git a/lib/src/bitcoin/script/utils.dart b/lib/src/bitcoin/script/utils.dart index 07af9c8..028c0b6 100644 --- a/lib/src/bitcoin/script/utils.dart +++ b/lib/src/bitcoin/script/utils.dart @@ -27,6 +27,13 @@ enum ScriptPubKeyType { } class BitcoinScriptUtils { + static Script buildOpReturn(List> data) { + return Script(script: [ + BitcoinOpcode.opReturn, + ...data.map((e) => BytesUtils.toHexString(e)) + ]); + } + static bool scriptContains( {required Script script, required List elements}) { if (elements.length != script.script.length) return false; diff --git a/lib/src/models/network.dart b/lib/src/models/network.dart index 440d5e5..0fdc7c5 100644 --- a/lib/src/models/network.dart +++ b/lib/src/models/network.dart @@ -613,3 +613,76 @@ class ElectraProtocolNetwork implements BasedUtxoNetwork { @override final String identifier; } + +// class BonkcoinNetwork implements BasedUtxoNetwork { +// /// Mainnet configuration with associated `CoinConf`. +// static const ElectraProtocolNetwork mainnet = ElectraProtocolNetwork._( +// 'BonkcoinMainnet', CoinsConf.electraProtocolMainNet, 'electra:mainnet'); + +// /// Testnet configuration with associated `CoinConf`. +// static const ElectraProtocolNetwork testnet = ElectraProtocolNetwork._( +// 'electraProtocolTestnet', +// CoinsConf.electraProtocolTestNet, +// 'electra:testnet'); + +// /// Overrides the `conf` property from `BasedUtxoNetwork` with the associated `CoinConf`. +// @override +// final CoinConf conf; +// @override +// final String value; + +// /// Constructor for creating a Electra Protocol network with a specific configuration. +// const BonkcoinNetwork._(this.value, this.conf, this.identifier); + +// /// Retrieves the Wallet Import Format (WIF) version bytes from the associated `CoinConf`. +// @override +// List get wifNetVer => conf.params.wifNetVer!; + +// /// Retrieves the Pay-to-Public-Key-Hash (P2PKH) version bytes from the associated `CoinConf`. +// @override +// List get p2pkhNetVer => conf.params.p2pkhNetVer!; + +// /// Retrieves the Pay-to-Script-Hash (P2SH) version bytes from the associated `CoinConf`. +// @override +// List get p2shNetVer => conf.params.p2shNetVer!; + +// /// Retrieves the Human-Readable Part (HRP) for Pay-to-Witness-Public-Key-Hash (P2WPKH) addresses +// /// from the associated `CoinConf`. +// @override +// String get p2wpkhHrp => conf.params.p2wpkhHrp!; + +// /// Checks if the current network is the mainnet. +// @override +// bool get isMainnet => this == ElectraProtocolNetwork.mainnet; + +// @override +// final List supportedAddress = const [ +// P2pkhAddressType.p2pkh, +// SegwitAddressType.p2wpkh, +// PubKeyAddressType.p2pk, +// SegwitAddressType.p2wsh, +// P2shAddressType.p2wshInP2sh, +// P2shAddressType.p2wpkhInP2sh, +// P2shAddressType.p2pkhInP2sh, +// P2shAddressType.p2pkInP2sh, +// ]; + +// @override +// List get coins { +// if (isMainnet) { +// return [ +// Bip44Coins.electraProtocol, +// Bip49Coins.electraProtocol, +// Bip84Coins.electraProtocol +// ]; +// } +// return [ +// Bip44Coins.electraProtocolTestnet, +// Bip49Coins.electraProtocolTestnet, +// Bip84Coins.electraProtocolTestnet +// ]; +// } + +// @override +// final String identifier; +// } diff --git a/lib/src/provider/providers/explorer.dart b/lib/src/provider/providers/explorer.dart index bfdcfdc..86db532 100644 --- a/lib/src/provider/providers/explorer.dart +++ b/lib/src/provider/providers/explorer.dart @@ -11,8 +11,8 @@ class ApiProvider { {required this.api, Map? header, required this.service}) : _header = header ?? {'Content-Type': 'application/json'}; factory ApiProvider.fromMempool(BasedUtxoNetwork network, ApiService service, - {Map? header}) { - final api = APIConfig.mempool(network); + {Map? header, String? baseUrl}) { + final api = APIConfig.mempool(network, baseUrl: baseUrl); return ApiProvider(api: api, header: header, service: service); } factory ApiProvider.fromBlocCypher( diff --git a/lib/src/psbt/psbt_builder/core/psbt_builder.dart b/lib/src/psbt/psbt_builder/core/psbt_builder.dart index 559ff69..fae6a21 100644 --- a/lib/src/psbt/psbt_builder/core/psbt_builder.dart +++ b/lib/src/psbt/psbt_builder/core/psbt_builder.dart @@ -127,6 +127,57 @@ abstract class PsbtBuilderImpl { _psbt.input.updateInputs(index, [pubKeyNonce]); } + /// Finalizes all inputs in the PSBT and returns the finalized transaction. + /// + /// This method attempts to finalize each input in the PSBT. + /// + /// Note: + /// - If an input is already finalized, it will be skipped. + /// - If any input requires custom script handling, you must provide an + /// `onFinalizeInput` callback to finalize it manually; otherwise, + /// the operation may fail. + /// + /// Returns the finalized [BtcTransaction] if successful. + BtcTransaction finalizeAll({ONFINALIZEINPUT? onFinalizeInput}); + + /// Estimates the transaction size before finalization. + /// Optionally accepts a [onFinalizeInput] callback to customize input finalization. + /// - If any input requires custom script handling, you must provide an + /// `onFinalizeInput` callback to finalize it manually; otherwise, + /// the operation may fail. + /// + /// This method does not reflect the finalized transaction size. + /// For unsigned P2PKH inputs, it assumes an uncompressed public key + /// unlocking script for estimation. + /// For SegWit transactions, it returns the estimated virtual size (vsize). + int getUnSafeTransactionSize({ONFINALIZEINPUT? onFinalizeInput}) { + final fakeSignature = PsbtGlobalProprietaryUseType( + identifier: PsbtUtils.fakeFinalizeGlobalIdentifier, + subkeydata: [], + data: const []); + final Psbt psbt = Psbt( + global: PsbtGlobal( + version: _psbt.version, + entries: [..._psbt.global.entries.clone(), fakeSignature]), + input: PsbtInput( + version: _psbt.version, entries: _psbt.input.entries.clone()), + output: PsbtOutput( + version: _psbt.version, entries: _psbt.output.entries.clone())); + final builder = PsbtBuilder.fromPsbt(psbt); + final tx = builder.finalizeAll(onFinalizeInput: onFinalizeInput); + return tx.getSize(); + } + + /// Finalizes all inputs and returns the serialized transaction size in bytes. + /// + /// Optionally accepts a [onFinalizeInput] callback to customize input finalization. + /// The returned size is the actual size of the fully finalized transaction. + /// For SegWit transactions, it returns the estimated virtual size (vsize). + int finalizeAllAndGetTransactionSize({ONFINALIZEINPUT? onFinalizeInput}) { + final tx = finalizeAll(onFinalizeInput: onFinalizeInput); + return tx.getSize(); + } + void _addNewTxOutput(PsbtTransactionOutput output) { PsbtUtils.validateCanAddOrUpdateOutput(psbt: _psbt); } diff --git a/lib/src/psbt/psbt_builder/impl/input.dart b/lib/src/psbt/psbt_builder/impl/input.dart index 08f85e2..bbcb322 100644 --- a/lib/src/psbt/psbt_builder/impl/input.dart +++ b/lib/src/psbt/psbt_builder/impl/input.dart @@ -132,6 +132,7 @@ mixin PsbtInputImpl on PsbtBuilderImpl { /// the operation may fail. /// /// Returns the finalized [BtcTransaction] if successful. + @override BtcTransaction finalizeAll({ONFINALIZEINPUT? onFinalizeInput}) { for (int i = 0; i < _psbt.input.entries.length; i++) { finalizeInput(i, onFinalizeInput: onFinalizeInput); diff --git a/lib/src/psbt/psbt_builder/types/internal_types.dart b/lib/src/psbt/psbt_builder/types/internal_types.dart index 8ff64ba..3688631 100644 --- a/lib/src/psbt/psbt_builder/types/internal_types.dart +++ b/lib/src/psbt/psbt_builder/types/internal_types.dart @@ -300,6 +300,14 @@ class PsbtGeneratedTransactionDigest { return partialSigs.first; } + PsbtInputPartialSig? getPartialSignatureOrNull() { + try { + return getPartialSignature(); + } catch (e) { + return null; + } + } + List getTaprootScriptSignatures( List xOnlyKeys) { final musig2Signatures = PsbtUtils.getReadyMusig2Signature(this); diff --git a/lib/src/psbt/psbt_builder/types/types.dart b/lib/src/psbt/psbt_builder/types/types.dart index 95fdf64..185fd6b 100644 --- a/lib/src/psbt/psbt_builder/types/types.dart +++ b/lib/src/psbt/psbt_builder/types/types.dart @@ -1,7 +1,6 @@ import 'package:bitcoin_base/src/bitcoin/address/address.dart'; import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; import 'package:bitcoin_base/src/bitcoin/taproot/taproot.dart'; -import 'package:bitcoin_base/src/crypto/crypto.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:bitcoin_base/src/provider/models/models.dart'; import 'package:bitcoin_base/src/psbt/psbt.dart'; @@ -180,9 +179,14 @@ class PsbtTransactionInput { "Mismatch detected: The provided scriptPubKey does not match the nonWitnessUtxo output scriptPubKey."); } final type = PsbtUtils.findScriptType(output.scriptPubKey); + if (type.isP2tr) { + throw DartBitcoinPluginException( + "Incorrect legacy scriptPubKey. Use `witnessV1` constractor instead of `legacy` for taproot spending.", + details: {"type": type.name}); + } if (type.isSegwit) { throw DartBitcoinPluginException( - "Incorrect scriptPubKey. Use `addWitnessV0Input` instead of `addLegacyInput`.", + "Incorrect legacy scriptPubKey. Use `witnessV0` constractor instead of `legacy` for segwit spending.", details: {"type": type.name}); } if (type.isP2sh) { @@ -191,6 +195,12 @@ class PsbtTransactionInput { "redeemScript is required to spend P2SH scripts.", details: {"script": scriptPubKey?.script.join(", ")}); } + if (BitcoinScriptUtils.isP2wsh(redeemScript) || + BitcoinScriptUtils.isP2wpkh(redeemScript)) { + throw DartBitcoinPluginException( + "Incorrect legacy scriptPubKey. Use `witnessV1` constractor instead of `legacy` for nested segwit p2sh spending.", + details: {"type": type.name}); + } P2shAddress addr; if (type.isP2sh32) { addr = P2shAddress.fromScript32(script: redeemScript); @@ -632,7 +642,6 @@ class PsbtUtxo { /// The list of individual TapLeaf merkle proofs used in script-path spending. /// Each proof shows how a leaf is part of the Taproot commitment. final List? leafScripts; - final List privateKeys; final List? muSig2ParticipantPublicKeys; final List? ripemd160; @@ -642,7 +651,6 @@ class PsbtUtxo { const PsbtUtxo( {required this.utxo, - required this.privateKeys, this.tx, required this.scriptPubKey, this.xOnlyOrInternalPubKey, @@ -661,11 +669,6 @@ class PsbtUtxo { factory PsbtUtxo.fromJson(Map json) { return PsbtUtxo( utxo: BitcoinUtxo.fromJson(json["utxo"]), - privateKeys: (json["privateKeys"] as List) - .map( - (e) => ECPrivate.fromHex(e), - ) - .toList(), tx: json["tx"] == null ? null : BtcTransaction.deserialize(BytesUtils.fromHexString(json["tx"])), @@ -705,7 +708,6 @@ class PsbtUtxo { "xonly": BytesUtils.tryToHexString(xOnlyOrInternalPubKey), "merkle_root": BytesUtils.tryToHexString(merkleRoot), "leaf_scripts": leafScripts?.map((e) => e.toJson()).toList(), - "privateKeys": privateKeys.map((e) => e.toHex()).toList() }; } } @@ -942,7 +944,7 @@ class PsbtTransactionOutput { "Either scriptPubKey or address must be provided."); } address ??= - BitcoinScriptUtils.generateAddressFromScriptPubKey(scriptPubKey); + BitcoinScriptUtils.tryGenerateAddressFromScriptPubKey(scriptPubKey); return PsbtTransactionOutput._( scriptPubKey: scriptPubKey, amount: amount, address: address); } diff --git a/lib/src/psbt/psbt_builder/versiones/v1.dart b/lib/src/psbt/psbt_builder/versiones/v1.dart index b0542af..bd74af9 100644 --- a/lib/src/psbt/psbt_builder/versiones/v1.dart +++ b/lib/src/psbt/psbt_builder/versiones/v1.dart @@ -23,19 +23,18 @@ class PsbtBuilderV0 extends PsbtBuilder { output: PsbtOutput(version: version))); } - BtcTransaction get _unsignedTx => _psbt.global.entries + BtcTransaction get _tx => _psbt.global.entries .whereType() .first .transaction; - @override TxInput txInput(int index) { - final unsignedTx = _unsignedTx; + final unsignedTx = _tx; PsbtUtils.validateTxInputs( psbInput: _psbt.input, inputIndex: index, inputsLength: unsignedTx.inputs.length); - return _unsignedTx.inputs[index]; + return _tx.inputs[index]; } @override @@ -47,19 +46,19 @@ class PsbtBuilderV0 extends PsbtBuilder { @override List txInputs() { - final unsignedTx = _unsignedTx; + final unsignedTx = _tx; return unsignedTx.inputs.clone(); } @override List txOutputs() { - final unsignedTx = _unsignedTx; + final unsignedTx = _tx; return unsignedTx.outputs.clone(); } @override BtcTransaction buildUnsignedTransaction() { - return _unsignedTx; + return _tx.copyWith(); } @override @@ -68,6 +67,7 @@ class PsbtBuilderV0 extends PsbtBuilder { } void _updateUnsignedTx(BtcTransaction transaction) { + // _unsignedTx.copyWith(); _psbt.global.updateGlobals([PsbtGlobalUnsignedTransaction(transaction)]); } @@ -75,7 +75,7 @@ class PsbtBuilderV0 extends PsbtBuilder { void _addNewTxOutput(PsbtTransactionOutput output) { super._addNewTxOutput(output); _psbt.output.addOutputs(output.toPsbtOutput(psbtVersion)); - BtcTransaction tx = _unsignedTx; + BtcTransaction tx = _tx; tx = tx.copyWith(outputs: [...tx.outputs, output.toTxOutput()]); _updateUnsignedTx(tx); } @@ -86,7 +86,7 @@ class PsbtBuilderV0 extends PsbtBuilder { _psbt.output.replaceOutput(index, output.toPsbtOutput(psbtVersion)); final txOutputs = this.txOutputs(); txOutputs[index] = output.toTxOutput(); - BtcTransaction tx = _unsignedTx; + BtcTransaction tx = _tx; tx = tx.copyWith(outputs: txOutputs); _updateUnsignedTx(tx); } @@ -97,7 +97,7 @@ class PsbtBuilderV0 extends PsbtBuilder { final outputs = txOutputs(); outputs.removeAt(index); _psbt.output.removeOutput(index); - BtcTransaction tx = _unsignedTx; + BtcTransaction tx = _tx; tx = tx.copyWith(outputs: outputs); _updateUnsignedTx(tx); } @@ -105,7 +105,7 @@ class PsbtBuilderV0 extends PsbtBuilder { @override void _updateTxInput(int index, PsbtTransactionInput input) { super._updateTxInput(index, input); - BtcTransaction tx = _unsignedTx; + BtcTransaction tx = _tx; final List newInputs = List.generate(tx.inputs.length, (i) { if (i == index) return input.txInput; return tx.inputs[i]; @@ -121,7 +121,7 @@ class PsbtBuilderV0 extends PsbtBuilder { _psbt.input.removeInput(index); final inputs = txInputs(); inputs.removeAt(index); - BtcTransaction tx = _unsignedTx; + BtcTransaction tx = _tx; final locktime = PsbtUtils.buildTransactionLocktime(inputs: inputs); tx = tx.copyWith(inputs: inputs, locktime: locktime); _updateUnsignedTx(tx); @@ -131,7 +131,7 @@ class PsbtBuilderV0 extends PsbtBuilder { void _addNewTxInput(PsbtTransactionInput input) { super._addNewTxInput(input); _psbt.input.addInputs(input.toPsbtInputs(psbtVersion)); - BtcTransaction tx = _unsignedTx; + BtcTransaction tx = _tx; final locktime = PsbtUtils.buildTransactionLocktime( inputs: [...tx.inputs, input.txInput]); tx = tx.copyWith(inputs: [...tx.inputs, input.txInput], locktime: locktime); diff --git a/lib/src/psbt/types/types/global.dart b/lib/src/psbt/types/types/global.dart index 6ef4704..1b2760e 100644 --- a/lib/src/psbt/types/types/global.dart +++ b/lib/src/psbt/types/types/global.dart @@ -141,8 +141,7 @@ class PsbtGlobal { } /// Retrieves all entries of a specified type. - List? getGlobals( - int index, PsbtGlobalTypes type) { + List? getGlobals(PsbtGlobalTypes type) { final data = _entries.where((e) => e.type == type); if (data.isEmpty) return null; return data.toList().cast(); diff --git a/lib/src/psbt/utils/utils.dart b/lib/src/psbt/utils/utils.dart index 96648b6..c410630 100644 --- a/lib/src/psbt/utils/utils.dart +++ b/lib/src/psbt/utils/utils.dart @@ -5,6 +5,7 @@ import 'package:bitcoin_base/src/crypto/crypto.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:bitcoin_base/src/provider/models/models.dart'; import 'package:bitcoin_base/src/psbt/psbt_builder/types/internal_types.dart'; +import 'package:bitcoin_base/src/psbt/types/types/global.dart'; import 'package:bitcoin_base/src/psbt/types/types/inputs.dart'; import 'package:bitcoin_base/src/psbt/types/types/outputs.dart'; import 'package:bitcoin_base/src/psbt/psbt_builder/types/types.dart'; @@ -12,6 +13,17 @@ import 'package:bitcoin_base/src/psbt/types/types/psbt.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; class PsbtUtils { + static final String fakeEcdsaPubKey = "0" * 33 * 2; + static final String fakeUnCompresedEcdsaPubKey = "0" * 65 * 2; + + /// 65 byte schnorr signature length + static const fakeSchnorSignaturBytes = + '0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'; + + /// 72 bytes (64 byte signature, 6-7 byte Der encoding length) + static const fakeECDSASignatureBytes = + '010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'; + static PsbtInputInfo _getInputInfo( {required Psbt psbt, required TxInput input, required int inputIndex}) { PsbtTxType inputType = PsbtTxType.legacy; @@ -420,12 +432,16 @@ class PsbtUtils { static List _finalizeMultisigScript( {required PsbtGeneratedTransactionDigest digest, - required MultiSignatureAddress multisig}) { + required MultiSignatureAddress multisig, + bool fake = false}) { final currentScript = digest.params.witnessScript ?? digest.params.p2shRedeemScript!; final multisigSigners = multisig.signers.map((e) => ECPublic.fromHex(e.publicKey)).toList(); - final validSignatures = digest.getPartialSignatures(multisigSigners); + List validSignatures = []; + if (!fake) { + validSignatures = digest.getPartialSignatures(multisigSigners); + } List signatures = []; final threshold = multisig.threshold; for (int i = 0; i < multisig.signers.length; i++) { @@ -433,12 +449,14 @@ class PsbtUtils { final pubKey = multisigSigners[i]; final signer = multisig.signers[i]; - final signature = validSignatures.firstWhereNullable((e) { - return e.publicKey == pubKey && e.mode == signer.keyType; - }); + final signature = fake + ? fakeECDSASignatureBytes + : validSignatures.firstWhereNullable((e) { + return e.publicKey == pubKey && e.mode == signer.keyType; + })?.signatureHex(); if (signature != null) { for (int w = 0; w < signer.weight; w++) { - signatures.add(signature.signatureHex()); + signatures.add(signature); if (signatures.length >= threshold) break; } } @@ -457,10 +475,12 @@ class PsbtUtils { static List _finalizeTaprootMultisigScript({ required P2trMultiSignatureAddress p2trMultisig, required PsbtGeneratedTransactionDigest digest, + bool fake = false, }) { final leafScript = digest.leafScript!; - List scriptSignatures = - digest.getTaprootScriptSignatures( + List scriptSignatures = fake + ? [] + : digest.getTaprootScriptSignatures( p2trMultisig.signers.map((e) => e.xOnly).toList()); scriptSignatures = () { final sigs = scriptSignatures.map((e) { @@ -474,15 +494,17 @@ class PsbtUtils { List signatures = []; int someWeight = 0; for (int i = 0; i < p2trMultisig.signers.length; i++) { - // if (signatures.length >= threshold) break; final signer = p2trMultisig.signers[i]; - final signature = scriptSignatures - .firstWhereNullable((e) => e.xOnlyPubKeyHex == signer.xOnly); + final signature = fake + ? fakeSchnorSignaturBytes + : scriptSignatures + .firstWhereNullable((e) => e.xOnlyPubKeyHex == signer.xOnly) + ?.signatureHex(); for (int w = 0; w < signer.weight; w++) { if (someWeight >= p2trMultisig.threshold) { signatures.add(''); } else { - signatures.add(signature?.signatureHex() ?? ''); + signatures.add(signature ?? ''); } if (signature != null) someWeight++; } @@ -532,10 +554,14 @@ class PsbtUtils { } static List _finalizeScriptSpent( - PsbtGeneratedTransactionDigest digest) { + PsbtGeneratedTransactionDigest digest, + {bool fake = false}) { final currentScript = digest.params.witnessScript ?? digest.params.p2shRedeemScript!; if (BitcoinScriptUtils.isP2wpkh(currentScript)) { + if (fake) { + return [fakeECDSASignatureBytes, fakeEcdsaPubKey]; + } final signature = digest.getPartialSignature(); final scriptPubKey = signature.publicKey.toP2wpkhInP2sh().toScriptPubKey(); @@ -547,21 +573,37 @@ class PsbtUtils { } if (BitcoinScriptUtils.isP2pkh(currentScript)) { - final signature = digest.getPartialSignature(); - final pubKey = findSciptKeyInfo( - publicKey: signature.publicKey, script: currentScript); - if (pubKey == null) { + final signature = digest.getPartialSignatureOrNull(); + final pubKey = signature == null + ? null + : findSciptKeyInfo( + publicKey: signature.publicKey, script: currentScript); + if (fake) { + if (pubKey != null) { + return [fakeECDSASignatureBytes, pubKey.key, currentScript.toHex()]; + } + return [ + fakeECDSASignatureBytes, + fakeUnCompresedEcdsaPubKey, + currentScript.toHex() + ]; + } + if (signature == null || pubKey == null) { throw DartBitcoinPluginException( "Cannot find current signer key in signature."); } return [signature.signatureHex(), pubKey.key, currentScript.toHex()]; } else if (BitcoinScriptUtils.isP2pk(currentScript)) { + if (fake) { + return [fakeECDSASignatureBytes, currentScript.toHex()]; + } final signature = digest.getPartialSignature(); return [signature.signatureHex(), currentScript.toHex()]; } final multisig = BitcoinScriptUtils.parseMultisigScript(currentScript); if (multisig != null) { - return _finalizeMultisigScript(digest: digest, multisig: multisig); + return _finalizeMultisigScript( + digest: digest, multisig: multisig, fake: fake); } if (currentScript.script.isEmpty) { return [currentScript.toHex()]; @@ -592,7 +634,7 @@ class PsbtUtils { return [pubKey.key, currentScript.toHex()]; } throw DartBitcoinPluginException( - "Unable to finalize custom script input at index ${digest.params.index}. Please use the onFinalizeCallback to complete the input finalization.", + "Unable to finalize custom script input at index ${digest.params.index}. Please use the onFinalizeCallback to complete the input finalization. ${currentScript.toHex()}", ); } @@ -649,7 +691,8 @@ class PsbtUtils { static List _finalizeTaprootScriptSpent( {required PsbtGeneratedTransactionDigest digest, - required PsbtInput input}) { + required PsbtInput input, + bool fake = false}) { PsbtInputTaprootLeafScript? leafScript = digest.leafScript; if (leafScript == null) { @@ -663,6 +706,9 @@ class PsbtUtils { if (BitcoinScriptUtils.hasAnyOpCheckSig(currentScript)) { if (BitcoinScriptUtils.isXOnlyOpChecksig(currentScript)) { + if (fake) { + return [fakeSchnorSignaturBytes, script, controlBlock]; + } final signature = digest .getTaprootScriptSignature(currentScript.script[0].toString()); return [signature.signatureHex(), script, controlBlock]; @@ -671,7 +717,7 @@ class PsbtUtils { BitcoinScriptUtils.isP2trMultiScript(leafScript.script); if (taprootMultisig != null) { return _finalizeTaprootMultisigScript( - p2trMultisig: taprootMultisig, digest: digest); + p2trMultisig: taprootMultisig, digest: digest, fake: fake); } } else { if (currentScript.script.isEmpty) { @@ -700,17 +746,36 @@ class PsbtUtils { static List _finalizeNonScriptSpent( {required PsbtGeneratedTransactionDigest digest, - required PsbtInput input}) { + required PsbtInput input, + bool fake = false}) { final script = digest.params.scriptPubKey; if (BitcoinScriptUtils.isP2tr(script)) { + if (fake) { + return [fakeSchnorSignaturBytes]; + } return [digest.getTaprootKeyPathSignature()]; } else if (BitcoinScriptUtils.isP2pk(digest.params.scriptPubKey)) { + if (fake) { + return [fakeECDSASignatureBytes]; + } return [digest.getPartialSignature().signatureHex()]; } else if (BitcoinScriptUtils.isP2pkh(script) || BitcoinScriptUtils.isP2wpkh(script)) { - final sig = digest.getPartialSignature(); - final pk = findSciptKeyInfo(publicKey: sig.publicKey, script: script); - if (pk == null) { + final sig = digest.getPartialSignatureOrNull(); + final pk = sig == null + ? null + : findSciptKeyInfo(publicKey: sig.publicKey, script: script); + if (fake) { + return [ + sig?.signatureHex() ?? fakeECDSASignatureBytes, + pk?.key ?? + (BitcoinScriptUtils.isP2wpkh(script) + ? fakeEcdsaPubKey + : fakeUnCompresedEcdsaPubKey) + ]; + } + + if (pk == null || sig == null) { throw DartBitcoinPluginException( "Signature public key does not match the scriptPubKey for input ${digest.params.index}."); } @@ -721,12 +786,20 @@ class PsbtUtils { details: {"scriptPubKey": digest.params.scriptPubKey.toString()}); } + static final List fakeFinalizeGlobalIdentifier = + 'fake_finalize'.codeUnits.asImmutableBytes; + static PsbtFinalizeInput finalizeInput( {required Psbt psbt, required int index, required List txInputs, required BtcTransaction unsignedTx, PsbtFinalizeResponse? userFinalizedInput}) { + final userData = psbt.global.getGlobals( + PsbtGlobalTypes.proprietary) ?? + []; + bool fake = userData.any((e) => + BytesUtils.bytesEqual(e.identifier, fakeFinalizeGlobalIdentifier)); final txType = getTxType(psbt.input); final params = getPsbtInputInfo(psbt: psbt, inputIndex: index, txInputs: txInputs); @@ -744,13 +817,14 @@ class PsbtUtils { txType: txType); } List unlockScript = switch (params.isScriptSpending) { - false => _finalizeNonScriptSpent(digest: digest, input: psbt.input), + false => + _finalizeNonScriptSpent(digest: digest, input: psbt.input, fake: fake), true => () { if (params.type.isP2tr) { return _finalizeTaprootScriptSpent( - digest: digest, input: psbt.input); + digest: digest, input: psbt.input, fake: fake); } - return _finalizeScriptSpent(digest); + return _finalizeScriptSpent(digest, fake: fake); }(), }; if (txType.isLegacy) { @@ -1212,30 +1286,4 @@ class PsbtUtils { static bool isAnyoneCanPay(int sighash) { return (sighash & BitcoinOpCodeConst.sighashAnyoneCanPay) != 0; } - - // static BigInt bchModifierRFC6979( - // {required List privateKey, - // required List digest, - // List algo16 = const [], - // List ndata = const []}) { - // final List blob = [...privateKey, ...digest, ...ndata, ...algo16]; - // List V = List.filled(32, 0x01); - // List K = CryptoOpsConst.zero.clone(); - // K = QuickCrypto.hmacsha256Hash(K, [...V, 0x00, ...blob]); - // V = QuickCrypto.hmacsha256Hash(K, V); - // K = QuickCrypto.hmacsha256Hash(K, [...V, 0x01, ...blob]); - // V = QuickCrypto.hmacsha256Hash(K, V); - // BigInt k = BigInt.zero; - // while (true) { - // V = QuickCrypto.hmacsha256Hash(K, V); - // List T = V.clone(); - // k = BigintUtils.fromBytes(T); - // if (k > BigInt.zero && k < Curves.generatorSecp256k1.order!) { - // break; - // } - // K = QuickCrypto.hmacsha256Hash(K, [...V, 0x00]); - // V = QuickCrypto.hmacsha256Hash(V, []); - // } - // return k; - // } } diff --git a/pubspec.yaml b/pubspec.yaml index f8820bd..9943da0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: bitcoin_base description: A versatile library for Bitcoin, Dogecoin, Litecoin, Dash, BSV, and BCH. Supports P2PKH, P2SH, P2WPKH, P2WSH, P2TR, with advanced creation, signing, and spending capabilities. -version: 6.2.0 +version: 6.3.0 homepage: "https://github.com/mrtnetwork/bitcoin_base" repository: "https://github.com/mrtnetwork/bitcoin_base" Author: mrhaydari.t@gmail.com From a1e5177a6bbae7931fed06d939f073a56ab80ba6 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Thu, 24 Apr 2025 20:59:41 +0330 Subject: [PATCH 03/12] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b327ee7..389649d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 6.3.0 - Update dependencies. -- Add estimate size for psbt +- Add estimate transaction size for psbt - Add more script validation in psbt ## 6.2.0 From 30687854f91b96a4085f95916555ad4664e0dfdb Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Tue, 6 May 2025 14:59:10 +0330 Subject: [PATCH 04/12] V6.4.0 - Update dependencies. - Improved security: Private key operations now use blinded ecmult for safer public key generation. - All signing methods now use constant-time operations with blinded ecmult to securely generate signatures. --- CHANGELOG.md | 8 ++++++++ example/lib/musig/methods.dart | 7 +++++-- example/pubspec.lock | 6 +++--- example/pubspec.yaml | 2 +- lib/src/bitcoin/address/legacy_address.dart | 2 +- lib/src/crypto/keypair/ec_private.dart | 11 ++++++----- lib/src/crypto/keypair/ec_public.dart | 6 +++--- .../psbt/psbt_builder/types/internal_types.dart | 5 +++-- lib/src/psbt/types/types/inputs.dart | 12 ++++++------ lib/src/psbt/types/types/outputs.dart | 14 +++++++------- pubspec.yaml | 4 ++-- 11 files changed, 45 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 389649d..9b970aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 6.4.0 + +- Update dependencies. +- Improved security: Private key operations now use blinded ecmult for safer public key generation. +- All signing methods now use constant-time operations with blinded ecmult to securely generate signatures. + + + ## 6.3.0 - Update dependencies. diff --git a/example/lib/musig/methods.dart b/example/lib/musig/methods.dart index e641a18..e5c22ee 100644 --- a/example/lib/musig/methods.dart +++ b/example/lib/musig/methods.dart @@ -3,8 +3,11 @@ import 'package:example/services_examples/electrum/electrum_ssl_service.dart'; Future getProvider( {String url = "testnet4-electrumx.wakiyamap.dev:51002"}) async { - final service = await ElectrumSSLService.connect( - "testnet4-electrumx.wakiyamap.dev:51002"); + // final service = await ElectrumSSLService.connect( + // "testnet4-electrumx.wakiyamap.dev:51002"); + + final service = + await ElectrumSSLService.connect("testnet.aranguren.org:51002"); return ElectrumProvider(service); } diff --git a/example/pubspec.lock b/example/pubspec.lock index ef10dfa..0de348c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -15,15 +15,15 @@ packages: path: ".." relative: true source: path - version: "6.2.0" + version: "6.3.0" blockchain_utils: dependency: "direct main" description: name: blockchain_utils - sha256: "104f5212ade36b01a67511b074feed2ece513876d45c8daace277818cdc16873" + sha256: fbddd2a7f1849d2244a35bf996b6fb00bf6bd557f0e13aed65ba0c16ad133cb7 url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "5.0.0" boolean_selector: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 49619de..5612b28 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: path: ../ # blockchain_utils: # path: ../../blockchain_utils - blockchain_utils: ^4.4.0 + blockchain_utils: ^5.0.0 http: ^1.2.0 dev_dependencies: diff --git a/lib/src/bitcoin/address/legacy_address.dart b/lib/src/bitcoin/address/legacy_address.dart index ec189ca..baaef74 100644 --- a/lib/src/bitcoin/address/legacy_address.dart +++ b/lib/src/bitcoin/address/legacy_address.dart @@ -154,7 +154,7 @@ class P2pkAddress extends LegacyAddress { P2pkAddress._(this.publicKey) : super._(); factory P2pkAddress({required String publicKey}) { final toBytes = BytesUtils.fromHexString(publicKey); - if (!Secp256k1PublicKeyEcdsa.isValidBytes(toBytes)) { + if (!Secp256k1PublicKey.isValidBytes(toBytes)) { throw const DartBitcoinPluginException('Invalid Public key.'); } return P2pkAddress._(StringUtils.strip0x(publicKey.toLowerCase())); diff --git a/lib/src/crypto/keypair/ec_private.dart b/lib/src/crypto/keypair/ec_private.dart index 2b2ee7f..0267e9a 100644 --- a/lib/src/crypto/keypair/ec_private.dart +++ b/lib/src/crypto/keypair/ec_private.dart @@ -101,7 +101,7 @@ class ECPrivate { List extraEntropy = const [], }) { final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); - final signature = btcSigner.signMessage( + final signature = btcSigner.signMessageConst( message: message, messagePrefix: messagePrefix, extraEntropy: extraEntropy); @@ -122,7 +122,7 @@ class ECPrivate { {String messagePrefix = BitcoinSignerUtils.signMessagePrefix, List extraEntropy = const []}) { final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); - final signature = btcSigner.signMessage( + final signature = btcSigner.signMessageConst( message: message, messagePrefix: messagePrefix, extraEntropy: extraEntropy); @@ -138,7 +138,7 @@ class ECPrivate { List extraEntropy = const []}) { final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); List signature = - btcSigner.signECDSADer(txDigest, extraEntropy: extraEntropy); + btcSigner.signECDSADerConst(txDigest, extraEntropy: extraEntropy); if (sighash != null) { signature = [...signature, sighash]; } @@ -157,7 +157,8 @@ class ECPrivate { {int sighash = BitcoinOpCodeConst.sighashDefault, List extraEntropy = const []}) { final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); - var signature = btcSigner.signSchnorr(txDigest, extraEntropy: extraEntropy); + var signature = + btcSigner.signSchnorrConst(txDigest, extraEntropy: extraEntropy); if (sighash != BitcoinOpCodeConst.sighashDefault) { signature = [...signature, sighash]; } @@ -193,7 +194,7 @@ class ECPrivate { "Use either tapTweakHash or (treeScript/merkleRoot), not both."); } final btcSigner = BitcoinKeySigner.fromKeyBytes(toBytes()); - List signature = btcSigner.signBip340( + List signature = btcSigner.signBip340Const( digest: txDigest, aux: aux, tapTweakHash: tweak diff --git a/lib/src/crypto/keypair/ec_public.dart b/lib/src/crypto/keypair/ec_public.dart index f33d3dc..dee7b0f 100644 --- a/lib/src/crypto/keypair/ec_public.dart +++ b/lib/src/crypto/keypair/ec_public.dart @@ -9,7 +9,7 @@ import 'package:blockchain_utils/blockchain_utils.dart'; typedef PublicKeyType = PubKeyModes; class ECPublic { - final Secp256k1PublicKeyEcdsa publicKey; + final Secp256k1PublicKey publicKey; const ECPublic._(this.publicKey); factory ECPublic.fromBip32(Bip32PublicKey publicKey) { @@ -17,13 +17,13 @@ class ECPublic { throw const DartBitcoinPluginException( 'invalid public key curve for bitcoin'); } - return ECPublic._(publicKey.pubKey as Secp256k1PublicKeyEcdsa); + return ECPublic._(publicKey.pubKey as Secp256k1PublicKey); } ProjectiveECCPoint get point => publicKey.point.cast(); /// Constructs an ECPublic key from a byte representation. factory ECPublic.fromBytes(List public) { - final publicKey = Secp256k1PublicKeyEcdsa.fromBytes(public); + final publicKey = Secp256k1PublicKey.fromBytes(public); return ECPublic._(publicKey); } diff --git a/lib/src/psbt/psbt_builder/types/internal_types.dart b/lib/src/psbt/psbt_builder/types/internal_types.dart index 3688631..17e84f2 100644 --- a/lib/src/psbt/psbt_builder/types/internal_types.dart +++ b/lib/src/psbt/psbt_builder/types/internal_types.dart @@ -335,13 +335,14 @@ class PsbtGeneratedTransactionDigest { bool verifyEcdsaSignature(PsbtInputPartialSig sig) { try { + /// handle bch schnorr signature if (CryptoSignatureUtils.isValidSchnorrSignature(sig.signature)) { return sig.publicKey .verifySchnorrSignature(digest: digest, signature: sig.signature); } - - return sig.publicKey + final verify = sig.publicKey .verifyDerSignature(digest: digest, signature: sig.signature); + return verify; } catch (_) { return false; } diff --git a/lib/src/psbt/types/types/inputs.dart b/lib/src/psbt/types/types/inputs.dart index 5ba8dcd..7afff35 100644 --- a/lib/src/psbt/types/types/inputs.dart +++ b/lib/src/psbt/types/types/inputs.dart @@ -651,7 +651,7 @@ class PsbtInputBip32DerivationPath extends PsbtInputData { required List publicKey, }) { if (fingerprint.length == Bip32KeyDataConst.fingerprintByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(publicKey)) { + Secp256k1PublicKey.isValidBytes(publicKey)) { return PsbtInputBip32DerivationPath._( fingerprint: fingerprint, indexes: indexes, @@ -694,7 +694,7 @@ class PsbtInputBip32DerivationPath extends PsbtInputData { offset, offset + Bip32KeyDataConst.keyIndexByteLen)); }); if (fingerPrint.length == Bip32KeyDataConst.fingerprintByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(keypair.key.extraData ?? [])) { + Secp256k1PublicKey.isValidBytes(keypair.key.extraData ?? [])) { return PsbtInputBip32DerivationPath._( fingerprint: fingerPrint, indexes: bip32Indexes, @@ -1943,9 +1943,9 @@ class PsbtInputMuSig2PublicNonce extends PsbtInputData { : keypair.key.extraData! .sublist(EcdsaKeysConst.pubKeyCompressedByteLen * 2); if (publicKey.length == EcdsaKeysConst.pubKeyCompressedByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(publicKey) && + Secp256k1PublicKey.isValidBytes(publicKey) && plainPublicKey.length == EcdsaKeysConst.pubKeyCompressedByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(plainPublicKey) && + Secp256k1PublicKey.isValidBytes(plainPublicKey) && (hash == null || hash.length == QuickCrypto.sha256DigestSize) && keypair.value.data.length == EcdsaKeysConst.pubKeyCompressedByteLen * 2) { @@ -2042,9 +2042,9 @@ class PsbtInputMuSig2ParticipantPartialSignature ? null : keypair.key.extraData!.sublist(66); if (publicKey.length == EcdsaKeysConst.pubKeyCompressedByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(publicKey) && + Secp256k1PublicKey.isValidBytes(publicKey) && plainPublicKey.length == EcdsaKeysConst.pubKeyCompressedByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(plainPublicKey) && + Secp256k1PublicKey.isValidBytes(plainPublicKey) && (hash == null || hash.length == QuickCrypto.sha256DigestSize) && keypair.value.data.length == QuickCrypto.sha256DigestSize) { return PsbtInputMuSig2ParticipantPartialSignature._( diff --git a/lib/src/psbt/types/types/outputs.dart b/lib/src/psbt/types/types/outputs.dart index b5db135..f7e9c17 100644 --- a/lib/src/psbt/types/types/outputs.dart +++ b/lib/src/psbt/types/types/outputs.dart @@ -326,7 +326,7 @@ class PsbtOutputBip32DerivationPath extends PsbtOutputData { required List indexes, required List publicKey}) { if (fingerprint.length == Bip32KeyDataConst.fingerprintByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(publicKey)) { + Secp256k1PublicKey.isValidBytes(publicKey)) { return PsbtOutputBip32DerivationPath._( fingerprint: fingerprint, indexes: indexes, @@ -348,7 +348,7 @@ class PsbtOutputBip32DerivationPath extends PsbtOutputData { "Invalid PSBT bip32 derivation path type flag"); } if (keypair.key.extraData == null || - !Secp256k1PublicKeyEcdsa.isValidBytes(keypair.key.extraData!)) { + !Secp256k1PublicKey.isValidBytes(keypair.key.extraData!)) { throw DartBitcoinPluginException( "Invalid PSBT bip32 derivation public key."); } @@ -370,7 +370,7 @@ class PsbtOutputBip32DerivationPath extends PsbtOutputData { offset, offset + Bip32KeyDataConst.keyIndexByteLen)); }); if (fingerPrint.length == Bip32KeyDataConst.fingerprintByteLen && - Secp256k1PublicKeyEcdsa.isValidBytes(keypair.key.extraData!)) { + Secp256k1PublicKey.isValidBytes(keypair.key.extraData!)) { return PsbtOutputBip32DerivationPath._( fingerprint: fingerPrint, indexes: bip32Indexes, @@ -748,8 +748,8 @@ class PsbtOutputMuSig2ParticipantPublicKeys extends PsbtOutputData { super(type: PsbtOutputTypes.muSig2ParticipantPublicKeys); factory PsbtOutputMuSig2ParticipantPublicKeys( {required List aggregatePubKey, required List> pubKeys}) { - if (Secp256k1PublicKeyEcdsa.isValidBytes(aggregatePubKey) && - pubKeys.every(Secp256k1PublicKeyEcdsa.isValidBytes)) { + if (Secp256k1PublicKey.isValidBytes(aggregatePubKey) && + pubKeys.every(Secp256k1PublicKey.isValidBytes)) { return PsbtOutputMuSig2ParticipantPublicKeys._( aggregatePubKey: aggregatePubKey, pubKeys: pubKeys, @@ -781,8 +781,8 @@ class PsbtOutputMuSig2ParticipantPublicKeys extends PsbtOutputData { offset, offset + EcdsaKeysConst.pubKeyCompressedByteLen); pubKeys.add(key); } - if (Secp256k1PublicKeyEcdsa.isValidBytes(keypair.key.extraData!) && - pubKeys.every(Secp256k1PublicKeyEcdsa.isValidBytes)) { + if (Secp256k1PublicKey.isValidBytes(keypair.key.extraData!) && + pubKeys.every(Secp256k1PublicKey.isValidBytes)) { return PsbtOutputMuSig2ParticipantPublicKeys._( aggregatePubKey: keypair.key.extraData!, pubKeys: pubKeys, diff --git a/pubspec.yaml b/pubspec.yaml index 9943da0..f48dc20 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: bitcoin_base description: A versatile library for Bitcoin, Dogecoin, Litecoin, Dash, BSV, and BCH. Supports P2PKH, P2SH, P2WPKH, P2WSH, P2TR, with advanced creation, signing, and spending capabilities. -version: 6.3.0 +version: 6.4.0 homepage: "https://github.com/mrtnetwork/bitcoin_base" repository: "https://github.com/mrtnetwork/bitcoin_base" Author: mrhaydari.t@gmail.com @@ -16,7 +16,7 @@ environment: dependencies: - blockchain_utils: ^4.4.0 + blockchain_utils: ^5.0.0 # blockchain_utils: # path: ../blockchain_utils From 532ce74dd2720cbf8792057a952e11dca348ca04 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Tue, 6 May 2025 15:22:39 +0330 Subject: [PATCH 05/12] Update dart.yml --- .github/workflows/dart.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index f6c130c..9e4dd4f 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -42,5 +42,5 @@ jobs: run: dart test ./test - name: Run tests web - run: dart test -p chrome ./test + run: dart test -p chrome ./test --timeout=20m From 58ef769f0245b56b45b6d5ae802774f17b754788 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Tue, 6 May 2025 16:56:44 +0330 Subject: [PATCH 06/12] Update dart.yml --- .github/workflows/dart.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 9e4dd4f..cc6bc3f 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -43,4 +43,6 @@ jobs: - name: Run tests web run: dart test -p chrome ./test --timeout=20m - + + - name: Run tests web wasm + run: dart test -p chrome ./test -c dart2wasm --timeout=20m From 42b8abe795b390b707949ebf4fc46a46900120d0 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Wed, 28 May 2025 22:22:00 +0330 Subject: [PATCH 07/12] V6.5.0 - Fix transaction parsing in Mempool API. --- CHANGELOG.md | 4 + .../provider/electrum_methods/methods.dart | 3 +- .../electrum_methods/methods/get_merkle.dart | 7 +- .../electrum_methods/methods/get_unspet.dart | 2 +- .../methods/get_verbose_transaction.dart | 32 +++++ .../methods/headers_subscribe.dart | 9 +- ...status.dart => script_hash_subscribe.dart} | 8 +- .../block_cypher/block_cypher_models.dart | 66 +++++++++ lib/src/provider/models/config.dart | 18 ++- .../models/electrum/electrum_utxo.dart | 51 ------- lib/src/provider/models/electrum/models.dart | 127 ++++++++++++++++++ .../models/mempool/mempol_models.dart | 2 +- lib/src/provider/models/models.dart | 2 +- lib/src/provider/providers/explorer.dart | 20 ++- pubspec.yaml | 2 +- 15 files changed, 275 insertions(+), 78 deletions(-) create mode 100644 lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart rename lib/src/provider/electrum_methods/methods/{status.dart => script_hash_subscribe.dart} (75%) delete mode 100644 lib/src/provider/models/electrum/electrum_utxo.dart create mode 100644 lib/src/provider/models/electrum/models.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b970aa..7b9a7bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.5.0 + +- Fix transaction parsing in Mempool API. + ## 6.4.0 - Update dependencies. diff --git a/lib/src/provider/electrum_methods/methods.dart b/lib/src/provider/electrum_methods/methods.dart index f217aae..9ad7b3d 100644 --- a/lib/src/provider/electrum_methods/methods.dart +++ b/lib/src/provider/electrum_methods/methods.dart @@ -26,5 +26,6 @@ export 'methods/protx_info.dart'; export 'methods/relay_fee.dart'; export 'methods/scripthash_unsubscribe.dart'; export 'methods/server_peer_subscribe.dart'; -export 'methods/status.dart'; +export 'methods/script_hash_subscribe.dart'; export 'methods/get_raw_transaction.dart'; +export 'methods/get_verbose_transaction.dart'; diff --git a/lib/src/provider/electrum_methods/methods/get_merkle.dart b/lib/src/provider/electrum_methods/methods/get_merkle.dart index 7620749..0e35191 100644 --- a/lib/src/provider/electrum_methods/methods/get_merkle.dart +++ b/lib/src/provider/electrum_methods/methods/get_merkle.dart @@ -1,10 +1,11 @@ import 'package:bitcoin_base/src/provider/core/methods.dart'; import 'package:bitcoin_base/src/provider/core/params.dart'; +import 'package:bitcoin_base/src/provider/models/electrum/models.dart'; /// Return the merkle branch to a confirmed transaction given its hash and height. /// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html class ElectrumRequestGetMerkle - extends ElectrumRequest, Map> { + extends ElectrumRequest> { ElectrumRequestGetMerkle( {required this.transactionHash, required this.height}); @@ -24,7 +25,7 @@ class ElectrumRequestGetMerkle } @override - Map onResonse(result) { - return result; + ElectrumGetMerkleResponse onResonse(result) { + return ElectrumGetMerkleResponse.fromJson(result); } } diff --git a/lib/src/provider/electrum_methods/methods/get_unspet.dart b/lib/src/provider/electrum_methods/methods/get_unspet.dart index 0e36cc2..ad8966a 100644 --- a/lib/src/provider/electrum_methods/methods/get_unspet.dart +++ b/lib/src/provider/electrum_methods/methods/get_unspet.dart @@ -1,4 +1,4 @@ -import 'package:bitcoin_base/src/provider/models/electrum/electrum_utxo.dart'; +import 'package:bitcoin_base/src/provider/models/electrum/models.dart'; import 'package:bitcoin_base/src/provider/core/methods.dart'; import 'package:bitcoin_base/src/provider/core/params.dart'; diff --git a/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart b/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart new file mode 100644 index 0000000..230533f --- /dev/null +++ b/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart @@ -0,0 +1,32 @@ +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:bitcoin_base/src/provider/core/methods.dart'; +import 'package:bitcoin_base/src/provider/core/params.dart'; + +/// Return a raw transaction. +/// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html +class ElectrumRequestGetVerboseTransaction + extends ElectrumRequest> { + ElectrumRequestGetVerboseTransaction(this.transactionHash); + + /// The transaction hash as a hexadecimal string. + final String transactionHash; + + /// blockchain.transaction.get + @override + String get method => ElectrumRequestMethods.getTransaction.method; + + @override + List toJson() { + return [transactionHash, true]; + } + + /// If verbose is false: + /// The raw transaction as a hexadecimal string. + /// + /// If verbose is true: + /// The result is a coin-specific dictionary – whatever the coin daemon returns when asked for a verbose form of the raw transaction. + @override + ElectrumVerbosTxResponse onResonse(result) { + return ElectrumVerbosTxResponse.fromJson(result); + } +} diff --git a/lib/src/provider/electrum_methods/methods/headers_subscribe.dart b/lib/src/provider/electrum_methods/methods/headers_subscribe.dart index 9dc0f18..352cff5 100644 --- a/lib/src/provider/electrum_methods/methods/headers_subscribe.dart +++ b/lib/src/provider/electrum_methods/methods/headers_subscribe.dart @@ -1,10 +1,11 @@ import 'package:bitcoin_base/src/provider/core/methods.dart'; import 'package:bitcoin_base/src/provider/core/params.dart'; +import 'package:bitcoin_base/src/provider/models/electrum/models.dart'; /// Subscribe to receive block headers when a new block is found. /// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html -class ElectrumRequestHeaderSubscribe - extends ElectrumRequest, Map> { +class ElectrumRequestHeaderSubscribe extends ElectrumRequest< + ElectrumHeaderSubscribeResponse, Map> { /// blockchain.headers.subscribe @override String get method => ElectrumRequestMethods.headersSubscribe.method; @@ -16,7 +17,7 @@ class ElectrumRequestHeaderSubscribe /// The header of the current block chain tip. @override - Map onResonse(result) { - return result; + ElectrumHeaderSubscribeResponse onResonse(result) { + return ElectrumHeaderSubscribeResponse.fromJson(result); } } diff --git a/lib/src/provider/electrum_methods/methods/status.dart b/lib/src/provider/electrum_methods/methods/script_hash_subscribe.dart similarity index 75% rename from lib/src/provider/electrum_methods/methods/status.dart rename to lib/src/provider/electrum_methods/methods/script_hash_subscribe.dart index 0a3b978..3f15f28 100644 --- a/lib/src/provider/electrum_methods/methods/status.dart +++ b/lib/src/provider/electrum_methods/methods/script_hash_subscribe.dart @@ -4,7 +4,7 @@ import 'package:bitcoin_base/src/provider/core/params.dart'; /// Subscribe to a script hash. /// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html class ElectrumRequestScriptHashSubscribe - extends ElectrumRequest, dynamic> { + extends ElectrumRequest { ElectrumRequestScriptHashSubscribe({required this.scriptHash}); /// /// The script hash as a hexadecimal string (BitcoinBaseAddress.pubKeyHash()) @@ -18,10 +18,4 @@ class ElectrumRequestScriptHashSubscribe List toJson() { return [scriptHash]; } - - /// The status of the script hash. - @override - Map onResonse(result) { - return Map.from(result); - } } diff --git a/lib/src/provider/models/block_cypher/block_cypher_models.dart b/lib/src/provider/models/block_cypher/block_cypher_models.dart index 09f994c..e894534 100644 --- a/lib/src/provider/models/block_cypher/block_cypher_models.dart +++ b/lib/src/provider/models/block_cypher/block_cypher_models.dart @@ -1,5 +1,6 @@ import 'package:bitcoin_base/src/bitcoin/address/address.dart'; import 'package:bitcoin_base/src/provider/models/utxo_details.dart'; +import 'package:blockchain_utils/utils/numbers/numbers.dart'; import 'package:blockchain_utils/utils/numbers/utils/bigint_utils.dart'; class TxRef implements UTXO { @@ -308,3 +309,68 @@ class BlockCypherAddressInfo { ); } } + +class BlockCypherChainInfo { + final BigInt height; + final String? hash; + final String? time; + final String? latestUrl; + final String? previousHash; + final String? previousUrl; + final int? peerCount; + final int? unconfirmedCount; + final BigInt? highFeePerKb; + final BigInt? mediumFeePerKb; + final BigInt? lowFeePerKb; + final BigInt? latestForkHeight; + final String? latestForkHash; + const BlockCypherChainInfo( + {required this.height, + required this.hash, + required this.time, + required this.latestUrl, + required this.previousHash, + required this.previousUrl, + required this.peerCount, + required this.unconfirmedCount, + required this.highFeePerKb, + required this.mediumFeePerKb, + required this.lowFeePerKb, + required this.latestForkHeight, + required this.latestForkHash}); + + factory BlockCypherChainInfo.fromJson(Map json) { + return BlockCypherChainInfo( + height: BigintUtils.parse(json["height"]), + hash: json["hash"], + time: json["time"], + latestUrl: json["latest_url"], + previousHash: json["previous_hash"], + previousUrl: json["previous_url"], + peerCount: IntUtils.tryParse(json["peer_count"]), + unconfirmedCount: IntUtils.tryParse(json["unconfirmed_count"]), + highFeePerKb: BigintUtils.tryParse(json["high_fee_per_kb"]), + mediumFeePerKb: BigintUtils.tryParse(json["medium_fee_per_kb"]), + lowFeePerKb: BigintUtils.tryParse(json["low_fee_per_kb"]), + latestForkHeight: BigintUtils.tryParse(json["last_fork_height"]), + latestForkHash: json["last_fork_hash"]); + } + + Map toJson() { + return { + "height": height.toString(), + "hash": hash, + "time": time, + "latest_url": latestUrl, + "previous_hash": previousHash, + "previous_url": previousUrl, + "peer_count": peerCount, + "unconfirmed_count": unconfirmedCount, + "high_fee_per_kb": highFeePerKb?.toString(), + "medium_fee_per_kb": mediumFeePerKb?.toString(), + "low_fee_per_kb": lowFeePerKb?.toString(), + "last_fork_height": latestForkHeight?.toString(), + "last_fork_hash": latestForkHash + }; + } +} diff --git a/lib/src/provider/models/config.dart b/lib/src/provider/models/config.dart index 1ace97c..7dbfaa5 100644 --- a/lib/src/provider/models/config.dart +++ b/lib/src/provider/models/config.dart @@ -11,6 +11,7 @@ class APIConfig { final String transactions; final String sendTransaction; final String blockHeight; + final String latestBlockHeight; final APIType apiType; final String rawTransaction; final BasedUtxoNetwork network; @@ -20,7 +21,7 @@ class APIConfig { case APIType.mempool: return APIConfig.mempool(network); default: - return APIConfig.mempool(network); + return APIConfig.fromBlockCypher(network); } } @@ -48,11 +49,15 @@ class APIConfig { return baseUrl.replaceAll('###', address); } - String getBlockHeight(int blockHaight) { + String getBlockHashByHeight(int blockHaight) { final baseUrl = blockHeight; return baseUrl.replaceAll('###', '$blockHaight'); } + String getLatestBlockHeightUrl() { + return latestBlockHeight; + } + factory APIConfig.fromBlockCypher(BasedUtxoNetwork network) { String baseUrl; switch (network) { @@ -86,7 +91,8 @@ class APIConfig { apiType: APIType.blockCypher, transactions: '$baseUrl/addrs/###/full?limit=200', network: network, - blockHeight: '$baseUrl/blocks/###'); + blockHeight: '$baseUrl/blocks/###', + latestBlockHeight: "$baseUrl/"); } factory APIConfig.mempool(BasedUtxoNetwork network, {String? baseUrl}) { @@ -114,7 +120,8 @@ class APIConfig { apiType: APIType.mempool, transactions: '$baseUrl/address/###/txs', network: network, - blockHeight: '$baseUrl/block-height/###'); + blockHeight: '$baseUrl/block-height/###', + latestBlockHeight: "$baseUrl/blocks/tip/height"); } APIConfig( @@ -126,5 +133,6 @@ class APIConfig { required this.apiType, required this.network, required this.blockHeight, - required this.rawTransaction}); + required this.rawTransaction, + required this.latestBlockHeight}); } diff --git a/lib/src/provider/models/electrum/electrum_utxo.dart b/lib/src/provider/models/electrum/electrum_utxo.dart deleted file mode 100644 index 4e6e848..0000000 --- a/lib/src/provider/models/electrum/electrum_utxo.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:bitcoin_base/src/bitcoin/address/address.dart'; -import 'package:bitcoin_base/src/cash_token/cash_token.dart'; -import 'package:bitcoin_base/src/provider/api_provider.dart'; -import 'package:blockchain_utils/utils/numbers/utils/bigint_utils.dart'; -import 'package:blockchain_utils/utils/numbers/utils/int_utils.dart'; - -class ElectrumUtxo implements UTXO { - factory ElectrumUtxo.fromJson(Map json) { - return ElectrumUtxo._( - height: IntUtils.parse(json['height']), - txId: json['tx_hash'], - vout: IntUtils.parse(json['tx_pos']), - value: BigintUtils.parse(json['value']), - token: json["token_data"] == null - ? null - : CashToken.fromJson(json['token_data'])); - } - const ElectrumUtxo._( - {required this.height, - required this.txId, - required this.vout, - required this.value, - this.token}); - final int height; - final String txId; - final int vout; - final BigInt value; - final CashToken? token; - - @override - BitcoinUtxo toUtxo(BitcoinAddressType addressType) { - return BitcoinUtxo( - txHash: txId, - value: value, - vout: vout, - scriptType: addressType, - blockHeight: height, - token: token); - } - - @override - Map toJson() { - return { - "token_data": token?.toJson(), - "height": height, - "tx_hash": txId, - "tx_pos": vout, - "value": value.toString() - }; - } -} diff --git a/lib/src/provider/models/electrum/models.dart b/lib/src/provider/models/electrum/models.dart new file mode 100644 index 0000000..60298f8 --- /dev/null +++ b/lib/src/provider/models/electrum/models.dart @@ -0,0 +1,127 @@ +import 'package:bitcoin_base/src/bitcoin/address/address.dart'; +import 'package:bitcoin_base/src/cash_token/cash_token.dart'; +import 'package:bitcoin_base/src/provider/api_provider.dart'; +import 'package:blockchain_utils/helper/extensions/extensions.dart'; +import 'package:blockchain_utils/utils/numbers/utils/bigint_utils.dart'; +import 'package:blockchain_utils/utils/numbers/utils/int_utils.dart'; + +class ElectrumUtxo implements UTXO { + factory ElectrumUtxo.fromJson(Map json) { + return ElectrumUtxo._( + height: IntUtils.parse(json['height']), + txId: json['tx_hash'], + vout: IntUtils.parse(json['tx_pos']), + value: BigintUtils.parse(json['value']), + token: json["token_data"] == null + ? null + : CashToken.fromJson(json['token_data'])); + } + const ElectrumUtxo._( + {required this.height, + required this.txId, + required this.vout, + required this.value, + this.token}); + final int height; + final String txId; + final int vout; + final BigInt value; + final CashToken? token; + + @override + BitcoinUtxo toUtxo(BitcoinAddressType addressType) { + return BitcoinUtxo( + txHash: txId, + value: value, + vout: vout, + scriptType: addressType, + blockHeight: height, + token: token); + } + + @override + Map toJson() { + return { + "token_data": token?.toJson(), + "height": height, + "tx_hash": txId, + "tx_pos": vout, + "value": value.toString() + }; + } +} + +class ElectrumHeaderSubscribeResponse { + final int block; + final String hex; + const ElectrumHeaderSubscribeResponse( + {required this.block, required this.hex}); + factory ElectrumHeaderSubscribeResponse.fromJson(Map json) { + return ElectrumHeaderSubscribeResponse( + block: IntUtils.parse(json["height"]), hex: json["hex"]); + } +} + +class ElectrumGetMerkleResponse { + final int blockHeight; + final int pos; + final List merkle; + ElectrumGetMerkleResponse({ + required this.blockHeight, + required this.pos, + required List merkle, + }) : merkle = merkle.immutable; + factory ElectrumGetMerkleResponse.fromJson(Map json) { + return ElectrumGetMerkleResponse( + blockHeight: IntUtils.parse(json["block_height"]), + pos: IntUtils.parse(json["pos"]), + merkle: (json["merkle"] as List?)?.cast() ?? [], + ); + } +} + +/// txid, hash, version, size, vsize, weight, locktime, vin, vout, hex, blockhash, confirmations, time, blocktime +/// txid, hash, version, size, vsize, weight, locktime, vin, vout, hex + +class ElectrumVerbosTxResponse { + final String txId; + final String hash; + final int version; + final int size; + final int? vsize; + final int? weight; + final int locktime; + final String hex; + final String? blockhash; + final int? confirmations; + final int? time; + final int? blocktime; + factory ElectrumVerbosTxResponse.fromJson(Map json) { + return ElectrumVerbosTxResponse( + txId: json["txid"], + hash: json["hash"], + version: IntUtils.parse(json["version"]), + size: IntUtils.parse(json["size"]), + vsize: IntUtils.tryParse(json["vsize"]), + weight: IntUtils.tryParse(json["weight"]), + locktime: IntUtils.parse(json["locktime"]), + hex: json["hex"], + blockhash: json["blockhash"], + confirmations: IntUtils.tryParse(json["confirmations"]), + blocktime: IntUtils.tryParse(json["blocktime"]), + time: IntUtils.tryParse(json["time"])); + } + const ElectrumVerbosTxResponse( + {required this.txId, + required this.hash, + required this.version, + required this.size, + required this.vsize, + required this.weight, + required this.locktime, + required this.hex, + this.blockhash, + this.confirmations, + this.time, + this.blocktime}); +} diff --git a/lib/src/provider/models/mempool/mempol_models.dart b/lib/src/provider/models/mempool/mempol_models.dart index d891541..4f06041 100644 --- a/lib/src/provider/models/mempool/mempol_models.dart +++ b/lib/src/provider/models/mempool/mempol_models.dart @@ -56,7 +56,7 @@ class MempoolVin { prevOut: MempoolPrevOut.fromJson(json['prevout']), scriptSig: json['scriptsig'], scriptSigAsm: json['scriptsig_asm'], - witness: List.from(json['witness']), + witness: List.from(json['witness'] ?? []), isCoinbase: json['is_coinbase'], sequence: json['sequence'], ); diff --git a/lib/src/provider/models/models.dart b/lib/src/provider/models/models.dart index 11fdd0a..6ad78a3 100644 --- a/lib/src/provider/models/models.dart +++ b/lib/src/provider/models/models.dart @@ -4,4 +4,4 @@ export 'mempool/mempol_models.dart'; export 'config.dart'; export 'utxo_details.dart'; export 'multisig_script.dart'; -export 'electrum/electrum_utxo.dart'; +export 'electrum/models.dart'; diff --git a/lib/src/provider/providers/explorer.dart b/lib/src/provider/providers/explorer.dart index 86db532..94c1917 100644 --- a/lib/src/provider/providers/explorer.dart +++ b/lib/src/provider/providers/explorer.dart @@ -4,6 +4,7 @@ import 'package:bitcoin_base/src/provider/models/models.dart'; import 'package:bitcoin_base/src/provider/services/explorer.dart'; import 'package:bitcoin_base/src/models/network.dart'; import 'package:blockchain_utils/utils/binary/utils.dart'; +import 'package:blockchain_utils/utils/numbers/utils/bigint_utils.dart'; import 'package:blockchain_utils/utils/string/string.dart'; class ApiProvider { @@ -138,8 +139,8 @@ class ApiProvider { } } - Future getBlockHeight(int height) async { - final url = api.getBlockHeight(height); + Future getBlockHashByHeight(int height) async { + final url = api.getBlockHashByHeight(height); final response = await _getRequest(url); switch (api.apiType) { case APIType.mempool: @@ -150,8 +151,21 @@ class ApiProvider { } } + Future getLatestBlockHeight() async { + final url = api.getLatestBlockHeightUrl(); + final response = await _getRequest(url); + switch (api.apiType) { + case APIType.mempool: + return BigintUtils.parse(response); + default: + final toJson = StringUtils.toJson>(response); + final chainInfo = BlockCypherChainInfo.fromJson(toJson); + return chainInfo.height; + } + } + Future genesis() async { - return getBlockHeight(0); + return getBlockHashByHeight(0); } Future getRawTransaction(String transactionId, diff --git a/pubspec.yaml b/pubspec.yaml index f48dc20..f612055 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: bitcoin_base description: A versatile library for Bitcoin, Dogecoin, Litecoin, Dash, BSV, and BCH. Supports P2PKH, P2SH, P2WPKH, P2WSH, P2TR, with advanced creation, signing, and spending capabilities. -version: 6.4.0 +version: 6.5.0 homepage: "https://github.com/mrtnetwork/bitcoin_base" repository: "https://github.com/mrtnetwork/bitcoin_base" Author: mrhaydari.t@gmail.com From db6c26fc47d1012f5bc1f0693670463f9bf37270 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 29 May 2025 04:13:06 +0300 Subject: [PATCH 08/12] rename package to old --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index a4d6530..87cf21c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,4 +1,4 @@ -name: bitcoin_base +name: bitcoin_base_old description: A versatile library for Bitcoin, Dogecoin, Litecoin, Dash, BSV, and BCH. Supports P2PKH, P2SH, P2WPKH, P2WSH, P2TR, with advanced creation, signing, and spending capabilities. version: 4.7.0 homepage: "https://github.com/mrtnetwork/bitcoin_base" From bb4318511312a454fd91bf49042e25ecc855e4ac Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 29 May 2025 04:50:42 +0300 Subject: [PATCH 09/12] Revert "rename package to old" This reverts commit db6c26fc47d1012f5bc1f0693670463f9bf37270. --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 87cf21c..a4d6530 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,4 +1,4 @@ -name: bitcoin_base_old +name: bitcoin_base description: A versatile library for Bitcoin, Dogecoin, Litecoin, Dash, BSV, and BCH. Supports P2PKH, P2SH, P2WPKH, P2WSH, P2TR, with advanced creation, signing, and spending capabilities. version: 4.7.0 homepage: "https://github.com/mrtnetwork/bitcoin_base" From 01d00bbbd1770ac5ca15a246f4cd26bd88f46ae4 Mon Sep 17 00:00:00 2001 From: Mohsen <56779182+mrtnetwork@users.noreply.github.com> Date: Sun, 1 Jun 2025 14:25:07 +0330 Subject: [PATCH 10/12] clean unused import --- analysis_options.yaml | 81 +++++++------------ example/pubspec.lock | 18 ++--- lib/src/bitcoin_cash/bcmr.dart | 4 +- .../methods/get_verbose_transaction.dart | 2 +- .../methods/headers_subscribe.dart | 1 - .../block_cypher/block_cypher_models.dart | 1 - lib/src/transaction_builder/core.dart | 3 +- 7 files changed, 42 insertions(+), 68 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 3c029d1..1c26b14 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,56 +1,29 @@ -include: package:lints/recommended.yaml -# include: package:flutter_lints/flutter.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options -# Uncomment the following section to specify additional rules. +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. rules: - # - always_declare_return_types - # - annotate_overrides - # - avoid_init_to_null - # - avoid_null_checks_in_equality_operators - # - avoid_relative_lib_imports - # - avoid_return_types_on_setters - # - avoid_shadowing_type_parameters - # - avoid_single_cascade_in_expression_statements - # - avoid_types_as_parameter_names - # - await_only_futures - # - camel_case_extensions - # - curly_braces_in_flow_control_structures - # - empty_catches - # - empty_constructor_bodies - # - library_names - # - library_prefixes - # - no_duplicate_case_values - # - null_closures - # - omit_local_variable_types - # - prefer_adjacent_string_concatenation - # - prefer_collection_literals - # - prefer_conditional_assignment - # - prefer_contains - # - prefer_equal_for_default_values - # - prefer_final_fields - # - prefer_for_elements_to_map_fromIterable - # - prefer_generic_function_type_aliases - # - prefer_if_null_operators - # - prefer_inlined_adds - # - prefer_is_empty - # - prefer_is_not_empty - # - prefer_iterable_whereType - # - prefer_single_quotes - # - prefer_spread_collections - # - recursive_getters - # - slash_for_doc_comments - # - sort_child_properties_last - # - type_init_formals - # - unawaited_futures - # - unnecessary_brace_in_string_interps - # - unnecessary_const - # - unnecessary_getters_setters - # - unnecessary_new - # - unnecessary_null_in_if_null_operators - # - unnecessary_this - # - unrelated_type_equality_checks - # - use_function_type_syntax_for_parameters - # - use_rethrow_when_possible - # - valid_regexps \ No newline at end of file + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options + diff --git a/example/pubspec.lock b/example/pubspec.lock index 0de348c..5468af3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,17 +5,17 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" bitcoin_base: dependency: "direct main" description: path: ".." relative: true source: path - version: "6.3.0" + version: "6.5.0" blockchain_utils: dependency: "direct main" description: @@ -68,10 +68,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" flutter: dependency: "direct main" description: flutter @@ -110,10 +110,10 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: @@ -243,10 +243,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" web: dependency: transitive description: diff --git a/lib/src/bitcoin_cash/bcmr.dart b/lib/src/bitcoin_cash/bcmr.dart index 3998290..9899000 100644 --- a/lib/src/bitcoin_cash/bcmr.dart +++ b/lib/src/bitcoin_cash/bcmr.dart @@ -1,4 +1,6 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; +import 'package:bitcoin_base/src/cash_token/cash_token.dart'; +import 'package:bitcoin_base/src/provider/models/utxo_details.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; /// Bitcoin Cash Metadata Registries Script to convert uris and content hash to bitcoin output script diff --git a/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart b/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart index 230533f..8135358 100644 --- a/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart +++ b/lib/src/provider/electrum_methods/methods/get_verbose_transaction.dart @@ -1,6 +1,6 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/src/provider/core/methods.dart'; import 'package:bitcoin_base/src/provider/core/params.dart'; +import 'package:bitcoin_base/src/provider/models/electrum/models.dart'; /// Return a raw transaction. /// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html diff --git a/lib/src/provider/electrum_methods/methods/headers_subscribe.dart b/lib/src/provider/electrum_methods/methods/headers_subscribe.dart index 352cff5..a101f07 100644 --- a/lib/src/provider/electrum_methods/methods/headers_subscribe.dart +++ b/lib/src/provider/electrum_methods/methods/headers_subscribe.dart @@ -6,7 +6,6 @@ import 'package:bitcoin_base/src/provider/models/electrum/models.dart'; /// https://electrumx-spesmilo.readthedocs.io/en/latest/protocol-methods.html class ElectrumRequestHeaderSubscribe extends ElectrumRequest< ElectrumHeaderSubscribeResponse, Map> { - /// blockchain.headers.subscribe @override String get method => ElectrumRequestMethods.headersSubscribe.method; diff --git a/lib/src/provider/models/block_cypher/block_cypher_models.dart b/lib/src/provider/models/block_cypher/block_cypher_models.dart index e894534..a237811 100644 --- a/lib/src/provider/models/block_cypher/block_cypher_models.dart +++ b/lib/src/provider/models/block_cypher/block_cypher_models.dart @@ -1,7 +1,6 @@ import 'package:bitcoin_base/src/bitcoin/address/address.dart'; import 'package:bitcoin_base/src/provider/models/utxo_details.dart'; import 'package:blockchain_utils/utils/numbers/numbers.dart'; -import 'package:blockchain_utils/utils/numbers/utils/bigint_utils.dart'; class TxRef implements UTXO { final String txHash; diff --git a/lib/src/transaction_builder/core.dart b/lib/src/transaction_builder/core.dart index 0606936..3511146 100644 --- a/lib/src/transaction_builder/core.dart +++ b/lib/src/transaction_builder/core.dart @@ -1,4 +1,5 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; +import 'package:bitcoin_base/src/provider/models/utxo_details.dart'; typedef BitcoinSignerCallBack = String Function( List trDigest, UtxoWithAddress utxo, String publicKey, int sighash); From 0a028b940955d84e29a2503aea35317596bfa4bc Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 25 Jun 2025 08:14:19 +0300 Subject: [PATCH 11/12] Merge latest main (v6.5.0) and fix a lot of errors --- .../lib/bitcoin_cash/burn_token_example.dart | 4 +- .../create_cash_token_example.dart | 4 +- .../lib/bitcoin_cash/create_nft_example.dart | 4 +- .../lib/bitcoin_cash/make_vout0_example.dart | 4 +- .../lib/bitcoin_cash/minting_nft_example.dart | 2 +- .../old_examples/old_example.dart | 12 +- .../bitcoin_cash/p2sh32_spend_example.dart | 4 +- .../bitcoin_cash/send_ft_token_example.dart | 4 +- .../bitcoin_cash/transfer_bch_example.dart | 4 +- example/lib/global/bch_example.dart | 4 +- .../global/old_examples/bitcoin_example.dart | 20 +-- .../old_examples/dash_example/dash.dart | 8 +- .../doge_example/doge_example.dart | 8 +- .../litecoin_example/litecoin_example.dart | 16 +-- .../spending_single_type.dart | 26 ++-- .../multi_sig_transactions.dart | 8 +- .../transaction_builder_example.dart | 8 +- example/lib/main.dart | 12 +- example/pubspec.lock | 130 +++++++++++++----- lib/src/bitcoin/address/address.dart | 1 + lib/src/bitcoin/address/core.dart | 10 +- lib/src/bitcoin/address/legacy_address.dart | 10 +- lib/src/bitcoin/address/segwit_address.dart | 44 +++--- lib/src/bitcoin/address/util.dart | 2 +- lib/src/bitcoin/script/script.dart | 9 +- lib/src/bitcoin/script/transaction.dart | 30 ++-- lib/src/bitcoin/script/witness.dart | 2 + lib/src/bitcoin/silent_payments/address.dart | 1 - lib/src/bitcoin/silent_payments/utils.dart | 8 +- lib/src/crypto/keypair/ec_private.dart | 4 +- lib/src/crypto/keypair/ec_public.dart | 14 +- lib/src/models/network.dart | 2 +- lib/src/provider/models/multisig_script.dart | 6 +- lib/src/provider/models/utxo_details.dart | 3 - lib/src/provider/providers/explorer.dart | 2 +- .../transaction_builder.dart | 17 +-- lib/src/utils/script.dart | 6 +- pubspec.yaml | 3 + test/p2sh_test.dart | 4 +- test/p2wpkh_test.dart | 4 +- test/p2wsh_test.dart | 6 +- test/silent_payments.dart | 8 +- 42 files changed, 277 insertions(+), 201 deletions(-) diff --git a/example/lib/bitcoin_cash/burn_token_example.dart b/example/lib/bitcoin_cash/burn_token_example.dart index a4a53b7..33c530a 100644 --- a/example/lib/bitcoin_cash/burn_token_example.dart +++ b/example/lib/bitcoin_cash/burn_token_example.dart @@ -29,7 +29,7 @@ void main() async { /// p2pkh with token address () final receiver1 = P2pkhAddress.fromHash160( - h160: publicKey.toP2pkhAddress().addressProgram, + addrHash: publicKey.toAddress().addressProgram, type: P2pkhAddressType.p2pkhwt); /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. @@ -80,7 +80,7 @@ void main() async { previousValue + element.utxo.token!.amount); final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ /// change address for bch values (sum of bch amout - (outputs amount + fee)) BitcoinOutput( address: p2pkhAddress.baseAddress, diff --git a/example/lib/bitcoin_cash/create_cash_token_example.dart b/example/lib/bitcoin_cash/create_cash_token_example.dart index 87f89c5..a93e635 100644 --- a/example/lib/bitcoin_cash/create_cash_token_example.dart +++ b/example/lib/bitcoin_cash/create_cash_token_example.dart @@ -25,7 +25,7 @@ void main() async { /// Derives a P2PKH address from the given public key and converts it to a Bitcoin Cash address /// for enhanced accessibility within the network. - final p2pkhAddress = publicKey.toP2pkhAddress(); + final p2pkhAddress = publicKey.toAddress(); /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. /// We does not need tokens utxo and we set to false. @@ -65,7 +65,7 @@ void main() async { return; } final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ BitcoinTokenOutput( address: p2pkhAddress, diff --git a/example/lib/bitcoin_cash/create_nft_example.dart b/example/lib/bitcoin_cash/create_nft_example.dart index 982e01a..42e3b5a 100644 --- a/example/lib/bitcoin_cash/create_nft_example.dart +++ b/example/lib/bitcoin_cash/create_nft_example.dart @@ -26,7 +26,7 @@ void main() async { /// Derives a P2PKH address from the given public key and converts it to a Bitcoin Cash address /// for enhanced accessibility within the network. final p2pkhAddress = - BitcoinCashAddress.fromBaseAddress(publicKey.toP2pkhAddress()); + BitcoinCashAddress.fromBaseAddress(publicKey.toAddress()); /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. /// We does not need tokens utxo and we set to false. @@ -66,7 +66,7 @@ void main() async { return; } final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ BitcoinOutput( address: p2pkhAddress.baseAddress, value: sumOfUtxo - diff --git a/example/lib/bitcoin_cash/make_vout0_example.dart b/example/lib/bitcoin_cash/make_vout0_example.dart index 8031e39..e48c169 100644 --- a/example/lib/bitcoin_cash/make_vout0_example.dart +++ b/example/lib/bitcoin_cash/make_vout0_example.dart @@ -26,7 +26,7 @@ void main() async { /// Derives a P2PKH address from the given public key and converts it to a Bitcoin Cash address /// for enhanced accessibility within the network. - final p2pkhAddress = publicKey.toP2pkhAddress(); + final p2pkhAddress = publicKey.toAddress(); /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. /// We does not need tokens utxo and we set to false. @@ -48,7 +48,7 @@ void main() async { final sumOfUtxo = utxos.sumOfUtxosValue(); final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ BitcoinOutput( address: p2pkhAddress, value: sumOfUtxo - BtcUtils.toSatoshi("0.00003"), diff --git a/example/lib/bitcoin_cash/minting_nft_example.dart b/example/lib/bitcoin_cash/minting_nft_example.dart index 5a1ca29..eb7d6fc 100644 --- a/example/lib/bitcoin_cash/minting_nft_example.dart +++ b/example/lib/bitcoin_cash/minting_nft_example.dart @@ -55,7 +55,7 @@ void main() async { "3f0d87791e5996aaddbce16c12651dd8b5b881cf7338340504bb7b2c6c08bfc4"; final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ BitcoinOutput( address: p2pkhAddress.baseAddress, value: sumOfUtxo - diff --git a/example/lib/bitcoin_cash/old_examples/old_example.dart b/example/lib/bitcoin_cash/old_examples/old_example.dart index 99dbce5..ce71cea 100644 --- a/example/lib/bitcoin_cash/old_examples/old_example.dart +++ b/example/lib/bitcoin_cash/old_examples/old_example.dart @@ -101,7 +101,7 @@ void _spendFrom2P2SHAnd2P2PKHAddress() async { ]); final b = ForkedTransactionBuilder( - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2shAddress and a value of 0.01 BCH BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("0.01")), @@ -138,13 +138,13 @@ void _spendFrom2P2SHAnd2P2PKHAddress() async { vout: 0, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey2.toP2pkhAddress().type, + scriptType: examplePublicKey2.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey2.toHex(), - address: examplePublicKey2.toP2pkhAddress())), + address: examplePublicKey2.toAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the ForkedTransactionBuilder @@ -247,7 +247,7 @@ void _spendFrom2P2SHAnd1P2PKHAddress() async { final b = ForkedTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2pkhAddress and a value of 0.01 BCH BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("0.01")), @@ -324,13 +324,13 @@ void _spendFrom2P2SHAnd1P2PKHAddress() async { vout: 2, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey.toP2pkhAddress().type, + scriptType: examplePublicKey.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey.toHex(), - address: examplePublicKey.toP2pkhAddress())), + address: examplePublicKey.toAddress())), UtxoWithAddress( utxo: BitcoinUtxo( /// Transaction hash uniquely identifies the referenced transaction diff --git a/example/lib/bitcoin_cash/p2sh32_spend_example.dart b/example/lib/bitcoin_cash/p2sh32_spend_example.dart index 6a2697a..93cfc27 100644 --- a/example/lib/bitcoin_cash/p2sh32_spend_example.dart +++ b/example/lib/bitcoin_cash/p2sh32_spend_example.dart @@ -27,7 +27,7 @@ void main() async { /// Derives a P2PKH address from the given public key and converts it to a Bitcoin Cash address /// for enhanced accessibility within the network. final p2pkhAddress = - BitcoinCashAddress.fromBaseAddress(publicKey.toP2pkhAddress()); + BitcoinCashAddress.fromBaseAddress(publicKey.toAddress()); /// Initialize two P2SH32 addresses for receiving funds. /// bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7 @@ -79,7 +79,7 @@ void main() async { return; } final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ BitcoinOutput( address: p2pkhAddress.baseAddress, value: BtcUtils.toSatoshi("0.00001"), diff --git a/example/lib/bitcoin_cash/send_ft_token_example.dart b/example/lib/bitcoin_cash/send_ft_token_example.dart index 97dff3b..a3acb9a 100644 --- a/example/lib/bitcoin_cash/send_ft_token_example.dart +++ b/example/lib/bitcoin_cash/send_ft_token_example.dart @@ -30,7 +30,7 @@ void main() async { /// p2pkh with token address () final receiver1 = P2pkhAddress.fromHash160( - h160: publicKey.toP2pkhAddress().addressProgram, + addrHash: publicKey.toAddress().addressProgram, type: P2pkhAddressType.p2pkhwt); /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. @@ -81,7 +81,7 @@ void main() async { previousValue + element.utxo.token!.amount); final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ /// change address for bch values (sum of bch amout - (outputs amount + fee)) BitcoinOutput( address: p2pkhAddress.baseAddress, diff --git a/example/lib/bitcoin_cash/transfer_bch_example.dart b/example/lib/bitcoin_cash/transfer_bch_example.dart index 93ea1c6..5df0ab5 100644 --- a/example/lib/bitcoin_cash/transfer_bch_example.dart +++ b/example/lib/bitcoin_cash/transfer_bch_example.dart @@ -27,7 +27,7 @@ void main() async { /// Derives a P2PKH address from the given public key and converts it to a Bitcoin Cash address /// for enhanced accessibility within the network. final p2pkhAddress = - BitcoinCashAddress.fromBaseAddress(publicKey.toP2pkhAddress()); + BitcoinCashAddress.fromBaseAddress(publicKey.toAddress()); /// Initialize two P2SH32 addresses for receiving funds. final p2sh32Example1 = BitcoinCashAddress( @@ -60,7 +60,7 @@ void main() async { } final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ /// change input (sumofutxos - spend) BitcoinOutput( address: p2pkhAddress.baseAddress, diff --git a/example/lib/global/bch_example.dart b/example/lib/global/bch_example.dart index c2f9768..7ab8975 100644 --- a/example/lib/global/bch_example.dart +++ b/example/lib/global/bch_example.dart @@ -24,7 +24,7 @@ void main() async { /// Derives a P2PKH address from the given public key and converts it to a Bitcoin Cash address /// for enhanced accessibility within the network. final p2pkhAddress = - BitcoinCashAddress.fromBaseAddress(publicKey.toP2pkhAddress()); + BitcoinCashAddress.fromBaseAddress(publicKey.toAddress()); /// Initialize two P2SH32 addresses for receiving funds. final p2sh32Example1 = BitcoinCashAddress( @@ -57,7 +57,7 @@ void main() async { } final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ /// change input (sumofutxos - spend) BitcoinOutput( address: p2pkhAddress.baseAddress, diff --git a/example/lib/global/old_examples/bitcoin_example.dart b/example/lib/global/old_examples/bitcoin_example.dart index bdcbfb8..2fe0924 100644 --- a/example/lib/global/old_examples/bitcoin_example.dart +++ b/example/lib/global/old_examples/bitcoin_example.dart @@ -97,7 +97,7 @@ void _spendFromP2pkhTo10DifferentType() async { final builder = BitcoinTransactionBuilder( /// outputs and values - outputs: [ + outPuts: [ BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("0.001")), BitcoinOutput(address: out2, value: BtcUtils.toSatoshi("0.001")), BitcoinOutput(address: out3, value: BtcUtils.toSatoshi("0.001")), @@ -140,24 +140,24 @@ void _spendFromP2pkhTo10DifferentType() async { vout: 3, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey2.toP2pkhAddress().type, + scriptType: examplePublicKey2.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey2.toHex(), - address: examplePublicKey2.toP2pkhAddress())), + address: examplePublicKey2.toAddress())), UtxoWithAddress( utxo: BitcoinUtxo( txHash: "6ff0bdb2966f62f5e202c924e1cab1368b0258833e48986cc0a70fbca624ba93", value: BigInt.from(812830), vout: 0, - scriptType: examplePublicKey2.toP2pkhAddress().type, + scriptType: examplePublicKey2.toAddress().type, ), ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey2.toHex(), - address: examplePublicKey2.toP2pkhAddress())), + address: examplePublicKey2.toAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the BitcoinTransactionBuilder @@ -255,7 +255,7 @@ void _spendFrom10DifferentTypeToP2pkh() async { final builder = BitcoinTransactionBuilder( /// outputs - outputs: [BitcoinOutput(address: out1, value: change)], + outPuts: [BitcoinOutput(address: out1, value: change)], /// Set the transaction fee fee: BtcUtils.toSatoshi("0.00005"), @@ -282,13 +282,13 @@ void _spendFrom10DifferentTypeToP2pkh() async { vout: 0, /// Script type indicates the type of script associated with the UTXO's address - scriptType: childKey1PublicKey.toP2pkhAddress().type, + scriptType: childKey1PublicKey.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: childKey1PublicKey.toHex(), - address: childKey1PublicKey.toP2pkhAddress())), + address: childKey1PublicKey.toAddress())), UtxoWithAddress( utxo: BitcoinUtxo( txHash: @@ -306,11 +306,11 @@ void _spendFrom10DifferentTypeToP2pkh() async { "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 2, - scriptType: childKey1PublicKey.toP2wpkhAddress().type, + scriptType: childKey1PublicKey.toSegwitAddress().type, ), ownerDetails: UtxoAddressDetails( publicKey: childKey1PublicKey.toHex(), - address: childKey1PublicKey.toP2wpkhAddress())), + address: childKey1PublicKey.toSegwitAddress())), UtxoWithAddress( utxo: BitcoinUtxo( txHash: diff --git a/example/lib/global/old_examples/dash_example/dash.dart b/example/lib/global/old_examples/dash_example/dash.dart index 236a015..d6a8880 100644 --- a/example/lib/global/old_examples/dash_example/dash.dart +++ b/example/lib/global/old_examples/dash_example/dash.dart @@ -78,7 +78,7 @@ void _spendFromTwoP2shAndOneP2PKH() async { final b = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2shAddress and a value of 1.0 DASH BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("1")), @@ -112,13 +112,13 @@ void _spendFromTwoP2shAndOneP2PKH() async { vout: 2, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey2.toP2pkhAddress().type, + scriptType: examplePublicKey2.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey2.toHex(), - address: examplePublicKey2.toP2pkhAddress())), + address: examplePublicKey2.toAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the BitcoinTransactionBuilder @@ -184,7 +184,7 @@ void _spendP2SH() async { final b = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ BitcoinOutput(address: out1, value: change), ], diff --git a/example/lib/global/old_examples/doge_example/doge_example.dart b/example/lib/global/old_examples/doge_example/doge_example.dart index 814e857..6eab54b 100644 --- a/example/lib/global/old_examples/doge_example/doge_example.dart +++ b/example/lib/global/old_examples/doge_example/doge_example.dart @@ -85,7 +85,7 @@ void _spendFrom3P2shAddress() async { final builder = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2shAddress and a value of 1 DOGE BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("1")), @@ -119,13 +119,13 @@ void _spendFrom3P2shAddress() async { vout: 1, /// Script type indicates the type of script associated with the UTXO's address - scriptType: childKey1PublicKey.toP2pkhAddress().type, + scriptType: childKey1PublicKey.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: childKey1PublicKey.toHex(), - address: childKey1PublicKey.toP2pkhAddress())), + address: childKey1PublicKey.toAddress())), ]); final tr = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { if (publicKey == childKey1PublicKey.toHex()) { @@ -213,7 +213,7 @@ void _spendFromP2pkhAndP2sh() async { final b = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2pkhAddress and a value of 1.0 DOGE BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("1")), diff --git a/example/lib/global/old_examples/litecoin_example/litecoin_example.dart b/example/lib/global/old_examples/litecoin_example/litecoin_example.dart index 3fd6a27..eb55185 100644 --- a/example/lib/global/old_examples/litecoin_example/litecoin_example.dart +++ b/example/lib/global/old_examples/litecoin_example/litecoin_example.dart @@ -78,7 +78,7 @@ void _spendLTCP2pkhAddress() async { final builder = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the first P2shAddress and a value of 0.0001 LTC BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("0.0001")), @@ -115,12 +115,12 @@ void _spendLTCP2pkhAddress() async { vout: 3, /// Script type indicates the type of script associated with the UTXO's address - scriptType: pub.toP2pkhAddress().type, + scriptType: pub.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( - publicKey: pub.toHex(), address: pub.toP2pkhAddress())), + publicKey: pub.toHex(), address: pub.toAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the BitcoinTransactionBuilder @@ -200,7 +200,7 @@ void _spendFrom2P2shAddressAndOneMultiSigP2shAddress() async { final builder = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the third P2shAddress and a value equal to the 'change' variable BitcoinOutput(address: out1, value: change), ], @@ -338,7 +338,7 @@ void _spendFromNestedSegwitP2WPKHInP2SH() async { final builder = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the third P2wpkhAddress and a value equal to the 'change' variable BitcoinOutput(address: out1, value: change), ], @@ -456,7 +456,7 @@ void _spendFromSegwitP2WPKHAddress() async { final builder = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the third P2pkhAddress and a value equal to the 'change' variable BitcoinOutput(address: input1, value: change), ], @@ -487,13 +487,13 @@ void _spendFromSegwitP2WPKHAddress() async { vout: 0, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey.toP2wpkhAddress().type, + scriptType: examplePublicKey.toSegwitAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey.toHex(), - address: examplePublicKey.toP2wpkhAddress())), + address: examplePublicKey.toSegwitAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the BitcoinTransactionBuilder instance (builder) diff --git a/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart b/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart index f4663e5..9e51c71 100644 --- a/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart +++ b/example/lib/global/old_examples/spending_with_scripts/spending_single_type.dart @@ -26,7 +26,7 @@ Future spendingP2WPKH(ECPrivate sWallet, ECPrivate rWallet) async { // In this section, you can add any number of addresses with type P2PWPH to this transaction. final publicKey = sWallet.getPublic(); // P2WPKH - final sender = publicKey.toP2wpkhAddress(); + final sender = publicKey.toSegwitAddress(); // Read UTXOs of accounts from the BlockCypher API. final utxo = await api.getAccountUtxo( UtxoAddressDetails(address: sender, publicKey: publicKey.toHex())); @@ -44,7 +44,7 @@ Future spendingP2WPKH(ECPrivate sWallet, ECPrivate rWallet) async { final prive = sWallet; final recPub = rWallet.getPublic(); // P2WPKH - final receiver = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); // P2TR final changeAddress = recPub.toTaprootAddress(); @@ -124,9 +124,9 @@ Future spendingP2WSH(ECPrivate sWallet, ECPrivate rWallet) async { final prive = sWallet; final recPub = rWallet.getPublic(); - final receiver = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); - final changeAddress = recPub.toP2wpkhAddress(); + final changeAddress = recPub.toSegwitAddress(); final List outputsAdress = [ BitcoinOutput(address: receiver, value: BigInt.zero), BitcoinOutput(address: changeAddress, value: BigInt.zero) @@ -160,7 +160,7 @@ Future spendingP2PKH(ECPrivate sWallet, ECPrivate rWallet) async { // and we use method `buildP2pkhTransaction` to create the transaction. final addr = sWallet.getPublic(); // P2PKH - final sender = addr.toP2pkhAddress(); + final sender = addr.toAddress(); final utxo = await api.getAccountUtxo( UtxoAddressDetails(address: sender, publicKey: addr.toHex())); final sumOfUtxo = utxo.sumOfUtxosValue(); @@ -173,8 +173,8 @@ Future spendingP2PKH(ECPrivate sWallet, ECPrivate rWallet) async { final prive = sWallet; final recPub = rWallet.getPublic(); - final receiver = recPub.toP2wpkhAddress(); - final changeAddress = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); + final changeAddress = recPub.toSegwitAddress(); final List outputsAdress = [ BitcoinOutput(address: receiver, value: BigInt.zero), BitcoinOutput(address: changeAddress, value: BigInt.zero) @@ -225,8 +225,8 @@ Future spendingP2SHNoneSegwit( final prive = sWallet; final recPub = rWallet.getPublic(); - final receiver = recPub.toP2wpkhAddress(); - final changeAddress = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); + final changeAddress = recPub.toSegwitAddress(); final List outputsAdress = [ BitcoinOutput(address: receiver, value: BigInt.zero), BitcoinOutput(address: changeAddress, value: BigInt.zero) @@ -275,9 +275,9 @@ Future spendingP2shSegwit(ECPrivate sWallet, ECPrivate rWallet) async { final prive = sWallet; final recPub = rWallet.getPublic(); - final receiver = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); - final changeAddress = recPub.toP2wpkhAddress(); + final changeAddress = recPub.toSegwitAddress(); final List outputsAdress = [ BitcoinOutput(address: receiver, value: BigInt.zero), BitcoinOutput(address: changeAddress, value: BigInt.zero) @@ -327,8 +327,8 @@ Future spendingP2TR(ECPrivate sWallet, ECPrivate rWallet) async { final prive = sWallet; final recPub = rWallet.getPublic(); - final receiver = recPub.toP2wpkhAddress(); - final changeAddress = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); + final changeAddress = recPub.toSegwitAddress(); final List outputsAdress = [ BitcoinOutput(address: receiver, value: BigInt.zero), BitcoinOutput(address: changeAddress, value: BigInt.zero) diff --git a/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart b/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart index a3c4d45..2320f5d 100644 --- a/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart +++ b/example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart @@ -60,7 +60,7 @@ void main() async { // tb1qxt3c7849m0m6cv3z3s35c3zvdna3my3yz0r609qd9g0dcyyk580sgyldhe final p2wshMultiSigAddress = - multiSignatureAddress.toP2wshAddress(network: network).toP2pkhAddress(network); + multiSignatureAddress.toP2wshAddress(network: network).toAddress(network); // p2sh(p2wsh) multisig final signerP2sh1 = @@ -80,12 +80,12 @@ void main() async { // 2N8co8bth9CNKtnWGfHW6HuUNgnNPNdpsMj final p2shMultisigAddress = p2shMultiSignature .toP2wshInP2shAddress(network: network) - .toP2pkhAddress(network); + .toAddress(network); // P2TR final exampleAddr2 = public2.toTaprootAddress(); // P2KH - final exampleAddr4 = public3.toP2pkhAddress(); + final exampleAddr4 = public3.toAddress(); // Spending List // i use some different address type for this // now i want to spending from 8 address in one transaction @@ -203,7 +203,7 @@ void main() async { // Now, we provide the UTXOs we want to spend. utxos: utxos, // We select transaction outputs - outputs: [output1, output2, output3, output4], + outPuts: [output1, output2, output3, output4], /* Transaction fee Ensure that you have accurately calculated the amounts. diff --git a/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart b/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart index 614685f..49fd392 100644 --- a/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart +++ b/example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart @@ -37,7 +37,7 @@ void main() async { final public4 = private4.getPublic(); // P2PKH ADDRESS - final exampleAddr1 = public1.toP2pkhAddress(); + final exampleAddr1 = public1.toAddress(); // P2TR final exampleAddr2 = public2.toTaprootAddress(); @@ -45,7 +45,7 @@ void main() async { // P2PKHINP2SH final exampleAddr3 = public2.toP2pkhInP2sh(); // P2KH - final exampleAddr4 = public3.toP2pkhAddress(); + final exampleAddr4 = public3.toAddress(); // P2PKHINP2SH final exampleAddr5 = public3.toP2pkhInP2sh(); // P2WSHINP2SH 1-1 multisig @@ -55,7 +55,7 @@ void main() async { // P2PKINP2SH final exampleAddr8 = public4.toP2pkInP2sh(); // P2WPKH - final exampleAddr9 = public3.toP2wpkhAddress(); + final exampleAddr9 = public3.toSegwitAddress(); // P2WSH 1-1 multisig final exampleAddr10 = public3.toP2wshAddress(); @@ -143,7 +143,7 @@ void main() async { // Now, we provide the UTXOs we want to spend. utxos: utxos, // We select transaction outputs - outputs: [ + outPuts: [ output1, output2, output3, diff --git a/example/lib/main.dart b/example/lib/main.dart index fc0ba8f..6a41b96 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -101,7 +101,7 @@ void _spendFrom2P2SHAnd2P2PKHAddress() async { ]); final b = ForkedTransactionBuilder( - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2shAddress and a value of 0.01 BCH BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("0.01")), @@ -138,13 +138,13 @@ void _spendFrom2P2SHAnd2P2PKHAddress() async { vout: 0, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey2.toP2pkhAddress().type, + scriptType: examplePublicKey2.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey2.toHex(), - address: examplePublicKey2.toP2pkhAddress())), + address: examplePublicKey2.toAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the ForkedTransactionBuilder @@ -247,7 +247,7 @@ void _spendFrom2P2SHAnd1P2PKHAddress() async { final b = ForkedTransactionBuilder( /// outputs - outputs: [ + outPuts: [ /// Define a BitcoinOutput with the P2pkhAddress and a value of 0.01 BCH BitcoinOutput(address: out1, value: BtcUtils.toSatoshi("0.01")), @@ -324,13 +324,13 @@ void _spendFrom2P2SHAnd1P2PKHAddress() async { vout: 2, /// Script type indicates the type of script associated with the UTXO's address - scriptType: examplePublicKey.toP2pkhAddress().type, + scriptType: examplePublicKey.toAddress().type, ), /// Include owner details with the public key and address associated with the UTXO ownerDetails: UtxoAddressDetails( publicKey: examplePublicKey.toHex(), - address: examplePublicKey.toP2pkhAddress())), + address: examplePublicKey.toAddress())), UtxoWithAddress( utxo: BitcoinUtxo( /// Transaction hash uniquely identifies the referenced transaction diff --git a/example/pubspec.lock b/example/pubspec.lock index 5468af3..077b5e5 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,18 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" + bip32: + dependency: transitive + description: + name: bip32 + sha256: "54787cd7a111e9d37394aabbf53d1fc5e2e0e0af2cd01c459147a97c0e3f8a97" + url: "https://pub.dev" + source: hosted + version: "2.0.0" bitcoin_base: dependency: "direct main" description: @@ -28,34 +36,58 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" + bs58check: + dependency: transitive + description: + name: bs58check + sha256: c4a164d42b25c2f6bc88a8beccb9fc7d01440f3c60ba23663a20a70faf484ea9 + url: "https://pub.dev" + source: hosted + version: "1.0.2" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -68,10 +100,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -90,6 +122,14 @@ packages: description: flutter source: sdk version: "0.0.0" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" http: dependency: "direct main" description: @@ -106,22 +146,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -142,10 +190,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -158,18 +206,26 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "3.9.1" sky_engine: dependency: transitive description: flutter @@ -179,50 +235,50 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.12.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.3.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.3" typed_data: dependency: transitive description: @@ -243,10 +299,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.3.0" web: dependency: transitive description: @@ -256,5 +312,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/bitcoin/address/address.dart b/lib/src/bitcoin/address/address.dart index c9cf5a6..e9c79d8 100644 --- a/lib/src/bitcoin/address/address.dart +++ b/lib/src/bitcoin/address/address.dart @@ -9,6 +9,7 @@ library; import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; +import 'package:bitcoin_base/src/bitcoin/silent_payments/silent_payments.dart'; import 'package:bitcoin_base/src/bitcoin/taproot/taproot.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:bitcoin_base/src/models/network.dart'; diff --git a/lib/src/bitcoin/address/core.dart b/lib/src/bitcoin/address/core.dart index 78bd508..89c4d78 100644 --- a/lib/src/bitcoin/address/core.dart +++ b/lib/src/bitcoin/address/core.dart @@ -25,10 +25,10 @@ abstract class BitcoinAddressType implements Enumerate { // } else if (address is SilentPaymentsAddresType) { // return SilentPaymentsAddresType.p2sp; // } else if (address is P2wpkhAddress) { - // return SegwitAddresType.p2wpkh; + // return SegwitAddressType.p2wpkh; // } // - // throw BitcoinBasePluginException('Invalid BitcoinAddressType: $address'); + // throw DartBitcoinPluginException('Invalid BitcoinAddressType: $address'); // } /// Check if the address type is Pay-to-Script-Hash (P2SH). @@ -137,7 +137,7 @@ abstract class BitcoinBaseAddress { return P2wpkhAddress.fromAddress(address: address, network: network); } - throw BitcoinBasePluginException('Invalid BitcoinBaseAddress: $address'); + throw DartBitcoinPluginException('Invalid BitcoinBaseAddress: $address'); } } @@ -230,7 +230,7 @@ class SegwitAddressType extends BitcoinAddressType { static const SegwitAddressType p2wpkh = SegwitAddressType._('P2WPKH'); static const SegwitAddressType p2tr = SegwitAddressType._('P2TR'); static const SegwitAddressType p2wsh = SegwitAddressType._('P2WSH'); - static const SegwitAddressType mweb = SegwitAddresType._("MWEB"); + static const SegwitAddressType mweb = SegwitAddressType._("MWEB"); @override bool get isP2sh => false; @override @@ -244,7 +244,7 @@ class SegwitAddressType extends BitcoinAddressType { switch (this) { case SegwitAddressType.p2wpkh: return 20; - case SegwitAddresType.mweb: + case SegwitAddressType.mweb: return 66; default: return 32; diff --git a/lib/src/bitcoin/address/legacy_address.dart b/lib/src/bitcoin/address/legacy_address.dart index 7b86114..545583a 100644 --- a/lib/src/bitcoin/address/legacy_address.dart +++ b/lib/src/bitcoin/address/legacy_address.dart @@ -25,8 +25,8 @@ abstract class LegacyAddress implements BitcoinBaseAddress { late final String _addressProgram; - ECPublic? get pubkey => _pubkey; - String? get signature => _signature; + // ECPublic? get pubkey => _pubkey; + // String? get signature => _signature; @override String get addressProgram { @@ -156,9 +156,9 @@ class P2pkhAddress extends LegacyAddress { @override final P2pkhAddressType type; - Script toScriptSig() { - return Script(script: [_signature, _pubkey]); - } + // Script toScriptSig() { + // return Script(script: [_signature, _pubkey]); + // } } class P2pkAddress extends LegacyAddress { diff --git a/lib/src/bitcoin/address/segwit_address.dart b/lib/src/bitcoin/address/segwit_address.dart index 58037f1..da1ad2e 100644 --- a/lib/src/bitcoin/address/segwit_address.dart +++ b/lib/src/bitcoin/address/segwit_address.dart @@ -74,14 +74,14 @@ class P2wpkhAddress extends SegwitAddress { // P2wpkhAddress.fromRedeemScript({required super.script, super.network}) // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - // - // factory P2wpkhAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { - // if (script.getAddressType() != SegwitAddresType.p2wpkh) { - // throw ArgumentError("Invalid scriptPubKey"); - // } - // - // return P2wpkhAddress.fromProgram(program: script.findScriptParam(1), network: network); - // } + + factory P2wpkhAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { + if (script.getAddressType() != SegwitAddressType.p2wpkh) { + throw ArgumentError("Invalid scriptPubKey"); + } + + return P2wpkhAddress.fromProgram(program: script.findScriptParam(1)); + } /// returns the scriptPubKey of a P2WPKH witness script @override @@ -117,15 +117,15 @@ class P2trAddress extends SegwitAddress { segwitVersion: _BitcoinAddressUtils.segwitV1, addresType: SegwitAddressType.p2tr); - P2trAddress.fromRedeemScript({required super.script, super.network}) - : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV1); + // P2trAddress.fromRedeemScript({required super.script, super.network}) + // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV1); factory P2trAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { - if (script.getAddressType() != SegwitAddresType.p2tr) { + if (script.getAddressType() != SegwitAddressType.p2tr) { throw ArgumentError("Invalid scriptPubKey"); } - return P2trAddress.fromProgram(program: script.findScriptParam(1), network: network); + return P2trAddress.fromProgram(program: script.findScriptParam(1)); } /// returns the scriptPubKey of a P2TR witness script @@ -151,15 +151,15 @@ class P2wshAddress extends SegwitAddress { P2wshAddress.fromScript({required super.script}) : super.fromScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - P2wshAddress.fromRedeemScript({required super.script, super.network}) - : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); + // P2wshAddress.fromRedeemScript({required super.script, super.network}) + // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); factory P2wshAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { - if (script.getAddressType() != SegwitAddresType.p2wsh) { + if (script.getAddressType() != SegwitAddressType.p2wsh) { throw ArgumentError("Invalid scriptPubKey"); } - return P2wshAddress.fromProgram(program: script.findScriptParam(1), network: network); + return P2wshAddress.fromProgram(program: script.findScriptParam(1)); } /// Returns the scriptPubKey of a P2WPKH witness script @@ -202,13 +202,13 @@ class MwebAddress extends SegwitAddress { MwebAddress.fromProgram({required super.program}) : super.fromProgram( segwitVersion: _BitcoinAddressUtils.segwitV0, - addressType: SegwitAddresType.mweb, + addresType: SegwitAddressType.mweb, ); - MwebAddress.fromRedeemScript({required super.script}) - : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); + // MwebAddress.fromRedeemScript({required super.script}) + // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - factory MwebAddress.fromScriptPubkey({required Script script, type = SegwitAddresType.mweb}) { - if (script.getAddressType() != SegwitAddresType.mweb) { + factory MwebAddress.fromScriptPubkey({required Script script, type = SegwitAddressType.mweb}) { + if (script.getAddressType() != SegwitAddressType.mweb) { throw ArgumentError("Invalid scriptPubKey"); } return MwebAddress.fromProgram(program: BytesUtils.toHexString(script.script as List)); @@ -222,5 +222,5 @@ class MwebAddress extends SegwitAddress { /// returns the type of address @override - SegwitAddresType get type => SegwitAddressType.mweb; + SegwitAddressType get type => SegwitAddressType.mweb; } diff --git a/lib/src/bitcoin/address/util.dart b/lib/src/bitcoin/address/util.dart index 7ec257d..ad099e2 100644 --- a/lib/src/bitcoin/address/util.dart +++ b/lib/src/bitcoin/address/util.dart @@ -17,7 +17,7 @@ class BitcoinAddressUtils { {required String address, required BasedUtxoNetwork network}) { final addressType = RegexUtils.addressTypeFromStr(address, network); - if (addressType.type == SegwitAddresType.mweb) { + if (addressType.type == SegwitAddressType.mweb) { return BytesUtils.fromHexString( MwebAddress.fromAddress(address: address, network: network).addressProgram, ); diff --git a/lib/src/bitcoin/script/script.dart b/lib/src/bitcoin/script/script.dart index 2753eae..9b088fc 100644 --- a/lib/src/bitcoin/script/script.dart +++ b/lib/src/bitcoin/script/script.dart @@ -1,3 +1,4 @@ +import 'package:bitcoin_base/src/bitcoin/address/address.dart'; import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; @@ -141,7 +142,7 @@ class Script { script.length == 66 && (script[0] == 2 || script[0] == 3) && (script[33] == 2 || script[33] == 3)) { - return SegwitAddresType.mweb; + return SegwitAddressType.mweb; } final first = findScriptParam(0); @@ -154,15 +155,15 @@ class Script { final lockingScriptBytes = opPushData(sec); if (lockingScriptBytes.length == 21) { - return SegwitAddresType.p2wpkh; + return SegwitAddressType.p2wpkh; } else if (lockingScriptBytes.length == 33) { - return SegwitAddresType.p2wsh; + return SegwitAddressType.p2wsh; } } else if (first == "OP_1") { final lockingScriptBytes = opPushData(sec); if (lockingScriptBytes.length == 33) { - return SegwitAddresType.p2tr; + return SegwitAddressType.p2tr; } } diff --git a/lib/src/bitcoin/script/transaction.dart b/lib/src/bitcoin/script/transaction.dart index 3b3556a..31abbf9 100644 --- a/lib/src/bitcoin/script/transaction.dart +++ b/lib/src/bitcoin/script/transaction.dart @@ -6,6 +6,7 @@ import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:blockchain_utils/helper/helper.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; +import 'package:collection/collection.dart'; import 'input.dart'; import 'output.dart'; import 'script.dart'; @@ -39,7 +40,11 @@ class BtcTransaction { List outputs = const [], List witnesses = const [], List locktime = BitcoinOpCodeConst.defaultTxLocktime, - List version = BitcoinOpCodeConst.defaultTxVersion}) { + List version = BitcoinOpCodeConst.defaultTxVersion, + bool hasSegwit = false, + bool canReplaceByFee = false, + List? mwebBytes, + bool hasSilentPayment = false,}) { if (locktime.length != BitcoinOpCodeConst.locktimeLengthInBytes) { throw DartBitcoinPluginException( "Invalid locktime length: expected ${BitcoinOpCodeConst.locktimeLengthInBytes}, but got ${locktime.length}."); @@ -53,6 +58,10 @@ class BtcTransaction { outputs: outputs, witnesses: witnesses, version: version, + hasSegwit: hasSegwit, + canReplaceByFee: canReplaceByFee, + hasSilentPayment: hasSilentPayment, + mwebBytes: mwebBytes, locktime: locktime); } final List inputs; @@ -97,21 +106,22 @@ class BtcTransaction { version: tx.version); } + static BtcTransaction fromRaw(String raw) { + final txBytes = BytesUtils.fromHexString(raw); + return deserialize(txBytes); + } + /// Instantiates a Transaction from serialized raw hexadacimal data (classmethod) - static BtcTransaction deserialize( - {List? txBytes, String? raw, bool allowWitness = true}) { - assert(txBytes != null || raw != null); + static BtcTransaction deserialize(List txBytes, + {bool allowWitness = true}) { try { - if (raw != null) { - final txBytes = BytesUtils.fromHexString(raw); - } final version = txBytes.sublist(0, 4); int cursor = 4; List? flag; bool hasWitness = false; bool hasMweb = false; if (txBytes[4] == 0) { - flag = List.from(rawtx.sublist(5, 6)); + flag = List.from(txBytes.sublist(5, 6)); if (allowWitness && (flag[0] & 1 > 0)) { hasWitness = true; } @@ -132,7 +142,7 @@ class BtcTransaction { if (canReplaceByFee == false) { canReplaceByFee = - const ListEquality().equals(input.sequence, BitcoinOpCodeConst.REPLACE_BY_FEE_SEQUENCE); + const ListEquality().equals(inp.item1.sequence, BitcoinOpCodeConst.replaceByFeeSequence); } } final outputs = []; @@ -167,7 +177,7 @@ class BtcTransaction { } List? mwebBytes; if (hasMweb) { - mwebBytes = txBytes.sublist(cursor, rawtx.length - 4); + mwebBytes = txBytes.sublist(cursor, txBytes.length - 4); } // TODO: should this be added // cursor = rawtx.length - 4; diff --git a/lib/src/bitcoin/script/witness.dart b/lib/src/bitcoin/script/witness.dart index 80e22b6..9424b6d 100644 --- a/lib/src/bitcoin/script/witness.dart +++ b/lib/src/bitcoin/script/witness.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:blockchain_utils/helper/extensions/extensions.dart'; import 'package:blockchain_utils/utils/utils.dart'; diff --git a/lib/src/bitcoin/silent_payments/address.dart b/lib/src/bitcoin/silent_payments/address.dart index e622851..9dd7d00 100644 --- a/lib/src/bitcoin/silent_payments/address.dart +++ b/lib/src/bitcoin/silent_payments/address.dart @@ -106,7 +106,6 @@ class SilentPaymentAddress implements BitcoinBaseAddress { final int version; final ECPublic B_scan; final ECPublic B_spend; - @override BasedUtxoNetwork? network; final String hrp; diff --git a/lib/src/bitcoin/silent_payments/utils.dart b/lib/src/bitcoin/silent_payments/utils.dart index 1f68c07..c43f0bf 100644 --- a/lib/src/bitcoin/silent_payments/utils.dart +++ b/lib/src/bitcoin/silent_payments/utils.dart @@ -67,7 +67,7 @@ ECPublic? getPubkeyFromInput(VinInfo vin) { final pubkeyBytes = vin.scriptSig.sublist(i - 33, i); final pubkeyHash = BytesUtils.toHexString(QuickCrypto.hash160(pubkeyBytes)); if (pubkeyHash == - P2pkhAddress.fromScriptPubkey(script: vin.prevOutScript).addressProgram) { + P2pkhAddress.fromScript(script: vin.prevOutScript).addressProgram) { return ECPublic.fromBytes(pubkeyBytes); } } @@ -75,13 +75,13 @@ ECPublic? getPubkeyFromInput(VinInfo vin) { break; case P2shAddressType.p2pkhInP2sh: final redeemScript = vin.scriptSig.sublist(1); - if (Script.fromRaw(byteData: redeemScript).getAddressType() == SegwitAddresType.p2wpkh) { + if (Script.deserialize(bytes: redeemScript).getAddressType() == SegwitAddressType.p2wpkh) { return ECPublic.fromBytes(vin.txinwitness.scriptWitness.stack.last.buffer.asUint8List()); } break; - case SegwitAddresType.p2wpkh: + case SegwitAddressType.p2wpkh: return ECPublic.fromBytes(vin.txinwitness.scriptWitness.stack.last.buffer.asUint8List()); - case SegwitAddresType.p2tr: + case SegwitAddressType.p2tr: final witnessStack = vin.txinwitness.scriptWitness.stack; if (witnessStack.isNotEmpty) { if (witnessStack.length > 1 && witnessStack.last.buffer.asUint8List()[0] == 0x50) { diff --git a/lib/src/crypto/keypair/ec_private.dart b/lib/src/crypto/keypair/ec_private.dart index d97ea9b..0abdb0e 100644 --- a/lib/src/crypto/keypair/ec_private.dart +++ b/lib/src/crypto/keypair/ec_private.dart @@ -214,10 +214,10 @@ class ECPrivate { } ECPrivate toTweakedTaprootKey() { - final t = P2TRUtils.calculateTweek(getPublic().publicKey.point as ProjectiveECCPoint); + final t = P2TRUtils.calculateTweek(getPublic().publicKey.point); return ECPrivate.fromBytes( - BitcoinSignerUtils.calculatePrivateTweek(toBytes(), BigintUtils.fromBytes(t))); + BitcoinSignerUtils.calculatePrivateTweek(toBytes(), t)); } static ECPrivate random() { diff --git a/lib/src/crypto/keypair/ec_public.dart b/lib/src/crypto/keypair/ec_public.dart index 8f91e14..8e47aab 100644 --- a/lib/src/crypto/keypair/ec_public.dart +++ b/lib/src/crypto/keypair/ec_public.dart @@ -5,6 +5,7 @@ import 'package:bitcoin_base/src/bitcoin/script/script.dart'; import 'package:bitcoin_base/src/bitcoin/taproot/taproot.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:blockchain_utils/crypto/crypto/cdsa/point/base.dart'; typedef PublicKeyType = PubKeyModes; @@ -169,6 +170,11 @@ class ECPublic { } } + /// toCompressedBytes returns the compressed byte representation of the ECPublic key. + List toCompressedBytes() { + return publicKey.compressed; + } + EncodeType? getEncodeType() { return publicKey.point.encodeType; } @@ -366,7 +372,7 @@ class ECPublic { int get hashCode => publicKey.hashCode; ECPublic tweakAdd(BigInt tweak) { - final point = publicKey.point as ProjectiveECCPoint; + final point = publicKey.point; // Compute the new public key after adding the tweak final tweakedKey = point + (Curves.generatorSecp256k1 * tweak); @@ -375,7 +381,7 @@ class ECPublic { // Perform the tweak multiplication ECPublic tweakMul(BigInt tweak) { - final point = publicKey.point as ProjectiveECCPoint; + final point = publicKey.point; // Perform the tweak multiplication final tweakedKey = point * tweak; @@ -383,13 +389,13 @@ class ECPublic { } ECPublic pubkeyAdd(ECPublic other) { - final tweakedKey = (publicKey.point as ProjectiveECCPoint) + other.publicKey.point; + final tweakedKey = (publicKey.point) + other.publicKey.point; return ECPublic.fromBytes(tweakedKey.toBytes()); } ECPublic negate() { // Negate the Y-coordinate by subtracting it from the field size (p). - final point = (publicKey.point as ProjectiveECCPoint); + final point = publicKey.point; final y = point.curve.p - point.y; return ECPublic.fromBytes(BytesUtils.fromHexString( "04${BytesUtils.toHexString(BigintUtils.toBytes(point.x, length: point.curve.baselen))}${BytesUtils.toHexString(BigintUtils.toBytes(y, length: point.curve.baselen))}")); diff --git a/lib/src/models/network.dart b/lib/src/models/network.dart index 2a7db9d..ccd25fd 100644 --- a/lib/src/models/network.dart +++ b/lib/src/models/network.dart @@ -187,7 +187,7 @@ class BitcoinNetwork implements BasedUtxoNetwork { P2shAddressType.p2wpkhInP2sh, P2shAddressType.p2pkhInP2sh, P2shAddressType.p2pkInP2sh, - SilentPaymentAddressType.p2ps, + SilentPaymentsAddresType.p2sp, ]; @override diff --git a/lib/src/provider/models/multisig_script.dart b/lib/src/provider/models/multisig_script.dart index 943e42e..320430e 100644 --- a/lib/src/provider/models/multisig_script.dart +++ b/lib/src/provider/models/multisig_script.dart @@ -76,7 +76,7 @@ class MultiSignatureAddress { BitcoinBaseAddress toP2wshInP2shAddress({required BasedUtxoNetwork network}) { final p2wsh = toP2wshAddress(network: network); - return P2shAddress.fromScriptPubkey( + return P2shAddress.fromScript( script: p2wsh.toScriptPubKey(), type: P2shAddressType.p2wshInP2sh); } @@ -89,11 +89,11 @@ class MultiSignatureAddress { if (addressType.hashLength == 32) { return P2shAddress.fromHash160( - h160: BytesUtils.toHexString( + addrHash: BytesUtils.toHexString( QuickCrypto.sha256DoubleHash(multiSigScript.toBytes())), type: addressType); } - return P2shAddress.fromScriptPubkey(script: multiSigScript, type: addressType); + return P2shAddress.fromScript(script: multiSigScript, type: addressType); } BitcoinBaseAddress fromType( diff --git a/lib/src/provider/models/utxo_details.dart b/lib/src/provider/models/utxo_details.dart index 8ccf830..95d35e0 100644 --- a/lib/src/provider/models/utxo_details.dart +++ b/lib/src/provider/models/utxo_details.dart @@ -230,7 +230,6 @@ class BitcoinUtxo { required this.scriptType, this.blockHeight, this.token, - this.isSilentPayment, required this.isP2tr, required this.isP2shSegwit, required this.isSegwit}); @@ -265,8 +264,6 @@ class BitcoinUtxo { isSegwit: isP2shSegwit || scriptType.isSegwit); } - bool? isSilentPayment; - /// check if utxos is p2tr final bool isP2tr; diff --git a/lib/src/provider/providers/explorer.dart b/lib/src/provider/providers/explorer.dart index 381ce4f..7f3dab2 100644 --- a/lib/src/provider/providers/explorer.dart +++ b/lib/src/provider/providers/explorer.dart @@ -53,7 +53,7 @@ class ApiProvider { "id": DateTime.now().millisecondsSinceEpoch.toString(), "params": params }; - final response = await _postReqiest>( + final response = await _postRequest>( "https://btc.getblock.io/786c97b8-f53f-427b-80f7-9af7bd5bdb84/testnet/", json.encode(data)); return response; } diff --git a/lib/src/transaction_builder/transaction_builder.dart b/lib/src/transaction_builder/transaction_builder.dart index 25878c9..2512011 100644 --- a/lib/src/transaction_builder/transaction_builder.dart +++ b/lib/src/transaction_builder/transaction_builder.dart @@ -1,5 +1,6 @@ import 'package:bitcoin_base/src/bitcoin/address/address.dart'; import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; +import 'package:bitcoin_base/src/bitcoin/silent_payments/silent_payments.dart'; import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:bitcoin_base/src/models/network.dart'; import 'package:bitcoin_base/src/provider/models/utxo_details.dart'; @@ -235,7 +236,7 @@ class BitcoinTransactionBuilder implements BasedBitcoinTransacationBuilder { return senderPub.toAddress().toScriptPubKey(); case SegwitAddressType.p2tr: return senderPub.toTaprootAddress().toScriptPubKey(); - case SegwitAddresType.mweb: + case SegwitAddressType.mweb: return Script(script: []); case P2shAddressType.p2pkhInP2sh: if (isTaproot) { @@ -372,7 +373,7 @@ that demonstrate the right to spend the bitcoins associated with the correspondi case SegwitAddressType.p2wpkh: case P2shAddressType.p2wpkhInP2sh: return [signedDigest, senderPub.toHex()]; - case SegwitAddresType.mweb: + case SegwitAddressType.mweb: return []; default: throw DartBitcoinPluginException( @@ -457,13 +458,13 @@ that demonstrate the right to spend the bitcoins associated with the correspondi void _buildSilentPayments() { List silentPaymentDestinations = []; - for (final out in outputs as List) { + for (final out in outPuts as List) { final address = out.address; if (address is SilentPaymentAddress) { try { - utxosInfo.firstWhere((utxo) => utxo.ownerDetails.address.type == SegwitAddresType.p2wsh); - throw const BitcoinBasePluginException('Silent payments not supported for P2WSH'); + utxosInfo.firstWhere((utxo) => utxo.ownerDetails.address.type == SegwitAddressType.p2wsh); + throw const DartBitcoinPluginException('Silent payments not supported for P2WSH'); } catch (_) {} out.isSilentPayment = true; @@ -484,8 +485,8 @@ that demonstrate the right to spend the bitcoins associated with the correspondi final outputsAdded = []; - for (var i = 0; i < outputs.length; i++) { - final out = outputs[i] as BitcoinOutput; + for (var i = 0; i < outPuts.length; i++) { + final out = outPuts[i] as BitcoinOutput; final silentOutputs = sendingOutputs[out.address.toAddress(network)]; @@ -493,7 +494,7 @@ that demonstrate the right to spend the bitcoins associated with the correspondi final silentOutput = silentOutputs.firstWhere((element) => !outputsAdded.contains(element)); - outputs[i] = BitcoinOutput( + outPuts[i] = BitcoinOutput( address: silentOutput.address, value: BigInt.from(silentOutput.amount), isSilentPayment: true, diff --git a/lib/src/utils/script.dart b/lib/src/utils/script.dart index f5a3838..e6dc4a6 100644 --- a/lib/src/utils/script.dart +++ b/lib/src/utils/script.dart @@ -1,9 +1,9 @@ import 'package:bitcoin_base/src/bitcoin/script/scripts.dart'; bool isDefinedHashType(sighash) { - final hashTypeMod = sighash & ~BitcoinOpCodeConst.SIGHASH_ANYONECANPAY; - return hashTypeMod > BitcoinOpCodeConst.SIGHASH_ALL && - hashTypeMod < BitcoinOpCodeConst.SIGHASH_SINGLE; + final hashTypeMod = sighash & ~BitcoinOpCodeConst.sighashAnyoneCanPay; + return hashTypeMod > BitcoinOpCodeConst.sighashAll && + hashTypeMod < BitcoinOpCodeConst.sighashSingle; } bool bip66check(buffer) { diff --git a/pubspec.yaml b/pubspec.yaml index f612055..3b6f604 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,9 @@ environment: dependencies: + convert: ^3.1.1 + pointycastle: ^3.7.4 + bip32: ^2.0.0 blockchain_utils: ^5.0.0 # blockchain_utils: diff --git a/test/p2sh_test.dart b/test/p2sh_test.dart index 41a76e7..dd64279 100644 --- a/test/p2sh_test.dart +++ b/test/p2sh_test.dart @@ -18,7 +18,7 @@ void main() { ]); final txout = TxOutput( amount: BigInt.from(9000000), - scriptPubKey: P2shAddress.fromRedeemScript( + scriptPubKey: P2shAddress.fromScript( script: p2pkRedeemScript, type: P2shAddressType.p2pkInP2sh) .toScriptPubKey()); const createP2shAndSendResult = @@ -44,7 +44,7 @@ void main() { txId: 'f557c623e55f0affc696b742630770df2342c4aac395e0ed470923247bc51b95', txIndex: 0, - sequence: seq.forInputSequence()); + sequance: seq.forInputSequence()); final anotherAddr = P2pkhAddress.fromAddress( address: 'n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR', network: BitcoinNetwork.testnet); diff --git a/test/p2wpkh_test.dart b/test/p2wpkh_test.dart index e64c4ad..4110d77 100644 --- a/test/p2wpkh_test.dart +++ b/test/p2wpkh_test.dart @@ -7,9 +7,9 @@ void main() { 'cTALNpTpRbbxTCJ2A5Vq88UxT44w1PE2cYqiB3n4hRvzyCev1Wwo', netVersion: BitcoinNetwork.testnet.wifNetVer); - final p2pkhAddr = sk.getPublic().toP2pkhAddress(); + final p2pkhAddr = sk.getPublic().toAddress(); - final p2wpkhAddr = sk.getPublic().toP2wpkhAddress(); + final p2wpkhAddr = sk.getPublic().toSegwitAddress(); final txin1 = TxInput( txId: diff --git a/test/p2wsh_test.dart b/test/p2wsh_test.dart index 01f4410..91e6c03 100644 --- a/test/p2wsh_test.dart +++ b/test/p2wsh_test.dart @@ -18,9 +18,9 @@ void main() { BitcoinOpcode.opCheckMultiSig ]); - final p2wshAddr = P2wshAddress.fromRedeemScript(script: p2wshScript); + final p2wshAddr = P2wshAddress.fromScript(script: p2wshScript); - final p2pkhAddr = sk1.getPublic().toP2pkhAddress(); + final p2pkhAddr = sk1.getPublic().toAddress(); final txin1 = TxInput( txId: @@ -59,7 +59,7 @@ void main() { amount: BigInt.from(100000), scriptPubKey: p2wshAddr.toScriptPubKey()); final output2Multiple = TxOutput( amount: BigInt.from(100000), - scriptPubKey: sk1.getPublic().toP2wpkhAddress().toScriptPubKey()); + scriptPubKey: sk1.getPublic().toSegwitAddress().toScriptPubKey()); final output3Multiple = TxOutput( amount: BigInt.from(1770000), scriptPubKey: p2pkhAddr.toScriptPubKey()); diff --git a/test/silent_payments.dart b/test/silent_payments.dart index 1593f9c..003d455 100644 --- a/test/silent_payments.dart +++ b/test/silent_payments.dart @@ -55,13 +55,13 @@ main() { final pubkey = getPubkeyFromInput(vin); - if (pubkey == null || pubkey.getEncodeType() != EncodeType.compressed) { + if (pubkey == null || pubkey.getEncodeType() != EncodeType.comprossed) { continue; } inputPrivKeyInfos.add(ECPrivateInfo( privkey, - prevoutScript.getAddressType() == SegwitAddresType.p2tr, + prevoutScript.getAddressType() == SegwitAddressType.p2tr, tweak: false, )); inputPubKeys.add(pubkey); @@ -159,7 +159,7 @@ main() { final pubkey = getPubkeyFromInput(vin); - if (pubkey == null || pubkey.getEncodeType() != EncodeType.compressed) { + if (pubkey == null || pubkey.getEncodeType() != EncodeType.comprossed) { continue; } @@ -196,7 +196,7 @@ main() { // Sign the message with schnorr final btcSigner = BitcoinSigner.fromKeyBytes(fullPrivateKey.toBytes()); List sig = - btcSigner.signSchnorrTransaction(msg, tapScripts: [], tweak: false, aux: aux); + btcSigner.signSchnorrTransaction(msg, tapScripts: [], tweak: false, auxRand: aux); // Verify the message is correct expect(btcSigner.verifyKey.verifySchnorr(msg, sig, isTweak: false), true); From 71cd5de83f29e20f410300397aa12435ae4bd33f Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 26 Jun 2025 05:00:10 +0300 Subject: [PATCH 12/12] fixes --- lib/src/bitcoin/address/util.dart | 8 ++++---- lib/src/bitcoin/silent_payments/address.dart | 5 ++++- lib/src/bitcoin/taproot/utils/utils.dart | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/src/bitcoin/address/util.dart b/lib/src/bitcoin/address/util.dart index a8db943..b101aea 100644 --- a/lib/src/bitcoin/address/util.dart +++ b/lib/src/bitcoin/address/util.dart @@ -21,7 +21,7 @@ class BitcoinAddressUtils { if (addressType.type == SegwitAddressType.mweb) { return BytesUtils.fromHexString( - MwebAddress.fromAddress(address: address).addressProgram, + MwebAddress.fromAddress(address: address, network: network).addressProgram, ); } @@ -32,12 +32,12 @@ class BitcoinAddressUtils { try { switch (script.getAddressType()) { case P2pkhAddressType.p2pkh: - return P2pkhAddress.fromScriptPubkey(script: script).toAddress(network); + return P2pkhAddress.fromScript(script: script).toAddress(network); case P2shAddressType.p2pkInP2sh: case P2shAddressType.p2pkhInP2sh: case P2shAddressType.p2wpkhInP2sh: case P2shAddressType.p2wshInP2sh: - return P2shAddress.fromScriptPubkey(script: script).toAddress(network); + return P2shAddress.fromScript(script: script).toAddress(network); case SegwitAddressType.p2wpkh: return P2wpkhAddress.fromScriptPubkey(script: script).toAddress(network); case SegwitAddressType.p2wsh: @@ -77,7 +77,7 @@ class BitcoinAddressUtils { } catch (_) {} try { - return MwebAddress.fromAddress(address: address).type; + return MwebAddress.fromAddress(address: address, network: network).type; } catch (_) {} try { diff --git a/lib/src/bitcoin/silent_payments/address.dart b/lib/src/bitcoin/silent_payments/address.dart index 9dd7d00..0545ce4 100644 --- a/lib/src/bitcoin/silent_payments/address.dart +++ b/lib/src/bitcoin/silent_payments/address.dart @@ -60,7 +60,10 @@ class SilentPaymentOwner extends SilentPaymentAddress { } List generateLabel(int m) { - return taggedHash(BytesUtils.concatBytes([b_scan.toBytes(), serUint32(m)]), "BIP0352/Label"); + return TaprootUtils.taggedHashTag( + BytesUtils.concatBytes([b_scan.toBytes(), serUint32(m)]), + "BIP0352/Label", + ); } SilentPaymentOwner toLabeledSilentPaymentAddress(int m) { diff --git a/lib/src/bitcoin/taproot/utils/utils.dart b/lib/src/bitcoin/taproot/utils/utils.dart index e49cc29..5fa714d 100644 --- a/lib/src/bitcoin/taproot/utils/utils.dart +++ b/lib/src/bitcoin/taproot/utils/utils.dart @@ -9,6 +9,20 @@ import 'package:bitcoin_base/src/exception/exception.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; class TaprootUtils { + /// Function: taggedHash + /// Description: Computes a tagged hash of the input data with a provided tag. + /// Input: + /// - `List` data - The data to be hashed. + /// - String tag - A unique tag to differentiate the hash. + /// Output: `List` - The resulting tagged hash. + /// Note: This function combines the provided tag with the input data to create a unique + /// hash by applying a double SHA-256 hash. + static List taggedHashTag(List data, String tag) { + /// Calculate the hash of the tag as List. + final tagDigest = QuickCrypto.sha256Hash(List.from(tag.codeUnits)); + return taggedHash(data, tagDigest); + } + static List taggedHash(List data, List tagDigest) { return QuickCrypto.sha256Hash([...tagDigest, ...tagDigest, ...data]); }