Skip to content

Commit 5d36e48

Browse files
committed
V6.0.0
- Added support for BIP-327: MuSig2 for BIP340-compatible multi-signatures - Implemented BIP-174: Partially Signed Bitcoin Transaction (PSBT) format - Integrated BIP-370: PSBT Version 2 enhancements - Included BIP-371: Taproot fields for PSBT - Extended support for BIP-373: MuSig2-related PSBT fields
1 parent 941ac9a commit 5d36e48

100 files changed

Lines changed: 16599 additions & 966 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 6.0.0
2+
3+
- Added support for BIP-327: MuSig2 for BIP340-compatible multi-signatures
4+
- Implemented BIP-174: Partially Signed Bitcoin Transaction (PSBT) format
5+
- Integrated BIP-370: PSBT Version 2 enhancements
6+
- Included BIP-371: Taproot fields for PSBT
7+
- Extended support for BIP-373: MuSig2-related PSBT fields
8+
19
## 5.3.0
210

311
* Update dependencies

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ Using this package, you can create a Bitcoin transaction in two ways: either thr
8080

8181
- P2SH(SEGWIT): A P2SH (Pay-to-Script-Hash) Segregated Witness (SegWit) address in Bitcoin combines the benefits of P2SH and SegWit technologies, allowing for enhanced transaction security, reduced fees, and improved scalability.
8282

83+
### MuSig2 BIP-327: MuSig2 for BIP340-compatible Multi-Signatures
84+
You can find the examples here.
85+
86+
- Sign/Verify: Supports signing and verifying multisignature transactions using MuSig2
87+
- NonceAgg: Aggregates nonces from multiple participants for secure signature generation.
88+
- KeyAgg: Combines multiple public keys into a single aggregated public key for efficient multisignature verification
89+
90+
### PSBT
91+
You can find the examples here.
92+
93+
- BIP-0174: Partially Signed Bitcoin Transaction Format
94+
- BIP-0370: PSBT Version 2
95+
- BIP-0371: Taproot Fields for PSBT
96+
- BIP-0373: MuSig2 PSBT Fields
97+
8398
### Addresses specific to Bitcoin Cash
8499

85100
- P2SH32: Pay to Script Hash 32

example/lib/bitcoin_cash/burn_token_example.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ void main() async {
6161
if (sumOfUtxo == BigInt.zero) {
6262
return;
6363
}
64-
// print(sumOfUtxo);
65-
// return;
64+
6665
/// CashToken{bitfield: 16, commitment: null, amount: 2000, category: 4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430}
6766
final CashToken token = elctrumUtxos
6867
.firstWhere((e) =>

example/lib/bitcoin_cash/create_cash_token_example.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ void main() async {
6464
/// if we dont have utxos with index 0 we must create them with some estimate transaction before create transaction
6565
return;
6666
}
67-
// print("vout $vout0Hash");
68-
// return;
6967
final bchTransaction = ForkedTransactionBuilder(
7068
outPuts: [
7169
BitcoinTokenOutput(

example/lib/global/old_examples/bitcoin_example.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ void _spendFromP2pkhTo10DifferentType() async {
181181
/// Calculate the size of the transaction in bytes.
182182
/// You can determine the transaction fee by multiplying the transaction size
183183
/// Formula: transaction fee = (transaction size in bytes * fee rate in bytes)
184-
final size = tr.hasSegwit ? tr.getVSize() : tr.getSize();
184+
final size = tr.hasWitness ? tr.getVSize() : tr.getSize();
185185

186186
/// broadcast transaction
187187
/// https://mempool.space/testnet/tx/05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258
@@ -427,7 +427,7 @@ void _spendFrom10DifferentTypeToP2pkh() async {
427427
/// Calculate the size of the transaction in bytes.
428428
/// You can determine the transaction fee by multiplying the transaction size
429429
/// Formula: transaction fee = (transaction size in bytes * fee rate in bytes)
430-
final size = tr.hasSegwit ? tr.getVSize() : tr.getSize();
430+
final size = tr.hasWitness ? tr.getVSize() : tr.getSize();
431431

432432
/// broadcast transaction
433433
/// https://mempool.space/testnet/tx/3e697e0993a6882689ff9b66ff73cdf53e4a3029664ec4a516da2b291e1cd8a6

example/lib/global/old_examples/litecoin_example/litecoin_example.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ void _spendLTCP2pkhAddress() async {
141141
/// Calculate the size of the transaction in bytes.
142142
/// You can determine the transaction fee by multiplying the transaction size
143143
/// Formula: transaction fee = (transaction size in bytes * fee rate in bytes)
144-
final size = tr.hasSegwit ? tr.getVSize() : tr.getSize();
144+
final size = tr.hasWitness ? tr.getVSize() : tr.getSize();
145145

146146
/// broadcast transaction
147147
await _broadcastTransaction(tr.serialize());
@@ -287,7 +287,7 @@ void _spendFrom2P2shAddressAndOneMultiSigP2shAddress() async {
287287
/// Calculate the size of the transaction in bytes.
288288
/// You can determine the transaction fee by multiplying the transaction size
289289
/// Formula: transaction fee = (transaction size in bytes * fee rate in bytes)
290-
final size = tr.hasSegwit ? tr.getVSize() : tr.getSize();
290+
final size = tr.hasWitness ? tr.getVSize() : tr.getSize();
291291

292292
/// broadcast transaction
293293
await _broadcastTransaction(tr.serialize());
@@ -399,7 +399,7 @@ void _spendFromNestedSegwitP2WPKHInP2SH() async {
399399
/// Calculate the size of the transaction in bytes.
400400
/// You can determine the transaction fee by multiplying the transaction size
401401
/// Formula: transaction fee = (transaction size in bytes * fee rate in bytes)
402-
final size = tr.hasSegwit ? tr.getVSize() : tr.getSize();
402+
final size = tr.hasWitness ? tr.getVSize() : tr.getSize();
403403

404404
/// broadcast transaction
405405
await _broadcastTransaction(tr.serialize());
@@ -519,7 +519,7 @@ void _spendFromSegwitP2WPKHAddress() async {
519519
/// Calculate the size of the transaction in bytes.
520520
/// You can determine the transaction fee by multiplying the transaction size
521521
/// Formula: transaction fee = (transaction size in bytes * fee rate in bytes)
522-
final size = tr.hasSegwit ? tr.getVSize() : tr.getSize();
522+
final size = tr.hasWitness ? tr.getVSize() : tr.getSize();
523523

524524
/// broadcast transaction
525525
await _broadcastTransaction(tr.serialize());

example/lib/global/old_examples/spending_with_scripts/spending_builders.dart

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ BtcTransaction buildP2wpkTransaction({
3232

3333
// create BtcTransaction instance with inputs, outputs and segwit
3434
// For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, we need to set 'hasSegwit' to true.
35-
BtcTransaction tx =
36-
BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: true);
35+
BtcTransaction tx = BtcTransaction(inputs: txin, outputs: txOut);
3736

3837
for (int i = 0; i < txin.length; i++) {
3938
// For SegWit transactions (excluding P2TR), we use the 'getTransactionSegwitDigit' method
@@ -47,7 +46,7 @@ BtcTransaction buildP2wpkTransaction({
4746
amount: utxo[i].utxo.value);
4847
// sign transaction
4948
final signedTx =
50-
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.SIGHASH_ALL);
49+
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll);
5150

5251
// create unlock script
5352

@@ -98,8 +97,7 @@ BtcTransaction buildP2WSHTransaction({
9897

9998
// create BtcTransaction instance with inputs, outputs and segwit
10099
// For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, we need to set 'hasSegwit' to true.
101-
BtcTransaction tx =
102-
BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: true);
100+
BtcTransaction tx = BtcTransaction(inputs: txin, outputs: txOut);
103101
for (int i = 0; i < txin.length; i++) {
104102
// For SegWit transactions (excluding P2TR), we use the 'getTransactionSegwitDigit' method
105103
// to obtain the input digest for signing.
@@ -113,7 +111,7 @@ BtcTransaction buildP2WSHTransaction({
113111

114112
// sign transaction
115113
final signedTx = sign(txDigit, utxo[i].public().toP2wshScript().toHex(),
116-
BitcoinOpCodeConst.SIGHASH_ALL);
114+
BitcoinOpCodeConst.sighashAll);
117115

118116
// create unlock script
119117

@@ -156,7 +154,7 @@ BtcTransaction buildP2pkhTransaction({
156154
.toList();
157155

158156
// For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, in this case we need to set 'hasSegwit' to false.
159-
final tx = BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: false);
157+
final tx = BtcTransaction(inputs: txin, outputs: txOut);
160158
for (int i = 0; i < txin.length; i++) {
161159
// For None-SegWit transactions, we use the 'getTransactionDigest' method
162160
// to obtain the input digest for signing.
@@ -169,7 +167,7 @@ BtcTransaction buildP2pkhTransaction({
169167

170168
// sign transaction
171169
final signedTx =
172-
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.SIGHASH_ALL);
170+
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll);
173171

174172
// set unlocking script for current index
175173
txin[i].scriptSig = Script(script: [signedTx, utxo[i].public().toHex()]);
@@ -202,7 +200,7 @@ BtcTransaction buildP2shNoneSegwitTransaction({
202200
.toList();
203201

204202
// For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, in this caase we need to set 'hasSegwit' to false.
205-
final tx = BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: false);
203+
final tx = BtcTransaction(inputs: txin, outputs: txOut);
206204
for (int i = 0; i < txin.length; i++) {
207205
final ownerPublic = utxo[i].public();
208206
final scriptPubKey =
@@ -219,7 +217,7 @@ BtcTransaction buildP2shNoneSegwitTransaction({
219217
);
220218
// sign transaction
221219
final signedTx =
222-
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.SIGHASH_ALL);
220+
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll);
223221

224222
// set unlocking script for current index
225223
switch (utxo[i].ownerDetails.address.type) {
@@ -269,7 +267,7 @@ BtcTransaction buildP2SHSegwitTransaction({
269267
final List<TxWitnessInput> witnesses = [];
270268

271269
// For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, we need to set 'hasSegwit' to true.
272-
final tx = BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: true);
270+
final tx = BtcTransaction(inputs: txin, outputs: txOut);
273271

274272
for (int i = 0; i < txin.length; i++) {
275273
final ownerPublic = utxo[i].public();
@@ -290,7 +288,7 @@ BtcTransaction buildP2SHSegwitTransaction({
290288

291289
// sign transaction
292290
final signedTx =
293-
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.SIGHASH_ALL);
291+
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll);
294292

295293
// In a SegWit P2SH (Pay-to-Script-Hash) transaction, you will find both a scriptSig field and a witness field.
296294
// This combination is used to maintain compatibility with non-SegWit Bitcoin nodes while taking advantage
@@ -347,8 +345,7 @@ BtcTransaction buildP2trTransaction({
347345
.toList();
348346

349347
// For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, we need to set 'hasSegwit' to true.
350-
BtcTransaction tx =
351-
BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: true);
348+
BtcTransaction tx = BtcTransaction(inputs: txin, outputs: txOut);
352349

353350
// in a SegWit (Segregated Witness) transaction, the witness data serves as the unlocking script
354351
// for the transaction inputs. In traditional non-SegWit transactions,
@@ -373,16 +370,12 @@ BtcTransaction buildP2trTransaction({
373370
utxo.map((e) => e.ownerDetails.address.toScriptPubKey()).toList(),
374371
amounts: utxo.map((e) => e.utxo.value).toList(),
375372

376-
// The tapleaf script that we are spending (ext_flag=1)
377-
script: null,
378-
sighash: BitcoinOpCodeConst.TAPROOT_SIGHASH_ALL,
379-
// default is 0; 1 is for script spending (BIP342)
380-
extFlags: 0,
373+
sighash: BitcoinOpCodeConst.sighashDefault,
381374
);
382375

383376
// sign transaction using `signTapRoot` method of thransaction
384377
final signedTx =
385-
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.SIGHASH_ALL);
378+
sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll);
386379

387380
// add witness for current index
388381
witnesses.add(TxWitnessInput(stack: [signedTx]));

example/lib/global/old_examples/spending_with_transaction_builder/multi_sig_transactions.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ void main() async {
282282

283283
// we check if transaction is segwit or not
284284
// When one of the input UTXO addresses is SegWit, the transaction is considered SegWit.
285-
final isSegwitTr = transaction.hasSegwit;
285+
final isSegwitTr = transaction.hasWitness;
286286

287287
// transaction id
288288
final transactionId = transaction.txId();

example/lib/global/old_examples/spending_with_transaction_builder/transaction_builder_example.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ void main() async {
229229

230230
// we check if transaction is segwit or not
231231
// When one of the input UTXO addresses is SegWit, the transaction is considered SegWit.
232-
final isSegwitTr = transaction.hasSegwit;
232+
final isSegwitTr = transaction.hasWitness;
233233

234234
// transaction id
235235
// ignore: unused_local_variable

example/lib/musig/methods.dart

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'package:bitcoin_base/bitcoin_base.dart';
2+
import 'package:example/services_examples/electrum/electrum_ssl_service.dart';
3+
4+
Future<ElectrumProvider> getProvider(
5+
{String url = "testnet4-electrumx.wakiyamap.dev:51002"}) async {
6+
final service = await ElectrumSSLService.connect(
7+
"testnet4-electrumx.wakiyamap.dev:51002");
8+
return ElectrumProvider(service);
9+
}
10+
11+
class PsbtUtxoRequest {
12+
final BitcoinBaseAddress address;
13+
final Script? p2shRedeemScript;
14+
final Script? witnessScript;
15+
final TapLeafMerkleProof? merkleProof;
16+
final TaprootTree? treeScript;
17+
final TaprootLeaf? leafScript;
18+
final List<int>? xOnlyOrInternalPubKey;
19+
final List<int>? merkleRoot;
20+
final List<TapLeafMerkleProof>? leafScripts;
21+
final List<ECPrivate> privateKeys;
22+
final List<PsbtInputMuSig2ParticipantPublicKeys>? muSig2ParticipantPublicKeys;
23+
final List<PsbtInputRipemd160>? ripemd160;
24+
final List<PsbtInputSha256>? sha256;
25+
final List<PsbtInputHash160>? hash160;
26+
final List<PsbtInputHash256>? hash256;
27+
const PsbtUtxoRequest(
28+
{required this.address,
29+
this.p2shRedeemScript,
30+
this.witnessScript,
31+
this.leafScript,
32+
this.merkleProof,
33+
this.treeScript,
34+
this.xOnlyOrInternalPubKey,
35+
this.merkleRoot,
36+
this.leafScripts,
37+
this.privateKeys = const [],
38+
this.muSig2ParticipantPublicKeys,
39+
this.ripemd160,
40+
this.hash160,
41+
this.hash256,
42+
this.sha256});
43+
}
44+
45+
Future<List<PsbtUtxo>> getPsbtUtxo(
46+
{required List<PsbtUtxoRequest> addresses,
47+
bool local = true,
48+
List<Map<String, dynamic>>? data}) async {
49+
final provider = await getProvider();
50+
51+
final utxos = await Future.wait(addresses.map((e) async {
52+
return await provider.request(ElectrumRequestScriptHashListUnspent(
53+
scriptHash: e.address.pubKeyHash()));
54+
}));
55+
56+
final utxoss = List.generate(utxos.length, (i) async {
57+
final request = addresses[i];
58+
final accountUtxos = utxos[i];
59+
final er = await Future.wait(accountUtxos
60+
.map((e) => provider.request(ElectrumRequestGetRawTransaction(e.txId)))
61+
.toList());
62+
return List.generate(
63+
accountUtxos.length,
64+
(index) {
65+
return PsbtUtxo(
66+
utxo: accountUtxos[index].toUtxo(request.address.type),
67+
p2shRedeemScript: request.p2shRedeemScript,
68+
p2wshWitnessScript: request.witnessScript,
69+
tx: er[index],
70+
scriptPubKey: request.address.toScriptPubKey(),
71+
leafScript: request.leafScript,
72+
leafScripts: request.leafScripts,
73+
merkleProof: request.merkleProof,
74+
treeScript: request.treeScript,
75+
merkleRoot: request.merkleRoot,
76+
privateKeys: request.privateKeys,
77+
xOnlyOrInternalPubKey: request.xOnlyOrInternalPubKey,
78+
muSig2ParticipantPublicKeys: request.muSig2ParticipantPublicKeys,
79+
hash160: request.hash160,
80+
hash256: request.hash256,
81+
ripemd160: request.ripemd160,
82+
sha256: request.sha256);
83+
},
84+
);
85+
});
86+
final e = await Future.wait(utxoss);
87+
return e.expand((e) => e).toList();
88+
}

0 commit comments

Comments
 (0)