Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@ jobs:
run: dart test ./test

- name: Run tests web
run: dart test -p chrome ./test

run: dart test -p chrome ./test --timeout=20m

- name: Run tests web wasm
run: dart test -p chrome ./test -c dart2wasm --timeout=20m
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ migrate_working_dir/
.packages
build/
*.zip
/example/test
80 changes: 80 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,83 @@
## 6.5.0

- Fix transaction parsing in Mempool API.

## 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.
- Add estimate transaction size for psbt
- Add more script validation in psbt

## 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.

## 6.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

## 5.3.0

* Update dependencies

## 5.2.0

* Update dependencies

## 5.1.0

* Update dependencies

## 5.0.0

* Update dependencies
* Minimum required Dart SDK version updated to 3.3.

## 4.9.4

* Improved serialization process for large transaction scripts.
* Added support for the Electra network.
* Create and spent from uncomprossed public key format.
* Important Notice: This is the final version supporting Dart v2. The next release will require Dart v3.3 or higher.

## 4.9.2

* Update dependencies
* Resolved issue with transaction deserialization (unsigned tx)

## 4.9.1

* Resolved issue with transaction deserialization (Issue #9)

## 4.9.0

* Correct Bitcoin address network configuration.
* Resolve issue with Electrum fee estimation results.


## 4.8.0

* Update dependencies

## 4.7.0

* Update dependencies
Expand Down
47 changes: 32 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ Using this package, you can create a Bitcoin transaction in two ways: either thr

- BitcoinTransactionBuilder: Even with limited prior knowledge, you can utilize this class to send various types of transactions. Below, I've provided an example in which a transaction features 8 distinct input addresses with different types and private keys, as well as 10 different output addresses. Furthermore, additional examples have been prepared, which you can find in the [`example`](https://github.com/mrtnetwork/bitcoin_base/tree/main/example) folder.

### PSBT
Find example implementations [here](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/lib/psbt)

- BIP-0174: Partially Signed Bitcoin Transaction Format
- BIP-0370: PSBT Version 2
- BIP-0371: Taproot Fields for PSBT
- BIP-0373: MuSig2 PSBT Fields

### MuSig2 (BIP-327):
Find example implementations [here](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/lib/musig)

- Sign/Verify: Supports signing and verifying multisignature transactions using MuSig2
- NonceAgg: Aggregates nonces from multiple participants for secure signature generation.
- KeyAgg: Combines multiple public keys into a single aggregated public key for efficient multisignature verification



### Addresses

- P2PKH A P2PKH (Pay-to-Public-Key-Hash) address in Bitcoin represents ownership of a cryptocurrency wallet by encoding a hashed public key
Expand Down Expand Up @@ -124,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.
Expand Down Expand Up @@ -280,7 +297,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li
await ElectrumWebSocketService.connect("184....");

/// create provider with service
final provider = ElectrumApiProvider(service);
final provider = ElectrumProvider(service);

/// spender details
final privateKey = ECPrivate.fromHex(
Expand Down Expand Up @@ -310,7 +327,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li
for (final i in spenders) {
/// Reads all UTXOs (Unspent Transaction Outputs) associated with the account
final elctrumUtxos = await provider
.request(ElectrumScriptHashListUnspent(scriptHash: i.pubKeyHash()));
.request(ElectrumRequestScriptHashListUnspent(scriptHash: i.pubKeyHash()));

/// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details.
/// read spender utxos
Expand Down Expand Up @@ -415,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
Expand All @@ -427,7 +444,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li
final raw = transaction.serialize();

/// send to network
await provider.request(ElectrumBroadCastTransaction(transactionRaw: raw));
await provider.request(ElectrumRequestBroadCastTransaction(transactionRaw: raw));

/// Once completed, we verify the status by checking the mempool or using another explorer to review the transaction details.
/// https://mempool.space/testnet/tx/70cf664bba4b5ac9edc6133e9c6891ffaf8a55eaea9d2ac99aceead1c3db8899
Expand All @@ -446,7 +463,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li
"wss://chipnet.imaginary.cash:50004");

/// create provider with service
final provider = ElectrumApiProvider(service);
final provider = ElectrumProvider(service);

/// initialize private key
final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString(
Expand All @@ -470,7 +487,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li

/// Reads all UTXOs (Unspent Transaction Outputs) associated with the account.
/// We does not need tokens utxo and we set to false.
final elctrumUtxos = await provider.request(ElectrumScriptHashListUnspent(
final elctrumUtxos = await provider.request(ElectrumRequestScriptHashListUnspent(
scriptHash: p2pkhAddress.baseAddress.pubKeyHash(),
includeTokens: true,
));
Expand Down Expand Up @@ -552,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
Expand All @@ -566,7 +583,7 @@ In the [example](https://github.com/mrtnetwork/bitcoin_base/tree/main/example/li

/// send transaction to network
await provider
.request(ElectrumBroadCastTransaction(transactionRaw: transactionRaw));
.request(ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw));

/// done! check the transaction in block explorer
/// https://chipnet.imaginary.cash/tx/97030c1236a024de7cad7ceadf8571833029c508e016bcc8173146317e367ae6
Expand Down Expand Up @@ -628,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
Expand Down Expand Up @@ -704,7 +721,7 @@ I haven't implemented any specific HTTP service or socket service within this pl
await ElectrumSSLService.connect("testnet.aranguren.org:51002");

/// create provider with service
final provider = ElectrumApiProvider(service);
final provider = ElectrumProvider(service);

final address = P2trAddress.fromAddress(address: ".....", network: network);

Expand All @@ -714,15 +731,15 @@ I haven't implemented any specific HTTP service or socket service within this pl

/// Return an ordered list of UTXOs sent to a script hash.
final accountUnspend = await provider
.request(ElectrumScriptHashListUnspent(scriptHash: address.pubKeyHash()));
.request(ElectrumRequestScriptHashListUnspent(scriptHash: address.pubKeyHash()));

/// Return the confirmed and unconfirmed history of a script hash.
final accountHistory = await provider
.request(ElectrumScriptHashGetHistory(scriptHash: address.pubKeyHash()));

/// Broadcast a transaction to the network.
final broadcastTransaction = await provider
.request(ElectrumBroadCastTransaction(transactionRaw: "txDigest"));
.request(ElectrumRequestBroadCastTransaction(transactionRaw: "txDigest"));

/// ....
```
Expand Down
27 changes: 26 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
# include: package:lints/recommended.yaml
# 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:
# 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

18 changes: 9 additions & 9 deletions example/lib/bitcoin_cash/burn_token_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void main() async {
"wss://chipnet.imaginary.cash:50004");

/// create provider with service
final provider = ElectrumApiProvider(service);
final provider = ElectrumProvider(service);

/// initialize private key
final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString(
Expand All @@ -29,12 +29,13 @@ 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.
/// We does not need tokens utxo and we set to false.
final elctrumUtxos = await provider.request(ElectrumScriptHashListUnspent(
final elctrumUtxos =
await provider.request(ElectrumRequestScriptHashListUnspent(
scriptHash: p2pkhAddress.baseAddress.pubKeyHash(),
includeTokens: true,
));
Expand All @@ -60,8 +61,7 @@ void main() async {
if (sumOfUtxo == BigInt.zero) {
return;
}
// print(sumOfUtxo);
// return;

/// CashToken{bitfield: 16, commitment: null, amount: 2000, category: 4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430}
final CashToken token = elctrumUtxos
.firstWhere((e) =>
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -141,8 +141,8 @@ void main() async {
final transactionRaw = transaaction.toHex();

/// send transaction to network
await provider
.request(ElectrumBroadCastTransaction(transactionRaw: transactionRaw));
await provider.request(
ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw));

/// done! check the transaction in block explorer
/// https://chipnet.imaginary.cash/tx/d85da44ba0c12ab8b0f4c636ca5451ae2c3a90b0f6d9e47fe381d0f5c6966ff3
Expand Down
17 changes: 8 additions & 9 deletions example/lib/bitcoin_cash/create_cash_token_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void main() async {
"wss://chipnet.imaginary.cash:50004");

/// create provider with service
final provider = ElectrumApiProvider(service);
final provider = ElectrumProvider(service);

/// initialize private key
final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString(
Expand All @@ -25,11 +25,12 @@ 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.
final elctrumUtxos = await provider.request(ElectrumScriptHashListUnspent(
final elctrumUtxos =
await provider.request(ElectrumRequestScriptHashListUnspent(
scriptHash: p2pkhAddress.pubKeyHash(),
includeTokens: false,
));
Expand Down Expand Up @@ -63,10 +64,8 @@ void main() async {
/// if we dont have utxos with index 0 we must create them with some estimate transaction before create transaction
return;
}
// print("vout $vout0Hash");
// return;
final bchTransaction = ForkedTransactionBuilder(
outputs: [
outPuts: [
BitcoinTokenOutput(
address: p2pkhAddress,

Expand Down Expand Up @@ -122,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
Expand All @@ -135,8 +134,8 @@ void main() async {
final transactionRaw = transaaction.toHex();

/// send transaction to network
await provider
.request(ElectrumBroadCastTransaction(transactionRaw: transactionRaw));
await provider.request(
ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw));

/// done! check the transaction in block explorer
/// https://chipnet.imaginary.cash/tx/fe0f9f84bd8782b8037160c09a515d39a9cc5bbeda6dcca6fb8a89e952bc9dea
Expand Down
Loading