diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index f6c130c..cc6bc3f 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 696858f..ae87f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +## 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. diff --git a/README.md b/README.md index c0fc6c2..cf82413 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/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/lib/bitcoin_cash/burn_token_example.dart b/example/lib/bitcoin_cash/burn_token_example.dart index 23283aa..33c530a 100644 --- a/example/lib/bitcoin_cash/burn_token_example.dart +++ b/example/lib/bitcoin_cash/burn_token_example.dart @@ -1,148 +1,149 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// https://github.com/cashtokens/cashtokens void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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.toP2pkInP2sh(useBCHP2sh32: true)); - - // /// p2pkh with token address () - // final receiver1 = P2pkhAddress.fromHash160( - // h160: publicKey.toP2pkhAddress().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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), - // includeTokens: true, - // )); - // // return; - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) - // .toList() - - // /// we only filter the utxos for this token or none token utxos - // .where((element) => - // element.utxo.token?.category == - // "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" || - // element.utxo.token == null) - // .toList(); - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - - // /// CashToken{bitfield: 16, commitment: null, amount: 2000, category: 4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430} - // final CashToken token = elctrumUtxos - // .firstWhere((e) => - // e.token?.category == - // "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") - // .token!; - - // /// sum of ft token amounts with category "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" - // final sumofTokenUtxos = utxos - // .where((element) => - // element.utxo.token?.category == - // "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") - // .fold( - // BigInt.zero, - // (previousValue, element) => - // previousValue + element.utxo.token!.amount); - - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // /// change address for bch values (sum of bch amout - (outputs amount + fee)) - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.00002") + BtcUtils.toSatoshi("0.00003")), - // ), - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: receiver1, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - - // /// clone the token with new token amount for output1 (15 amount of category) - // token: token.copyWith(amount: BigInt.from(15))), - - // /// another change token value to change account like bch - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - - // /// clone the token with new token amount for change amount in this case we want to burn 20 token (output1 + 20) - // token: token.copyWith(amount: sumofTokenUtxos - BigInt.from(35))), - - // /// create burnable output for this category - // /// for nft token you should use utxoHash - // BitcoinBurnableOutput( - // categoryID: token.category, value: BigInt.from(20), utxoHash: null) - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - - // /// Bitcoin Cash Metadata Registries - // /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata - // /// we does not create metadata for this token - // memo: null, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/d85da44ba0c12ab8b0f4c636ca5451ae2c3a90b0f6d9e47fe381d0f5c6966ff3 + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.toP2pkInP2sh(useBCHP2sh32: true)); + + /// p2pkh with token address () + final receiver1 = P2pkhAddress.fromHash160( + 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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), + includeTokens: true, + )); + // return; + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) + .toList() + + /// we only filter the utxos for this token or none token utxos + .where((element) => + element.utxo.token?.category == + "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" || + element.utxo.token == null) + .toList(); + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + + /// CashToken{bitfield: 16, commitment: null, amount: 2000, category: 4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430} + final CashToken token = elctrumUtxos + .firstWhere((e) => + e.token?.category == + "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") + .token!; + + /// sum of ft token amounts with category "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" + final sumofTokenUtxos = utxos + .where((element) => + element.utxo.token?.category == + "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") + .fold( + BigInt.zero, + (previousValue, element) => + previousValue + element.utxo.token!.amount); + + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + /// change address for bch values (sum of bch amout - (outputs amount + fee)) + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.00002") + BtcUtils.toSatoshi("0.00003")), + ), + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: receiver1, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + + /// clone the token with new token amount for output1 (15 amount of category) + token: token.copyWith(amount: BigInt.from(15))), + + /// another change token value to change account like bch + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + + /// clone the token with new token amount for change amount in this case we want to burn 20 token (output1 + 20) + token: token.copyWith(amount: sumofTokenUtxos - BigInt.from(35))), + + /// create burnable output for this category + /// for nft token you should use utxoHash + BitcoinBurnableOutput( + categoryID: token.category, value: BigInt.from(20), utxoHash: null) + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + + /// Bitcoin Cash Metadata Registries + /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata + /// we does not create metadata for this token + memo: null, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/d85da44ba0c12ab8b0f4c636ca5451ae2c3a90b0f6d9e47fe381d0f5c6966ff3 } diff --git a/example/lib/bitcoin_cash/create_cash_token_example.dart b/example/lib/bitcoin_cash/create_cash_token_example.dart index c2a425b..a93e635 100644 --- a/example/lib/bitcoin_cash/create_cash_token_example.dart +++ b/example/lib/bitcoin_cash/create_cash_token_example.dart @@ -1,141 +1,142 @@ -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// please make sure read this before create transaction on mainnet /// https://github.com/cashtokens/cashtokens void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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(); - - // /// 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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.pubKeyHash(), - // includeTokens: false, - // )); - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress))) - // .toList(); - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - - // // return; - - // /// Every token category ID is a transaction ID: - // /// the ID must be selected from the inputs of its genesis transaction, - // /// and only token genesis inputs – inputs which spend output 0 of their - // /// parent transaction – are eligible - // /// (i.e. outpoint transaction hashes of inputs with an outpoint index of 0). - // /// As such, implementations can locate the genesis transaction of any category - // /// by identifying the transaction that spent the 0th output of the transaction referenced by the category ID. - // String? vout0Hash; - // try { - // // Retrieve the transaction hash of the 0th output UTXO - // vout0Hash = - // utxos.firstWhere((element) => element.utxo.vout == 0).utxo.txHash; - // } on StateError { - // /// if we dont have utxos with index 0 we must create them with some estimate transaction before create transaction - // return; - // } - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // BitcoinTokenOutput( - // address: p2pkhAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - // token: CashToken( - // category: vout0Hash, - - // /// The commitment contents of the NFT held in this output (0 to 40 bytes). T - // /// his field is omitted if no NFT is present. In this case, it is null as it is not an NFT. - // commitment: null, - - // /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). - // /// This field is omitted if no fungible tokens are present. - // amount: BigInt.from(800000000000000000), - // bitfield: CashTokenUtils.buildBitfield( - // hasAmount: true, - - // /// nfts field - // capability: null, - // hasCommitmentLength: false, - // hasNFT: false))), - - // /// change address- back amount to account exclude fee and token input value - // BitcoinOutput( - // address: p2pkhAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.00001") + BtcUtils.toSatoshi("0.00003")), - // ), - - // /// add token meta data to transaction - // /// see https://cashtokens.org/docs/bcmr/chip/ how to create BCMR - // /// also you can use Registery class for create BCMR - // BCMR( - // uris: [ - // "ipfs://bafkreihfrxykireezlcp2jstp7cjwx5nl7of3nuul3qnubxygwvwcjun44" - // ], - // hash: - // "e58df0a44484cac4fd26537fc49b5fad5fdc5db6945ee0da06f835ab61268de7") - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - - // /// Bitcoin Cash Metadata Registries - // /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata - // /// we does not create metadata for this token - // memo: null, - // utxos: utxos, - - // /// disable ordering - // outputOrdering: BitcoinOrdering.none); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/fe0f9f84bd8782b8037160c09a515d39a9cc5bbeda6dcca6fb8a89e952bc9dea + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.pubKeyHash(), + includeTokens: false, + )); + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress))) + .toList(); + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + + // return; + + /// Every token category ID is a transaction ID: + /// the ID must be selected from the inputs of its genesis transaction, + /// and only token genesis inputs – inputs which spend output 0 of their + /// parent transaction – are eligible + /// (i.e. outpoint transaction hashes of inputs with an outpoint index of 0). + /// As such, implementations can locate the genesis transaction of any category + /// by identifying the transaction that spent the 0th output of the transaction referenced by the category ID. + String? vout0Hash; + try { + // Retrieve the transaction hash of the 0th output UTXO + vout0Hash = + utxos.firstWhere((element) => element.utxo.vout == 0).utxo.txHash; + } on StateError { + /// if we dont have utxos with index 0 we must create them with some estimate transaction before create transaction + return; + } + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + BitcoinTokenOutput( + address: p2pkhAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + token: CashToken( + category: vout0Hash, + + /// The commitment contents of the NFT held in this output (0 to 40 bytes). T + /// his field is omitted if no NFT is present. In this case, it is null as it is not an NFT. + commitment: null, + + /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). + /// This field is omitted if no fungible tokens are present. + amount: BigInt.from(800000000000000000), + bitfield: CashTokenUtils.buildBitfield( + hasAmount: true, + + /// nfts field + capability: null, + hasCommitmentLength: false, + hasNFT: false))), + + /// change address- back amount to account exclude fee and token input value + BitcoinOutput( + address: p2pkhAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.00001") + BtcUtils.toSatoshi("0.00003")), + ), + + /// add token meta data to transaction + /// see https://cashtokens.org/docs/bcmr/chip/ how to create BCMR + /// also you can use Registery class for create BCMR + BCMR( + uris: [ + "ipfs://bafkreihfrxykireezlcp2jstp7cjwx5nl7of3nuul3qnubxygwvwcjun44" + ], + hash: + "e58df0a44484cac4fd26537fc49b5fad5fdc5db6945ee0da06f835ab61268de7") + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + + /// Bitcoin Cash Metadata Registries + /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata + /// we does not create metadata for this token + memo: null, + utxos: utxos, + + /// disable ordering + outputOrdering: BitcoinOrdering.none); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/fe0f9f84bd8782b8037160c09a515d39a9cc5bbeda6dcca6fb8a89e952bc9dea } diff --git a/example/lib/bitcoin_cash/create_nft_example.dart b/example/lib/bitcoin_cash/create_nft_example.dart index a1e912d..42e3b5a 100644 --- a/example/lib/bitcoin_cash/create_nft_example.dart +++ b/example/lib/bitcoin_cash/create_nft_example.dart @@ -1,129 +1,130 @@ -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// please make sure read this before create transaction on mainnet //// https://github.com/cashtokens/cashtokens void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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()); - - // /// 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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), - // includeTokens: false, - // )); - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) - // .toList(); - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - - // /// Every token category ID is a transaction ID: - // /// the ID must be selected from the inputs of its genesis transaction, - // /// and only token genesis inputs – inputs which spend output 0 of their - // /// parent transaction – are eligible - // /// (i.e. outpoint transaction hashes of inputs with an outpoint index of 0). - // /// As such, implementations can locate the genesis transaction of any category - // /// by identifying the transaction that spent the 0th output of the transaction referenced by the category ID. - // String? vout0Hash; - // try { - // vout0Hash = - // utxos.firstWhere((element) => element.utxo.vout == 0).utxo.txHash; - // } on StateError { - // /// if we dont have utxos with index 0 we must create them with some estimate transaction before create transaction - // return; - // } - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.00001") + BtcUtils.toSatoshi("0.00003")), - // ), - // BitcoinTokenOutput( - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - // token: CashToken( - // category: vout0Hash, - - // /// The commitment contents of the NFT held in this output (0 to 40 bytes). T - // /// his field is omitted if no NFT is present - // commitment: null, - - // /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). - // /// This field is omitted if no fungible tokens are present. - // amount: null, - // bitfield: CashTokenUtils.buildBitfield( - // hasAmount: false, - - // /// nfts field - // /// mintable nft - // capability: CashTokenCapability.minting, - // hasCommitmentLength: false, - // hasNFT: true))), - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - - // /// Bitcoin Cash Metadata Registries - // /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata - // /// we does not create metadata for this token - // memo: null, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/4e153029c75963f39920184233756f8f55d5a8f86e01cbdaf0340320c814e25e + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), + includeTokens: false, + )); + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) + .toList(); + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + + /// Every token category ID is a transaction ID: + /// the ID must be selected from the inputs of its genesis transaction, + /// and only token genesis inputs – inputs which spend output 0 of their + /// parent transaction – are eligible + /// (i.e. outpoint transaction hashes of inputs with an outpoint index of 0). + /// As such, implementations can locate the genesis transaction of any category + /// by identifying the transaction that spent the 0th output of the transaction referenced by the category ID. + String? vout0Hash; + try { + vout0Hash = + utxos.firstWhere((element) => element.utxo.vout == 0).utxo.txHash; + } on StateError { + /// if we dont have utxos with index 0 we must create them with some estimate transaction before create transaction + return; + } + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.00001") + BtcUtils.toSatoshi("0.00003")), + ), + BitcoinTokenOutput( + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + token: CashToken( + category: vout0Hash, + + /// The commitment contents of the NFT held in this output (0 to 40 bytes). T + /// his field is omitted if no NFT is present + commitment: null, + + /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). + /// This field is omitted if no fungible tokens are present. + amount: null, + bitfield: CashTokenUtils.buildBitfield( + hasAmount: false, + + /// nfts field + /// mintable nft + capability: CashTokenCapability.minting, + hasCommitmentLength: false, + hasNFT: true))), + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + + /// Bitcoin Cash Metadata Registries + /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata + /// we does not create metadata for this token + memo: null, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/4e153029c75963f39920184233756f8f55d5a8f86e01cbdaf0340320c814e25e } diff --git a/example/lib/bitcoin_cash/make_vout0_example.dart b/example/lib/bitcoin_cash/make_vout0_example.dart index 5a9ba62..e48c169 100644 --- a/example/lib/bitcoin_cash/make_vout0_example.dart +++ b/example/lib/bitcoin_cash/make_vout0_example.dart @@ -1,5 +1,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_ssl_service.dart'; /// make vout 0 for account for create token hash /// estimate transaction to your self with input 0 @@ -7,14 +8,15 @@ import 'package:blockchain_utils/blockchain_utils.dart'; void main() async { /// connect to electrum service with ssl /// please see `services_examples` folder for how to create electrum ssl service - final service = ElectrumSSLService.connect(Uri.parse("tcp://chipnet.imaginary.cash:50002")); + final service = + await ElectrumSSLService.connect("chipnet.imaginary.cash:50002"); /// create provider with service - final provider = await ElectrumProvider.connect(service); + final provider = ElectrumProvider(service); /// initialize private key - final privateKey = ECPrivate.fromBytes( - BytesUtils.fromHexString("f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); /// public key final publicKey = privateKey.getPublic(); @@ -24,11 +26,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(ElectrumRequestScriptHashListUnspent( + final elctrumUtxos = + await provider.request(ElectrumRequestScriptHashListUnspent( scriptHash: p2pkhAddress.pubKeyHash(), includeTokens: false, )); @@ -37,14 +40,15 @@ void main() async { final List utxos = elctrumUtxos .map((e) => UtxoWithAddress( utxo: e.toUtxo(p2pkhAddress.type), - ownerDetails: UtxoAddressDetails(publicKey: publicKey.toHex(), address: p2pkhAddress))) + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress))) .toList(); /// som of utxos in satoshi final sumOfUtxo = utxos.sumOfUtxosValue(); final bchTransaction = ForkedTransactionBuilder( - outputs: [ + outPuts: [ BitcoinOutput( address: p2pkhAddress, value: sumOfUtxo - BtcUtils.toSatoshi("0.00003"), @@ -59,8 +63,9 @@ void main() async { memo: null, utxos: utxos, ); - final transaaction = bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - return privateKey.signInput(trDigest, sigHash: sighash); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); }); /// transaction ID @@ -73,7 +78,8 @@ void main() async { final transactionRaw = transaaction.toHex(); /// send transaction to network - await provider.request(ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); /// done! check the transaction in block explorer /// https://chipnet.imaginary.cash/tx/b20d4c13fe67adc2f73aee0161eb51c7e813643ddc8eb655c6bd9ae72b7562cb diff --git a/example/lib/bitcoin_cash/minting_nft_example.dart b/example/lib/bitcoin_cash/minting_nft_example.dart index 3ce9d5a..eb7d6fc 100644 --- a/example/lib/bitcoin_cash/minting_nft_example.dart +++ b/example/lib/bitcoin_cash/minting_nft_example.dart @@ -1,185 +1,186 @@ -// import 'dart:convert'; -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'dart:convert'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// https://github.com/cashtokens/cashtokens void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = - // await ElectrumWebSocketService.connect("wss://tbch4.loping.net:62004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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.toP2pkInP2sh(useBCHP2sh32: true)); - - // /// 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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), - // includeTokens: true, - // )); - // // return; - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) - // .toList(); - // // return; - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - // const String nftCategoryId = - // "3f0d87791e5996aaddbce16c12651dd8b5b881cf7338340504bb7b2c6c08bfc4"; - - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.00004") + BtcUtils.toSatoshi("0.00003")), - // ), - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - // token: CashToken( - // category: nftCategoryId, - - // /// The commitment contents of the NFT held in this output (0 to 40 bytes). T - // /// his field is omitted if no NFT is present - // commitment: utf8.encode("github.com/mrtnetwork"), - - // /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). - // /// This field is omitted if no fungible tokens are present. - // amount: BigInt.from(160000000) ~/ BigInt.from(4), - // bitfield: CashTokenUtils.buildBitfield( - // hasAmount: true, - // capability: CashTokenCapability.minting, - // hasCommitmentLength: true, - // hasNFT: true))), - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - // token: CashToken( - // category: nftCategoryId, - - // /// The commitment contents of the NFT held in this output (0 to 40 bytes). T - // /// his field is omitted if no NFT is present - // commitment: utf8.encode("github.com/mrtnetwork"), - - // /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). - // /// This field is omitted if no fungible tokens are present. - // amount: BigInt.from(160000000) ~/ BigInt.from(4), - // bitfield: CashTokenUtils.buildBitfield( - // hasAmount: true, - // capability: CashTokenCapability.mutable, - // hasCommitmentLength: true, - // hasNFT: true))), - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - // token: CashToken( - // category: nftCategoryId, - - // /// The commitment contents of the NFT held in this output (0 to 40 bytes). T - // /// his field is omitted if no NFT is present - // commitment: utf8.encode("github.com/mrtnetwork"), - - // /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). - // /// This field is omitted if no fungible tokens are present. - // amount: BigInt.from(160000000) ~/ BigInt.from(4), - // bitfield: CashTokenUtils.buildBitfield( - // hasAmount: true, - // capability: CashTokenCapability.mutable, - // hasCommitmentLength: true, - // hasNFT: true))), - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - // token: CashToken( - // category: nftCategoryId, - - // /// The commitment contents of the NFT held in this output (0 to 40 bytes). T - // /// his field is omitted if no NFT is present - // commitment: utf8.encode("github.com/mrtnetwork"), - - // /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). - // /// This field is omitted if no fungible tokens are present. - // amount: BigInt.from(160000000) ~/ BigInt.from(4), - // bitfield: CashTokenUtils.buildBitfield( - // hasAmount: true, - // capability: CashTokenCapability.mutable, - // hasCommitmentLength: true, - // hasNFT: true))), - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - - // /// Bitcoin Cash Metadata Registries - // /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata - // /// we does not create metadata for this token - // memo: null, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://tbch4.loping.net/tx/caa91b0fea2843a99c3cd7375ac4d3102b6b74a25e52cd866ad7ecc486204f0d + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = + await ElectrumWebSocketService.connect("wss://tbch4.loping.net:62004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.toP2pkInP2sh(useBCHP2sh32: true)); + + /// 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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), + includeTokens: true, + )); + // return; + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) + .toList(); + // return; + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + const String nftCategoryId = + "3f0d87791e5996aaddbce16c12651dd8b5b881cf7338340504bb7b2c6c08bfc4"; + + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.00004") + BtcUtils.toSatoshi("0.00003")), + ), + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + token: CashToken( + category: nftCategoryId, + + /// The commitment contents of the NFT held in this output (0 to 40 bytes). T + /// his field is omitted if no NFT is present + commitment: utf8.encode("github.com/mrtnetwork"), + + /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). + /// This field is omitted if no fungible tokens are present. + amount: BigInt.from(160000000) ~/ BigInt.from(4), + bitfield: CashTokenUtils.buildBitfield( + hasAmount: true, + capability: CashTokenCapability.minting, + hasCommitmentLength: true, + hasNFT: true))), + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + token: CashToken( + category: nftCategoryId, + + /// The commitment contents of the NFT held in this output (0 to 40 bytes). T + /// his field is omitted if no NFT is present + commitment: utf8.encode("github.com/mrtnetwork"), + + /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). + /// This field is omitted if no fungible tokens are present. + amount: BigInt.from(160000000) ~/ BigInt.from(4), + bitfield: CashTokenUtils.buildBitfield( + hasAmount: true, + capability: CashTokenCapability.mutable, + hasCommitmentLength: true, + hasNFT: true))), + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + token: CashToken( + category: nftCategoryId, + + /// The commitment contents of the NFT held in this output (0 to 40 bytes). T + /// his field is omitted if no NFT is present + commitment: utf8.encode("github.com/mrtnetwork"), + + /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). + /// This field is omitted if no fungible tokens are present. + amount: BigInt.from(160000000) ~/ BigInt.from(4), + bitfield: CashTokenUtils.buildBitfield( + hasAmount: true, + capability: CashTokenCapability.mutable, + hasCommitmentLength: true, + hasNFT: true))), + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + token: CashToken( + category: nftCategoryId, + + /// The commitment contents of the NFT held in this output (0 to 40 bytes). T + /// his field is omitted if no NFT is present + commitment: utf8.encode("github.com/mrtnetwork"), + + /// The number of fungible tokens held in this output (an integer between 1 and 9223372036854775807). + /// This field is omitted if no fungible tokens are present. + amount: BigInt.from(160000000) ~/ BigInt.from(4), + bitfield: CashTokenUtils.buildBitfield( + hasAmount: true, + capability: CashTokenCapability.mutable, + hasCommitmentLength: true, + hasNFT: true))), + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + + /// Bitcoin Cash Metadata Registries + /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata + /// we does not create metadata for this token + memo: null, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://tbch4.loping.net/tx/caa91b0fea2843a99c3cd7375ac4d3102b6b74a25e52cd866ad7ecc486204f0d } diff --git a/example/lib/bitcoin_cash/old_examples/old_example.dart b/example/lib/bitcoin_cash/old_examples/old_example.dart index 7f534ce..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 @@ -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(); @@ -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 @@ -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 18691af..93cfc27 100644 --- a/example/lib/bitcoin_cash/p2sh32_spend_example.dart +++ b/example/lib/bitcoin_cash/p2sh32_spend_example.dart @@ -1,118 +1,119 @@ -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// CHIP-2022-05 Pay-to-Script-Hash-32 (P2SH32) for Bitcoin Cash /// https://bitcoincashresearch.org/t/chip-2022-05-pay-to-script-hash-32-p2sh32-for-bitcoin-cash/806 /// Send funds to a BCH P2SH32 address. void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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()); - - // /// Initialize two P2SH32 addresses for receiving funds. - // /// bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7 - // /// Avoid using `BitcoinCashAddress('address')` when obtaining the address type for `UtxoWithAddress`. - // /// This is crucial for spending, as the current P2SH type is needed for unlocking the script. - // final p2sh32Example1 = BitcoinCashAddress.fromBaseAddress( - // publicKey.toP2pkhInP2sh(useBCHP2sh32: true)); - - // /// bchtest:pw054wtjjc70rrvx4ftl4p63gluedyt0qmpgz705f8x3gxygrzarzls7vp2sj - // /// Avoid using `BitcoinCashAddress('address')` when obtaining the address type for `UtxoWithAddress`. - // /// This is crucial for spending, as the current P2SH type is needed for unlocking the script. - // final p2sh32Example2 = BitcoinCashAddress.fromBaseAddress( - // publicKey.toP2pkInP2sh(useBCHP2sh32: true)); - - // /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. - // /// We does not need tokens utxo and we set to false. - // final example1ElectrumUtxos = - // await provider.request(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2sh32Example1.baseAddress.pubKeyHash(), - // includeTokens: false, - // )); - // final example2ElectrumUtxos = - // await provider.request(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2sh32Example2.baseAddress.pubKeyHash(), - // includeTokens: false, - // )); - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = [ - // ...example2ElectrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2sh32Example2.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), - // address: p2pkhAddress.baseAddress))) - // .toList(), - // ...example1ElectrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2sh32Example1.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), - // address: p2pkhAddress.baseAddress))) - // .toList() - // ]; - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: BtcUtils.toSatoshi("0.00001"), - // ), - - // /// change input (sumofutxos - spend) - // BitcoinOutput( - // address: p2sh32Example1.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.00001") + BtcUtils.toSatoshi("0.00003")), - // ), - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/b76b851ce0374504591db414d7469aadb68649079defb26e44c62e970afda729 + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.toAddress()); + + /// Initialize two P2SH32 addresses for receiving funds. + /// bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7 + /// Avoid using `BitcoinCashAddress('address')` when obtaining the address type for `UtxoWithAddress`. + /// This is crucial for spending, as the current P2SH type is needed for unlocking the script. + final p2sh32Example1 = BitcoinCashAddress.fromBaseAddress( + publicKey.toP2pkhInP2sh(useBCHP2sh32: true)); + + /// bchtest:pw054wtjjc70rrvx4ftl4p63gluedyt0qmpgz705f8x3gxygrzarzls7vp2sj + /// Avoid using `BitcoinCashAddress('address')` when obtaining the address type for `UtxoWithAddress`. + /// This is crucial for spending, as the current P2SH type is needed for unlocking the script. + final p2sh32Example2 = BitcoinCashAddress.fromBaseAddress( + publicKey.toP2pkInP2sh(useBCHP2sh32: true)); + + /// Reads all UTXOs (Unspent Transaction outputs) associated with the account. + /// We does not need tokens utxo and we set to false. + final example1ElectrumUtxos = + await provider.request(ElectrumRequestScriptHashListUnspent( + scriptHash: p2sh32Example1.baseAddress.pubKeyHash(), + includeTokens: false, + )); + final example2ElectrumUtxos = + await provider.request(ElectrumRequestScriptHashListUnspent( + scriptHash: p2sh32Example2.baseAddress.pubKeyHash(), + includeTokens: false, + )); + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = [ + ...example2ElectrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2sh32Example2.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), + address: p2pkhAddress.baseAddress))) + .toList(), + ...example1ElectrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2sh32Example1.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), + address: p2pkhAddress.baseAddress))) + .toList() + ]; + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: BtcUtils.toSatoshi("0.00001"), + ), + + /// change input (sumofutxos - spend) + BitcoinOutput( + address: p2sh32Example1.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.00001") + BtcUtils.toSatoshi("0.00003")), + ), + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/b76b851ce0374504591db414d7469aadb68649079defb26e44c62e970afda729 } diff --git a/example/lib/bitcoin_cash/send_ft_token_example.dart b/example/lib/bitcoin_cash/send_ft_token_example.dart index d405c18..a3acb9a 100644 --- a/example/lib/bitcoin_cash/send_ft_token_example.dart +++ b/example/lib/bitcoin_cash/send_ft_token_example.dart @@ -1,144 +1,145 @@ -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// please make sure read this before create transaction on mainnet //// https://github.com/cashtokens/cashtokens void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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.toP2pkInP2sh(useBCHP2sh32: true)); - - // /// p2pkh with token address () - // final receiver1 = P2pkhAddress.fromHash160( - // h160: publicKey.toP2pkhAddress().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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), - // includeTokens: true, - // )); - // // return; - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) - // .toList() - - // /// we only filter the utxos for this token or none token utxos - // .where((element) => - // element.utxo.token?.category == - // "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" || - // element.utxo.token == null) - // .toList(); - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - - // /// CashToken{bitfield: 16, commitment: null, amount: 2000, category: 4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430} - // final CashToken token = elctrumUtxos - // .firstWhere((e) => - // e.token?.category == - // "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") - // .token!; - - // /// sum of ft token amounts with category "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" - // final sumofTokenUtxos = utxos - // .where((element) => - // element.utxo.token?.category == - // "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") - // .fold( - // BigInt.zero, - // (previousValue, element) => - // previousValue + element.utxo.token!.amount); - - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // /// change address for bch values (sum of bch amout - (outputs amount + fee)) - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.00002") + BtcUtils.toSatoshi("0.00003")), - // ), - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: receiver1, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - - // /// clone the token with new token amount for output1 (15 amount of category) - // token: token.copyWith(amount: BigInt.from(15))), - - // /// another change token value to change account like bch - // BitcoinTokenOutput( - // utxoHash: utxos.first.utxo.txHash, - // address: p2pkhAddress.baseAddress, - - // /// for a token-bearing output (600-700) satoshi - // /// hard-coded value which is expected to be enough to allow - // /// all conceivable token-bearing UTXOs (1000 satoshi) - // value: BtcUtils.toSatoshi("0.00001"), - - // /// clone the token with new token amount for output1 (15 amount of category) - // token: token.copyWith(amount: sumofTokenUtxos - BigInt.from(15))), - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - - // /// Bitcoin Cash Metadata Registries - // /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata - // /// we does not create metadata for this token - // memo: null, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/97030c1236a024de7cad7ceadf8571833029c508e016bcc8173146317e367ae6 + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.toP2pkInP2sh(useBCHP2sh32: true)); + + /// p2pkh with token address () + final receiver1 = P2pkhAddress.fromHash160( + 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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), + includeTokens: true, + )); + // return; + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) + .toList() + + /// we only filter the utxos for this token or none token utxos + .where((element) => + element.utxo.token?.category == + "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" || + element.utxo.token == null) + .toList(); + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + + /// CashToken{bitfield: 16, commitment: null, amount: 2000, category: 4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430} + final CashToken token = elctrumUtxos + .firstWhere((e) => + e.token?.category == + "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") + .token!; + + /// sum of ft token amounts with category "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430" + final sumofTokenUtxos = utxos + .where((element) => + element.utxo.token?.category == + "4e7873d4529edfd2c6459139257042950230baa9297f111b8675829443f70430") + .fold( + BigInt.zero, + (previousValue, element) => + previousValue + element.utxo.token!.amount); + + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + /// change address for bch values (sum of bch amout - (outputs amount + fee)) + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.00002") + BtcUtils.toSatoshi("0.00003")), + ), + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: receiver1, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + + /// clone the token with new token amount for output1 (15 amount of category) + token: token.copyWith(amount: BigInt.from(15))), + + /// another change token value to change account like bch + BitcoinTokenOutput( + utxoHash: utxos.first.utxo.txHash, + address: p2pkhAddress.baseAddress, + + /// for a token-bearing output (600-700) satoshi + /// hard-coded value which is expected to be enough to allow + /// all conceivable token-bearing UTXOs (1000 satoshi) + value: BtcUtils.toSatoshi("0.00001"), + + /// clone the token with new token amount for output1 (15 amount of category) + token: token.copyWith(amount: sumofTokenUtxos - BigInt.from(15))), + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + + /// Bitcoin Cash Metadata Registries + /// pleas see https://cashtokens.org/docs/bcmr/chip/ for how to create cash metadata + /// we does not create metadata for this token + memo: null, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/97030c1236a024de7cad7ceadf8571833029c508e016bcc8173146317e367ae6 } diff --git a/example/lib/bitcoin_cash/transfer_bch_example.dart b/example/lib/bitcoin_cash/transfer_bch_example.dart index 6b4cbf3..5df0ab5 100644 --- a/example/lib/bitcoin_cash/transfer_bch_example.dart +++ b/example/lib/bitcoin_cash/transfer_bch_example.dart @@ -1,104 +1,105 @@ -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; /// CHIP-2022-05 Pay-to-Script-Hash-32 (P2SH32) for Bitcoin Cash /// https://bitcoincashresearch.org/t/chip-2022-05-pay-to-script-hash-32-p2sh32-for-bitcoin-cash/806 /// Send funds to a BCH P2SH32 address. void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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()); - - // /// Initialize two P2SH32 addresses for receiving funds. - // final p2sh32Example1 = BitcoinCashAddress( - // "bchtest:pw054wtjjc70rrvx4ftl4p63gluedyt0qmpgz705f8x3gxygrzarzls7vp2sj", - // network: network); - // final p2sh32Example2 = BitcoinCashAddress( - // "bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7", - // network: network); - - // /// 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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), - // includeTokens: false, - // )); - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) - // .toList(); - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // /// change input (sumofutxos - spend) - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.0001") + - // BtcUtils.toSatoshi("0.0001") + - // BtcUtils.toSatoshi("0.00003")), - // ), - // BitcoinOutput( - // address: p2sh32Example1.baseAddress, - // value: BtcUtils.toSatoshi("0.0001"), - // ), - // BitcoinOutput( - // address: p2sh32Example2.baseAddress, - // value: BtcUtils.toSatoshi("0.0001"), - // ), - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/9e534f8a64f76b1af5ccf2522392697f2242fd215206a458cfe286bca4a3ec0a + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.toAddress()); + + /// Initialize two P2SH32 addresses for receiving funds. + final p2sh32Example1 = BitcoinCashAddress( + "bchtest:pw054wtjjc70rrvx4ftl4p63gluedyt0qmpgz705f8x3gxygrzarzls7vp2sj", + network: network); + final p2sh32Example2 = BitcoinCashAddress( + "bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7", + network: network); + + /// 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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), + includeTokens: false, + )); + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) + .toList(); + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + /// change input (sumofutxos - spend) + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.0001") + + BtcUtils.toSatoshi("0.0001") + + BtcUtils.toSatoshi("0.00003")), + ), + BitcoinOutput( + address: p2sh32Example1.baseAddress, + value: BtcUtils.toSatoshi("0.0001"), + ), + BitcoinOutput( + address: p2sh32Example2.baseAddress, + value: BtcUtils.toSatoshi("0.0001"), + ), + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/9e534f8a64f76b1af5ccf2522392697f2242fd215206a458cfe286bca4a3ec0a } diff --git a/example/lib/global/bch_example.dart b/example/lib/global/bch_example.dart index 277282b..7ab8975 100644 --- a/example/lib/global/bch_example.dart +++ b/example/lib/global/bch_example.dart @@ -1,101 +1,102 @@ -// import 'package:bitcoin_base/bitcoin_base.dart'; -// import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/electrum/electrum_websocket_service.dart'; void main() async { - // /// connect to electrum service with websocket - // /// please see `services_examples` folder for how to create electrum websocket service - // final service = await ElectrumWebSocketService.connect( - // "wss://chipnet.imaginary.cash:50004"); - - // /// create provider with service - // final provider = ElectrumProvider(service); - - // /// initialize private key - // final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( - // "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); - - // /// public key - // final publicKey = privateKey.getPublic(); - - // /// network - // const network = BitcoinCashNetwork.testnet; - - // /// 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()); - - // /// Initialize two P2SH32 addresses for receiving funds. - // final p2sh32Example1 = BitcoinCashAddress( - // "bchtest:pw054wtjjc70rrvx4ftl4p63gluedyt0qmpgz705f8x3gxygrzarzls7vp2sj", - // network: network); - // final p2sh32Example2 = BitcoinCashAddress( - // "bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7", - // network: network); - - // /// 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(ElectrumRequestScriptHashListUnspent( - // scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), - // includeTokens: false, - // )); - - // /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. - // final List utxos = elctrumUtxos - // .map((e) => UtxoWithAddress( - // utxo: e.toUtxo(p2pkhAddress.type), - // ownerDetails: UtxoAddressDetails( - // publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) - // .toList(); - - // /// som of utxos in satoshi - // final sumOfUtxo = utxos.sumOfUtxosValue(); - // if (sumOfUtxo == BigInt.zero) { - // return; - // } - - // final bchTransaction = ForkedTransactionBuilder( - // outputs: [ - // /// change input (sumofutxos - spend) - // BitcoinOutput( - // address: p2pkhAddress.baseAddress, - // value: sumOfUtxo - - // (BtcUtils.toSatoshi("0.0001") + - // BtcUtils.toSatoshi("0.0001") + - // BtcUtils.toSatoshi("0.00003")), - // ), - // BitcoinOutput( - // address: p2sh32Example1.baseAddress, - // value: BtcUtils.toSatoshi("0.0001"), - // ), - // BitcoinOutput( - // address: p2sh32Example2.baseAddress, - // value: BtcUtils.toSatoshi("0.0001"), - // ), - // ], - // fee: BtcUtils.toSatoshi("0.00003"), - // network: network, - // utxos: utxos, - // ); - // final transaaction = - // bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { - // return privateKey.signInput(trDigest, sigHash: sighash); - // }); - - // /// transaction ID - // transaaction.txId(); - - // /// for calculation fee - // transaaction.getSize(); - - // /// raw of encoded transaction in hex - // final transactionRaw = transaaction.toHex(); - - // /// send transaction to network - // await provider.request( - // ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); - - // /// done! check the transaction in block explorer - // /// https://chipnet.imaginary.cash/tx/9e534f8a64f76b1af5ccf2522392697f2242fd215206a458cfe286bca4a3ec0a + /// connect to electrum service with websocket + /// please see `services_examples` folder for how to create electrum websocket service + final service = await ElectrumWebSocketService.connect( + "wss://chipnet.imaginary.cash:50004"); + + /// create provider with service + final provider = ElectrumProvider(service); + + /// initialize private key + final privateKey = ECPrivate.fromBytes(BytesUtils.fromHexString( + "f9061c5cb343c6b6a73900ee29509bb0bd2213319eea46d2f2a431068c9da06b")); + + /// public key + final publicKey = privateKey.getPublic(); + + /// network + const network = BitcoinCashNetwork.testnet; + + /// 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.toAddress()); + + /// Initialize two P2SH32 addresses for receiving funds. + final p2sh32Example1 = BitcoinCashAddress( + "bchtest:pw054wtjjc70rrvx4ftl4p63gluedyt0qmpgz705f8x3gxygrzarzls7vp2sj", + network: network); + final p2sh32Example2 = BitcoinCashAddress( + "bchtest:pvw39llgap0a4vm8jn9sjsvfsthah4wgemjlh6epdtzr3pl2fqtmsn3s4vcm7", + network: network); + + /// 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(ElectrumRequestScriptHashListUnspent( + scriptHash: p2pkhAddress.baseAddress.pubKeyHash(), + includeTokens: false, + )); + + /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. + final List utxos = elctrumUtxos + .map((e) => UtxoWithAddress( + utxo: e.toUtxo(p2pkhAddress.type), + ownerDetails: UtxoAddressDetails( + publicKey: publicKey.toHex(), address: p2pkhAddress.baseAddress))) + .toList(); + + /// som of utxos in satoshi + final sumOfUtxo = utxos.sumOfUtxosValue(); + if (sumOfUtxo == BigInt.zero) { + return; + } + + final bchTransaction = ForkedTransactionBuilder( + outPuts: [ + /// change input (sumofutxos - spend) + BitcoinOutput( + address: p2pkhAddress.baseAddress, + value: sumOfUtxo - + (BtcUtils.toSatoshi("0.0001") + + BtcUtils.toSatoshi("0.0001") + + BtcUtils.toSatoshi("0.00003")), + ), + BitcoinOutput( + address: p2sh32Example1.baseAddress, + value: BtcUtils.toSatoshi("0.0001"), + ), + BitcoinOutput( + address: p2sh32Example2.baseAddress, + value: BtcUtils.toSatoshi("0.0001"), + ), + ], + fee: BtcUtils.toSatoshi("0.00003"), + network: network, + utxos: utxos, + ); + final transaaction = + bchTransaction.buildTransaction((trDigest, utxo, publicKey, sighash) { + return privateKey.signECDSA(trDigest, sighash: sighash); + }); + + /// transaction ID + transaaction.txId(); + + /// for calculation fee + transaaction.getSize(); + + /// raw of encoded transaction in hex + final transactionRaw = transaaction.toHex(); + + /// send transaction to network + await provider.request( + ElectrumRequestBroadCastTransaction(transactionRaw: transactionRaw)); + + /// done! check the transaction in block explorer + /// https://chipnet.imaginary.cash/tx/9e534f8a64f76b1af5ccf2522392697f2242fd215206a458cfe286bca4a3ec0a } 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 b0c2350..f0d64fb 100644 --- a/example/lib/global/btc_examples_multisig_taproot_legacy_uncomp.dart +++ b/example/lib/global/btc_examples_multisig_taproot_legacy_uncomp.dart @@ -112,9 +112,9 @@ void main() async { final transaaction = 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 ff62a2a..2fe0924 100644 --- a/example/lib/global/old_examples/bitcoin_example.dart +++ b/example/lib/global/old_examples/bitcoin_example.dart @@ -2,6 +2,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/explorer_service/explorer_service.dart'; /// Calculates the change value based on the sum of all provided values. /// @@ -18,7 +19,8 @@ import 'package:blockchain_utils/blockchain_utils.dart'; /// Returns: /// - The change value. BigInt _changeValue(BigInt sum, List all) { - final sumAll = all.fold(BigInt.zero, (previousValue, element) => previousValue + element); + final sumAll = all.fold( + BigInt.zero, (previousValue, element) => previousValue + element); final remind = sum - sumAll; if (remind < BigInt.zero) { @@ -59,25 +61,27 @@ void _spendFromP2pkhTo10DifferentType() async { final examplePublicKey2 = examplePrivateKey.getPublic(); /// Define transaction outputs - final out1 = - P2pkhAddress.fromAddress(address: "msxiCJXD2WB43wK2PpTUvoqQLF7ZP98qqM", network: network); + final out1 = P2pkhAddress.fromAddress( + address: "msxiCJXD2WB43wK2PpTUvoqQLF7ZP98qqM", network: network); final out2 = P2trAddress.fromAddress( - address: "tb1plq65drqavf93wf63d8g7d8ypuzaargd5h9d35u05ktrcwxq4a6ss0gpvrt", network: network); + address: "tb1plq65drqavf93wf63d8g7d8ypuzaargd5h9d35u05ktrcwxq4a6ss0gpvrt", + network: network); final out3 = P2wpkhAddress.fromAddress( address: "tb1q3zqgu9j368wgk8u5f9vtmkdwq8geetdxry690d", network: network); - final out4 = P2pkAddress.fromPubkey(pubkey: ECPublic.fromBip32(examplePublicKey.publicKey)); - final out5 = - P2shAddress.fromAddress(address: "2N5hVdETdJMwLDxxttfqeWgMuny6K4SYGSc", network: network); - final out6 = - P2shAddress.fromAddress(address: "2NDAUpeUB1kGAQET8SojF8seXNrk3uudtCb", network: network); - final out7 = - P2shAddress.fromAddress(address: "2NE9CYdxju2iEAfR4FMdKPUcZbnKcfCiLhM", network: network); - final out8 = - P2shAddress.fromAddress(address: "2MwGRf8wNJsaYKdigqPwikPpg9JAT2faaPB", network: network); + final out4 = P2pkAddress.fromPubkey(pubkey: examplePublicKey.publicKey.toHex()); + final out5 = P2shAddress.fromAddress( + address: "2N5hVdETdJMwLDxxttfqeWgMuny6K4SYGSc", network: network); + final out6 = P2shAddress.fromAddress( + address: "2NDAUpeUB1kGAQET8SojF8seXNrk3uudtCb", network: network); + final out7 = P2shAddress.fromAddress( + address: "2NE9CYdxju2iEAfR4FMdKPUcZbnKcfCiLhM", network: network); + final out8 = P2shAddress.fromAddress( + address: "2MwGRf8wNJsaYKdigqPwikPpg9JAT2faaPB", network: network); final out9 = P2wshAddress.fromAddress( - address: "tb1qes3upam2nv3rc6s38tqgk0cqh6dlycvk6cjydyvpx9zlumh4h4lsjq26p8", network: network); - final out10 = - P2shAddress.fromAddress(address: "2N2aRKjTQ3uzgUSLWFQAUDvKLnKCiBfCSAh", network: network); + address: "tb1qes3upam2nv3rc6s38tqgk0cqh6dlycvk6cjydyvpx9zlumh4h4lsjq26p8", + network: network); + final out10 = P2shAddress.fromAddress( + address: "2N2aRKjTQ3uzgUSLWFQAUDvKLnKCiBfCSAh", network: network); /// Calculate the change value for the transaction final change = _changeValue( @@ -93,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")), @@ -126,7 +130,8 @@ void _spendFromP2pkhTo10DifferentType() async { /// Create a UTXO using a BitcoinUtxo with specific details utxo: BitcoinUtxo( /// Transaction hash uniquely identifies the referenced transaction - txHash: "b06f4ed0b49a5092a9ea206553ddc5fc469be694d0d28c95598c653e66cdeb5e", + txHash: + "b06f4ed0b49a5092a9ea206553ddc5fc469be694d0d28c95598c653e66cdeb5e", /// Value represents the amount of the UTXO in satoshis. value: BigInt.from(250000), @@ -135,21 +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())), + publicKey: examplePublicKey2.toHex(), + address: examplePublicKey2.toAddress())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "6ff0bdb2966f62f5e202c924e1cab1368b0258833e48986cc0a70fbca624ba93", + txHash: + "6ff0bdb2966f62f5e202c924e1cab1368b0258833e48986cc0a70fbca624ba93", value: BigInt.from(812830), vout: 0, - scriptType: examplePublicKey2.toP2pkhAddress().type, + scriptType: examplePublicKey2.toAddress().type, ), ownerDetails: UtxoAddressDetails( - publicKey: examplePublicKey2.toHex(), address: examplePublicKey2.toP2pkhAddress())), + publicKey: examplePublicKey2.toHex(), + address: examplePublicKey2.toAddress())), ]); /// Build the transaction by invoking the buildTransaction method on the BitcoinTransactionBuilder @@ -159,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(); @@ -241,13 +249,13 @@ void _spendFrom10DifferentTypeToP2pkh() async { /// outputs /// make sure pass network to address for validate, before sending create transaction - final out1 = - P2pkhAddress.fromAddress(address: "n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR", network: network); + final out1 = P2pkhAddress.fromAddress( + address: "n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR", network: network); 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"), @@ -264,7 +272,8 @@ void _spendFrom10DifferentTypeToP2pkh() async { UtxoWithAddress( utxo: BitcoinUtxo( /// Transaction hash uniquely identifies the referenced transaction - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", /// Value represents the amount of the UTXO in satoshis. value: BtcUtils.toSatoshi("0.001"), @@ -273,16 +282,17 @@ 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: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 1, scriptType: childKey1PublicKey.toTaprootAddress().type, @@ -292,54 +302,64 @@ void _spendFrom10DifferentTypeToP2pkh() async { address: childKey1PublicKey.toTaprootAddress())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "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: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 3, scriptType: examplePublicKey.toP2pkAddress().type, ), ownerDetails: UtxoAddressDetails( - publicKey: examplePublicKey.toHex(), address: examplePublicKey.toP2pkAddress())), + publicKey: examplePublicKey.toHex(), + address: examplePublicKey.toP2pkAddress())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 4, scriptType: examplePublicKey.toP2pkInP2sh().type, ), ownerDetails: UtxoAddressDetails( - publicKey: examplePublicKey.toHex(), address: examplePublicKey.toP2pkInP2sh())), + publicKey: examplePublicKey.toHex(), + address: examplePublicKey.toP2pkInP2sh())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 5, scriptType: examplePublicKey.toP2pkhInP2sh().type, ), ownerDetails: UtxoAddressDetails( - publicKey: examplePublicKey.toHex(), address: examplePublicKey.toP2pkhInP2sh())), + publicKey: examplePublicKey.toHex(), + address: examplePublicKey.toP2pkhInP2sh())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 6, scriptType: examplePublicKey.toP2wpkhInP2sh().type, blockHeight: 0, ), ownerDetails: UtxoAddressDetails( - publicKey: examplePublicKey.toHex(), address: examplePublicKey.toP2wpkhInP2sh())), + publicKey: examplePublicKey.toHex(), + address: examplePublicKey.toP2wpkhInP2sh())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 7, scriptType: msig.toP2shAddress().type, @@ -351,24 +371,28 @@ void _spendFrom10DifferentTypeToP2pkh() async { multiSigAddress: msig, address: msig.toP2shAddress())), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.001"), vout: 8, scriptType: msig.toP2wshAddress(network: network).type, blockHeight: 0, ), ownerDetails: UtxoAddressDetails.multiSigAddress( - multiSigAddress: msig, address: msig.toP2wshAddress(network: network))), + multiSigAddress: msig, + address: msig.toP2wshAddress(network: network))), UtxoWithAddress( utxo: BitcoinUtxo( - txHash: "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", + txHash: + "05411dce1a1c9e3f44b54413bdf71e7ab3eff1e2f94818a3568c39814c27b258", value: BtcUtils.toSatoshi("0.0015783"), vout: 9, scriptType: msig2.toP2wshInP2shAddress(network: network).type, blockHeight: 0, ), ownerDetails: UtxoAddressDetails.multiSigAddress( - multiSigAddress: msig2, address: msig2.toP2wshInP2shAddress(network: network))), + multiSigAddress: msig2, + address: msig2.toP2wshInP2shAddress(network: network))), ]); /// Build the transaction by invoking the buildTransaction method on the BitcoinTransactionBuilder @@ -377,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 c4aa4d6..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 @@ -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(); @@ -184,7 +184,7 @@ void _spendP2SH() async { final b = BitcoinTransactionBuilder( /// outputs - outputs: [ + outPuts: [ BitcoinOutput(address: out1, value: change), ], @@ -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 943fab4..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,23 +119,23 @@ 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()) { - 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(); @@ -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")), @@ -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 e388efa..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 @@ -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(); @@ -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), ], @@ -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(); @@ -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), ], @@ -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(); @@ -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) @@ -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 b43c85c..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,11 +2,13 @@ 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. - final txin = utxo.map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)).toList(); + final txin = utxo + .map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)) + .toList(); // in a SegWit (Segregated Witness) transaction, the witness data serves as the unlocking script // for the transaction inputs. In traditional non-SegWit transactions, // the unlocking script is part of the scriptSig field, which contains @@ -24,7 +26,8 @@ BtcTransaction buildP2wpkTransaction({ // It includes the recipient's Bitcoin address (encoded in a specific way) // and can also include additional conditions or requirements based on the type of script used. final List txOut = receiver - .map((e) => TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) + .map((e) => + TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) .toList(); // create BtcTransaction instance with inputs, outputs and segwit @@ -38,11 +41,12 @@ BtcTransaction buildP2wpkTransaction({ // index of input txInIndex: i, // script pub key of spender address - script: utxo[i].public().toP2pkhAddress().toScriptPubKey(), + script: utxo[i].public().toAddress().toScriptPubKey(), // amount of utxo amount: utxo[i].utxo.value); // sign transaction - final signedTx = sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); + final signedTx = + sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); // create unlock script @@ -52,7 +56,8 @@ BtcTransaction buildP2wpkTransaction({ // P2WSH (Pay-to-Witness-Script-Hash): Similarly, for P2WSH addresses, you create SegWit transactions, // and the witness data (signatures and script) is separated from the transaction data. - final p2wpkhWitness = TxWitnessInput(stack: [signedTx, utxo[i].public().toHex()]); + final p2wpkhWitness = + TxWitnessInput(stack: [signedTx, utxo[i].public().toHex()]); witnesses.add(p2wpkhWitness); } tx = tx.copyWith(witnesses: witnesses); @@ -61,11 +66,13 @@ 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. - final txin = utxo.map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)).toList(); + final txin = utxo + .map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)) + .toList(); // in a SegWit (Segregated Witness) transaction, the witness data serves as the unlocking script // for the transaction inputs. In traditional non-SegWit transactions, @@ -84,7 +91,8 @@ BtcTransaction buildP2WSHTransaction({ // It includes the recipient's Bitcoin address (encoded in a specific way) // and can also include additional conditions or requirements based on the type of script used. final List txOut = receiver - .map((e) => TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) + .map((e) => + TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) .toList(); // create BtcTransaction instance with inputs, outputs and segwit @@ -97,13 +105,13 @@ BtcTransaction buildP2WSHTransaction({ // index of utxo txInIndex: i, // P2WSH scripts - script: utxo[i].public().toP2wshRedeemScript(), + script: utxo[i].public().toP2wshScript(), // amount of utxo amount: utxo[i].utxo.value); // sign transaction - final signedTx = sign( - txDigit, utxo[i].public().toP2wshRedeemScript().toHex(), BitcoinOpCodeConst.sighashAll); + final signedTx = sign(txDigit, utxo[i].public().toP2wshScript().toHex(), + BitcoinOpCodeConst.sighashAll); // create unlock script @@ -113,7 +121,8 @@ BtcTransaction buildP2WSHTransaction({ // P2WSH (Pay-to-Witness-Script-Hash): Similarly, for P2WSH addresses, you create SegWit transactions, // and the witness data (signatures and script) is separated from the transaction data. - final p2wshWitness = TxWitnessInput(stack: ['', signedTx, utxo[i].public().toHex()]); + final p2wshWitness = + TxWitnessInput(stack: ['', signedTx, utxo[i].public().toHex()]); witnesses.add(p2wshWitness); } @@ -123,11 +132,13 @@ 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. - final txin = utxo.map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)).toList(); + final txin = utxo + .map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)) + .toList(); // Create transaction outputs // Parameters @@ -138,7 +149,8 @@ BtcTransaction buildP2pkhTransaction({ // It includes the recipient's Bitcoin address (encoded in a specific way) // and can also include additional conditions or requirements based on the type of script used. final List txOut = receiver - .map((e) => TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) + .map((e) => + TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) .toList(); // For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, in this case we need to set 'hasSegwit' to false. @@ -150,11 +162,12 @@ BtcTransaction buildP2pkhTransaction({ // index of utxo txInIndex: i, // spender script pub key - script: utxo[i].public().toP2pkhAddress().toScriptPubKey(), + script: utxo[i].public().toAddress().toScriptPubKey(), ); // sign transaction - final signedTx = sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); + final signedTx = + sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); // set unlocking script for current index txin[i].scriptSig = Script(script: [signedTx, utxo[i].public().toHex()]); @@ -165,11 +178,13 @@ 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. - final txin = utxo.map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)).toList(); + final txin = utxo + .map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)) + .toList(); // Create transaction outputs // Parameters @@ -180,16 +195,18 @@ BtcTransaction buildP2shNoneSegwitTransaction({ // It includes the recipient's Bitcoin address (encoded in a specific way) // and can also include additional conditions or requirements based on the type of script used. final List txOut = receiver - .map((e) => TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) + .map((e) => + TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) .toList(); // For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, in this caase we need to set 'hasSegwit' to false. final tx = BtcTransaction(inputs: txin, outputs: txOut); for (int i = 0; i < txin.length; i++) { final ownerPublic = utxo[i].public(); - final scriptPubKey = utxo[i].ownerDetails.address.type == P2shAddressType.p2pkhInP2sh - ? ownerPublic.toP2pkhAddress().toScriptPubKey() - : ownerPublic.toRedeemScript(); + final scriptPubKey = + utxo[i].ownerDetails.address.type == P2shAddressType.p2pkhInP2sh + ? ownerPublic.toAddress().toScriptPubKey() + : ownerPublic.toRedeemScript(); // For None-SegWit transactions, we use the 'getTransactionDigest' method // to obtain the input digest for signing. final txDigit = tx.getTransactionDigest( @@ -199,12 +216,14 @@ BtcTransaction buildP2shNoneSegwitTransaction({ script: scriptPubKey, ); // sign transaction - final signedTx = sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); + final signedTx = + sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); // set unlocking script for current index switch (utxo[i].ownerDetails.address.type) { case P2shAddressType.p2pkhInP2sh: - txin[i].scriptSig = Script(script: [signedTx, ownerPublic.toHex(), scriptPubKey.toHex()]); + txin[i].scriptSig = Script( + script: [signedTx, ownerPublic.toHex(), scriptPubKey.toHex()]); break; case P2shAddressType.p2pkInP2sh: txin[i].scriptSig = Script(script: [signedTx, scriptPubKey.toHex()]); @@ -219,11 +238,13 @@ 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. - final txin = utxo.map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)).toList(); + final txin = utxo + .map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)) + .toList(); // Create transaction outputs // Parameters @@ -234,7 +255,8 @@ BtcTransaction buildP2SHSegwitTransaction({ // It includes the recipient's Bitcoin address (encoded in a specific way) // and can also include additional conditions or requirements based on the type of script used. final List txOut = receiver - .map((e) => TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) + .map((e) => + TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) .toList(); // in a SegWit (Segregated Witness) transaction, the witness data serves as the unlocking script // for the transaction inputs. In traditional non-SegWit transactions, @@ -249,9 +271,10 @@ BtcTransaction buildP2SHSegwitTransaction({ for (int i = 0; i < txin.length; i++) { final ownerPublic = utxo[i].public(); - final scriptPubKey = utxo[i].ownerDetails.address.type == P2shAddressType.p2wpkhInP2sh - ? ownerPublic.toP2pkhAddress().toScriptPubKey() - : ownerPublic.toP2wshRedeemScript(); + final scriptPubKey = + utxo[i].ownerDetails.address.type == P2shAddressType.p2wpkhInP2sh + ? ownerPublic.toAddress().toScriptPubKey() + : ownerPublic.toP2wshScript(); // For SegWit transactions (excluding P2TR), we use the 'getTransactionSegwitDigit' method // to obtain the input digest for signing. @@ -264,7 +287,8 @@ BtcTransaction buildP2SHSegwitTransaction({ amount: utxo[i].utxo.value); // sign transaction - final signedTx = sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); + final signedTx = + sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); // In a SegWit P2SH (Pay-to-Script-Hash) transaction, you will find both a scriptSig field and a witness field. // This combination is used to maintain compatibility with non-SegWit Bitcoin nodes while taking advantage @@ -281,7 +305,7 @@ BtcTransaction buildP2SHSegwitTransaction({ switch (utxo[i].ownerDetails.address.type) { case P2shAddressType.p2wpkhInP2sh: witnesses.add(TxWitnessInput(stack: [signedTx, ownerPublic.toHex()])); - final script = ownerPublic.toP2wpkhAddress().toScriptPubKey(); + final script = ownerPublic.toSegwitAddress().toScriptPubKey(); txin[i].scriptSig = Script(script: [script.toHex()]); break; case P2shAddressType.p2wshInP2sh: @@ -299,11 +323,13 @@ 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. - final txin = utxo.map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)).toList(); + final txin = utxo + .map((e) => TxInput(txId: e.utxo.txHash, txIndex: e.utxo.vout)) + .toList(); // Create transaction outputs // Parameters @@ -314,7 +340,8 @@ BtcTransaction buildP2trTransaction({ // It includes the recipient's Bitcoin address (encoded in a specific way) // and can also include additional conditions or requirements based on the type of script used. final List txOut = receiver - .map((e) => TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) + .map((e) => + TxOutput(amount: e.value, scriptPubKey: e.address.toScriptPubKey())) .toList(); // For P2TR, P2WPKH, P2WSH, and P2SH (SegWit) transactions, we need to set 'hasSegwit' to true. @@ -339,14 +366,16 @@ BtcTransaction buildP2trTransaction({ // you typically need to include all the scriptPubKeys of the UTXOs // being spent and their corresponding amounts. // This information is required to ensure that the transaction is properly structured and secure - scriptPubKeys: utxo.map((e) => e.ownerDetails.address.toScriptPubKey()).toList(), + scriptPubKeys: + utxo.map((e) => e.ownerDetails.address.toScriptPubKey()).toList(), amounts: utxo.map((e) => e.utxo.value).toList(), sighash: BitcoinOpCodeConst.sighashDefault, ); - // sign transaction using `signTapRoot` method of thransaction - final signedTx = sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); + // sign transaction using `signBip340` method of thransaction + final signedTx = + sign(txDigit, utxo[i].public().toHex(), BitcoinOpCodeConst.sighashAll); // add witness for current index witnesses.add(TxWitnessInput(stack: [signedTx])); 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 12b0f72..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 @@ -1,14 +1,16 @@ // ignore_for_file: unused_local_variable import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:example/services_examples/explorer_service/explorer_service.dart'; import 'spending_builders.dart'; // Define the network as the Testnet (used for testing and development purposes). const network = BitcoinNetwork.testnet; +final service = BitcoinApiService(); // Initialize an API provider for interacting with the Testnet's blockchain data. -final api = ApiProvider.fromMempool(network); +final api = ApiProvider.fromMempool(network, service); // In these tutorials, you will learn how to spend various types of UTXOs. // Each method is specific to a type of UTXO. @@ -24,24 +26,25 @@ 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())); + final utxo = await api.getAccountUtxo( + UtxoAddressDetails(address: sender, publicKey: publicKey.toHex())); // The total amount of UTXOs that we can spend. final sumOfUtxo = utxo.sumOfUtxosValue(); if (sumOfUtxo == BigInt.zero) { - throw Exception("account does not have any unspent transaction or mybe no confirmed"); + throw Exception( + "account does not have any unspent transaction or mybe no confirmed"); } // Receive network fees - final feeRate = await api.getRecommendedFeeRate(); + final feeRate = await api.getNetworkFeeRate(); // feeRate.medium, feeRate.high ,feeRate.low P/KB // In this section, we select the transaction outputs; the number and type of addresses are not important final prive = sWallet; final recPub = rWallet.getPublic(); // P2WPKH - final receiver = recPub.toP2wpkhAddress(); + final receiver = recPub.toSegwitAddress(); // P2TR final changeAddress = recPub.toTaprootAddress(); @@ -61,7 +64,8 @@ Future spendingP2WPKH(ECPrivate sWallet, ECPrivate rWallet) async { // Now that we've determined the transaction size, let's calculate the transaction fee // based on the transaction size and the desired fee rate. - final estimateFee = feeRate.getEstimate(transactionSize, feeRateType: BitcoinFeeRateType.medium); + final estimateFee = feeRate.getEstimate(transactionSize, + feeRateType: BitcoinFeeRateType.medium); // We subtract the fee from the total amount of UTXOs to calculate // the actual amount we can spend in this transaction. @@ -70,19 +74,20 @@ Future spendingP2WPKH(ECPrivate sWallet, ECPrivate rWallet) async { // We specify the desired amount for each address. Here, I have divided the desired total // amount by the number of outputs to ensure an equal amount for each. final outPutWithValue = outputsAdress - .map((e) => - BitcoinOutput(address: e.address, value: canSpend ~/ BigInt.from(outputsAdress.length))) + .map((e) => BitcoinOutput( + address: e.address, + value: canSpend ~/ BigInt.from(outputsAdress.length))) .toList(); // I use the 'buildP2wpkTransaction' method to create a transaction. // 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, ); @@ -107,36 +112,39 @@ Future spendingP2WSH(ECPrivate sWallet, ECPrivate rWallet) async { final addr = sWallet.getPublic(); // P2WSH ADDRESS final sender = addr.toP2wshAddress(); - final utxo = - await api.getAccountUtxo(UtxoAddressDetails(address: sender, publicKey: addr.toHex())); + final utxo = await api.getAccountUtxo( + UtxoAddressDetails(address: sender, publicKey: addr.toHex())); final sumOfUtxo = utxo.sumOfUtxosValue(); if (sumOfUtxo == BigInt.zero) { - throw Exception("account does not have any unspent transaction or mybe no confirmed"); + throw Exception( + "account does not have any unspent transaction or mybe no confirmed"); } - final feeRate = await api.getRecommendedFeeRate(); + final feeRate = await api.getNetworkFeeRate(); 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) ]; final transactionSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxo, outputs: outputsAdress, network: network); - final estimateFee = feeRate.getEstimate(transactionSize, feeRateType: BitcoinFeeRateType.medium); + final estimateFee = feeRate.getEstimate(transactionSize, + feeRateType: BitcoinFeeRateType.medium); final canSpend = sumOfUtxo - estimateFee; final outPutWithValue = outputsAdress - .map((e) => - BitcoinOutput(address: e.address, value: canSpend ~/ BigInt.from(outputsAdress.length))) + .map((e) => BitcoinOutput( + address: e.address, + value: canSpend ~/ BigInt.from(outputsAdress.length))) .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, ); @@ -152,37 +160,40 @@ 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 utxo = - await api.getAccountUtxo(UtxoAddressDetails(address: sender, publicKey: addr.toHex())); + final sender = addr.toAddress(); + final utxo = await api.getAccountUtxo( + UtxoAddressDetails(address: sender, publicKey: addr.toHex())); final sumOfUtxo = utxo.sumOfUtxosValue(); if (sumOfUtxo == BigInt.zero) { - throw Exception("account does not have any unspent transaction or mybe no confirmed"); + throw Exception( + "account does not have any unspent transaction or mybe no confirmed"); } - final feeRate = await api.getRecommendedFeeRate(); + final feeRate = await api.getNetworkFeeRate(); 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) ]; final transactionSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxo, outputs: outputsAdress, network: network); - final estimateFee = feeRate.getEstimate(transactionSize, feeRateType: BitcoinFeeRateType.medium); + final estimateFee = feeRate.getEstimate(transactionSize, + feeRateType: BitcoinFeeRateType.medium); final canSpend = sumOfUtxo - estimateFee; final outPutWithValue = outputsAdress - .map((e) => - BitcoinOutput(address: e.address, value: canSpend ~/ BigInt.from(outputsAdress.length))) + .map((e) => BitcoinOutput( + address: e.address, + value: canSpend ~/ BigInt.from(outputsAdress.length))) .toList(); 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, ); @@ -194,42 +205,46 @@ Future spendingP2PKH(ECPrivate sWallet, ECPrivate rWallet) async { // Spend P2SH(P2PKH) or P2SH(P2PK): Please note that all input addresses must be of P2SH(P2PKH) or P2SH(P2PK) type; otherwise, the transaction will fail. // This method is for standard 1-1 Multisig P2SH. // For standard n-of-m multi-signature scripts, please refer to the 'multi_sig_transactions.dart' tutorial. -Future spendingP2SHNoneSegwit(ECPrivate sWallet, ECPrivate rWallet) async { +Future spendingP2SHNoneSegwit( + 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 `buildP2shNoneSegwitTransaction` to create the transaction. final addr = sWallet.getPublic(); // P2SH(P2PK) final sender = addr.toP2pkInP2sh(); - final utxo = - await api.getAccountUtxo(UtxoAddressDetails(address: sender, publicKey: addr.toHex())); + final utxo = await api.getAccountUtxo( + UtxoAddressDetails(address: sender, publicKey: addr.toHex())); final sumOfUtxo = utxo.sumOfUtxosValue(); if (sumOfUtxo == BigInt.zero) { - throw Exception("account does not have any unspent transaction or mybe no confirmed"); + throw Exception( + "account does not have any unspent transaction or mybe no confirmed"); } - final feeRate = await api.getRecommendedFeeRate(); + final feeRate = await api.getNetworkFeeRate(); 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) ]; final transactionSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxo, outputs: outputsAdress, network: network); - final estimateFee = feeRate.getEstimate(transactionSize, feeRateType: BitcoinFeeRateType.medium); + final estimateFee = feeRate.getEstimate(transactionSize, + feeRateType: BitcoinFeeRateType.medium); final canSpend = sumOfUtxo - estimateFee; final outPutWithValue = outputsAdress - .map((e) => - BitcoinOutput(address: e.address, value: canSpend ~/ BigInt.from(outputsAdress.length))) + .map((e) => BitcoinOutput( + address: e.address, + value: canSpend ~/ BigInt.from(outputsAdress.length))) .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, ); @@ -248,38 +263,41 @@ Future spendingP2shSegwit(ECPrivate sWallet, ECPrivate rWallet) async { final addr = sWallet.getPublic(); // P2SH(P2PWKH) final sender = addr.toP2wpkhInP2sh(); - final utxo = - await api.getAccountUtxo(UtxoAddressDetails(address: sender, publicKey: addr.toHex())); + final utxo = await api.getAccountUtxo( + UtxoAddressDetails(address: sender, publicKey: addr.toHex())); final sumOfUtxo = utxo.sumOfUtxosValue(); if (sumOfUtxo == BigInt.zero) { - throw Exception("account does not have any unspent transaction or mybe no confirmed"); + throw Exception( + "account does not have any unspent transaction or mybe no confirmed"); } - final feeRate = await api.getRecommendedFeeRate(); + final feeRate = await api.getNetworkFeeRate(); 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) ]; final transactionSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxo, outputs: outputsAdress, network: network); - final estimateFee = feeRate.getEstimate(transactionSize, feeRateType: BitcoinFeeRateType.medium); + final estimateFee = feeRate.getEstimate(transactionSize, + feeRateType: BitcoinFeeRateType.medium); final canSpend = sumOfUtxo - estimateFee; final outPutWithValue = outputsAdress - .map((e) => - BitcoinOutput(address: e.address, value: canSpend ~/ BigInt.from(outputsAdress.length))) + .map((e) => BitcoinOutput( + address: e.address, + value: canSpend ~/ BigInt.from(outputsAdress.length))) .toList(); // 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, ); @@ -293,41 +311,44 @@ 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(); - final utxo = - await api.getAccountUtxo(UtxoAddressDetails(address: sender, publicKey: addr.toHex())); + final utxo = await api.getAccountUtxo( + UtxoAddressDetails(address: sender, publicKey: addr.toHex())); final sumOfUtxo = utxo.sumOfUtxosValue(); if (sumOfUtxo == BigInt.zero) { - throw Exception("account does not have any unspent transaction or mybe no confirmed"); + throw Exception( + "account does not have any unspent transaction or mybe no confirmed"); } - final feeRate = await api.getRecommendedFeeRate(); + final feeRate = await api.getNetworkFeeRate(); 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) ]; final transactionSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxo, outputs: outputsAdress, network: network); - final estimateFee = feeRate.getEstimate(transactionSize, feeRateType: BitcoinFeeRateType.medium); + final estimateFee = feeRate.getEstimate(transactionSize, + feeRateType: BitcoinFeeRateType.medium); final canSpend = sumOfUtxo - estimateFee; final outPutWithValue = outputsAdress - .map((e) => - BitcoinOutput(address: e.address, value: canSpend ~/ BigInt.from(outputsAdress.length))) + .map((e) => BitcoinOutput( + address: e.address, + value: canSpend ~/ BigInt.from(outputsAdress.length))) .toList(); 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 d0a3bcc..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 @@ -4,6 +4,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:example/services_examples/explorer_service/explorer_service.dart'; void main() async { final service = BitcoinApiService(); @@ -12,7 +13,7 @@ void main() async { // select api for read accounts UTXOs and send transaction // Mempool or BlockCypher - final api = ApiProvider.fromMempool(network); + final api = ApiProvider.fromMempool(network, service); final mnemonic = Bip39SeedGenerator(Mnemonic.fromString( "spy often critic spawn produce volcano depart fire theory fog turn retire")) @@ -58,14 +59,18 @@ void main() async { // P2WSH Multisig 4-6 // tb1qxt3c7849m0m6cv3z3s35c3zvdna3my3yz0r609qd9g0dcyyk580sgyldhe - final p2wshMultiSigAddress = multiSignatureAddress.toP2wshAddress(network: network); + final p2wshMultiSigAddress = + multiSignatureAddress.toP2wshAddress(network: network).toAddress(network); // p2sh(p2wsh) multisig - final signerP2sh1 = MultiSignatureSigner(publicKey: public5.toHex(), weight: 1); + final signerP2sh1 = + MultiSignatureSigner(publicKey: public5.toHex(), weight: 1); - final signerP2sh2 = MultiSignatureSigner(publicKey: public6.toHex(), weight: 1); + final signerP2sh2 = + MultiSignatureSigner(publicKey: public6.toHex(), weight: 1); - final signerP2sh3 = MultiSignatureSigner(publicKey: public1.toHex(), weight: 1); + final signerP2sh3 = + MultiSignatureSigner(publicKey: public1.toHex(), weight: 1); final MultiSignatureAddress p2shMultiSignature = MultiSignatureAddress( threshold: 2, @@ -73,12 +78,14 @@ void main() async { ); // P2SH(P2WSH) miltisig 2-3 // 2N8co8bth9CNKtnWGfHW6HuUNgnNPNdpsMj - final p2shMultisigAddress = p2shMultiSignature.toP2wshInP2shAddress(network: network); + final p2shMultisigAddress = p2shMultiSignature + .toP2wshInP2shAddress(network: 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 @@ -142,9 +149,11 @@ void main() async { utxos: utxos, outputs: [ BitcoinOutput( - address: p2shMultiSignature.toP2wshInP2shAddress(network: network), value: BigInt.zero), + address: p2shMultiSignature.toP2wshInP2shAddress(network: network), + value: BigInt.zero), BitcoinOutput( - address: multiSignatureAddress.toP2wshAddress(network: network), value: BigInt.zero), + address: multiSignatureAddress.toP2wshAddress(network: network), + value: BigInt.zero), BitcoinOutput(address: exampleAddr2, value: BigInt.zero), BitcoinOutput(address: exampleAddr4, value: BigInt.zero) ], @@ -159,7 +168,7 @@ void main() async { // That's my perspective, of course. final blockCypher = ApiProvider.fromBlocCypher(network, service); - final feeRate = await blockCypher.getRecommendedFeeRate(); + final feeRate = await blockCypher.getNetworkFeeRate(); // fee rate inKB // feeRate.medium: 32279 P/KB // feeRate.high: 43009 P/KB @@ -181,9 +190,12 @@ void main() async { address: p2shMultiSignature.toP2wshInP2shAddress(network: network), value: BigInt.from(365449)); final output2 = BitcoinOutput( - address: multiSignatureAddress.toP2wshAddress(network: network), value: BigInt.from(365449)); - final output3 = BitcoinOutput(address: exampleAddr2, value: BigInt.from(365448)); - final output4 = BitcoinOutput(address: exampleAddr4, value: BigInt.from(365448)); + address: multiSignatureAddress.toP2wshAddress(network: network), + value: BigInt.from(365449)); + final output3 = + BitcoinOutput(address: exampleAddr2, value: BigInt.from(365448)); + final output4 = + BitcoinOutput(address: exampleAddr4, value: BigInt.from(365448)); // Well, now it is clear to whom we are going to pay the amount // Now let's create the transaction @@ -191,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. @@ -221,7 +233,8 @@ void main() async { // I've added a method for signing the transaction as a parameter. // This method sends you the public key for each UTXO, // allowing you to sign the desired input with the associated private key - final transaction = transactionBuilder.buildTransaction((trDigest, utxo, publicKey, sighash) { + final transaction = + transactionBuilder.buildTransaction((trDigest, utxo, publicKey, sighash) { late ECPrivate key; // ok we have the public key of the current UTXO and we use some conditions to find private key and sign transaction @@ -256,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 67653da..b5e2f28 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 @@ -36,7 +36,7 @@ void main() async { final public4 = private4.getPublic(); // P2PKH ADDRESS - final exampleAddr1 = public1.toP2pkhAddress(); + final exampleAddr1 = public1.toAddress(); // P2TR final exampleAddr2 = public2.toTaprootAddress(); @@ -44,7 +44,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 @@ -54,7 +54,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(); @@ -132,7 +132,7 @@ void main() async { // Now, we provide the UTXOs we want to spend. utxos: utxos, // We select transaction outputs - outputs: [ + outPuts: [ output1, output2, output3, @@ -204,10 +204,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 b9fba57..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 @@ -1,4 +1,5 @@ import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:example/services_examples/electrum/electrum_ssl_service.dart'; /// If you are working with different networks, /// you can apply this tutorial universally. @@ -14,17 +15,18 @@ import 'package:bitcoin_base/bitcoin_base.dart'; void main() async { /// connect to electrum service with websocket /// please see `services_examples` folder for how to create electrum websocket service - final service = ElectrumSSLService.connect(Uri.parse("testnet.aranguren.org:51002")); + final service = + await ElectrumSSLService.connect("testnet.aranguren.org:51002"); /// create provider with service - final provider = await ElectrumProvider.connect(service); + final provider = ElectrumProvider(service); /// spender details - final privateKey = - ECPrivate.fromHex("76257aafc9b954351c7f6445b2d07277f681a5e83d515a1f32ebf54989c2af4f"); + final privateKey = ECPrivate.fromHex( + "76257aafc9b954351c7f6445b2d07277f681a5e83d515a1f32ebf54989c2af4f"); final examplePublicKey = privateKey.getPublic(); - final spender1 = examplePublicKey.toP2pkhAddress(); - final spender2 = examplePublicKey.toP2wpkhAddress(); + final spender1 = examplePublicKey.toAddress(); + final spender2 = examplePublicKey.toSegwitAddress(); final spender3 = examplePublicKey.toTaprootAddress(); final spender4 = examplePublicKey.toP2pkhInP2sh(); final spender5 = examplePublicKey.toP2pkInP2sh(); @@ -46,15 +48,16 @@ void main() async { /// loop each spenders address and get utxos and add to accountsUtxos for (final i in spenders) { /// Reads all UTXOs (Unspent Transaction Outputs) associated with the account - final electrumUtxos = - await provider.request(ElectrumRequestScriptHashListUnspent(scriptHash: i.pubKeyHash())); + final elctrumUtxos = await provider.request( + ElectrumRequestScriptHashListUnspent(scriptHash: i.pubKeyHash())); /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. /// read spender utxos - final List utxos = electrumUtxos + final List utxos = elctrumUtxos .map((e) => UtxoWithAddress( utxo: e.toUtxo(i.type), - ownerDetails: UtxoAddressDetails(publicKey: examplePublicKey.toHex(), address: i))) + ownerDetails: UtxoAddressDetails( + publicKey: examplePublicKey.toHex(), address: i))) .toList(); accountsUtxos.addAll(utxos); } @@ -65,39 +68,47 @@ void main() async { return; } - final examplePublicKey2 = - ECPublic.fromHex("02d82c9860e36f15d7b72aa59e29347f951277c21cd4d34822acdeeadbcff8a546"); + final examplePublicKey2 = ECPublic.fromHex( + "02d82c9860e36f15d7b72aa59e29347f951277c21cd4d34822acdeeadbcff8a546"); /// When creating outputs with an address, I utilize the public key. Alternatively, an address class, such as /// P2pkhAddress.fromAddress(address: ".....", network: network); /// P2trAddress.fromAddress(address: "....", network: network) /// .... - final List outputs = [ + final List outPuts = [ BitcoinOutput( - address: examplePublicKey2.toP2wpkhAddress(), value: BtcUtils.toSatoshi("0.00001")), + address: examplePublicKey2.toSegwitAddress(), + value: BtcUtils.toSatoshi("0.00001")), BitcoinOutput( - address: examplePublicKey2.toTaprootAddress(), value: BtcUtils.toSatoshi("0.00001")), - BitcoinOutput(address: examplePublicKey2.toP2pkhInP2sh(), value: BtcUtils.toSatoshi("0.00001")), - BitcoinOutput(address: examplePublicKey2.toP2pkInP2sh(), value: BtcUtils.toSatoshi("0.00001")), + address: examplePublicKey2.toTaprootAddress(), + value: BtcUtils.toSatoshi("0.00001")), BitcoinOutput( - address: examplePublicKey2.toP2wshAddress(), value: BtcUtils.toSatoshi("0.00001")), + address: examplePublicKey2.toP2pkhInP2sh(), + value: BtcUtils.toSatoshi("0.00001")), + BitcoinOutput( + address: examplePublicKey2.toP2pkInP2sh(), + value: BtcUtils.toSatoshi("0.00001")), + BitcoinOutput( + address: examplePublicKey2.toP2wshAddress(), + value: BtcUtils.toSatoshi("0.00001")), ]; /// OP_RETURN const String memo = "https://github.com/mrtnetwork"; /// SUM OF OUTOUT AMOUNTS - final sumOfOutputs = - outputs.fold(BigInt.zero, (previousValue, element) => previousValue + element.value); + final sumOfOutputs = outPuts.fold( + BigInt.zero, (previousValue, element) => previousValue + element.value); /// Estimate transaction size int transactionSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: accountsUtxos, outputs: [ - ...outputs, + ...outPuts, /// I add more output for change value to get correct transaction size - BitcoinOutput(address: examplePublicKey2.toP2pkhAddress(), value: BigInt.zero) + BitcoinOutput( + address: examplePublicKey2.toAddress(), value: BigInt.zero) ], /// network @@ -118,7 +129,8 @@ void main() async { } /// Convert kilobytes to bytes, multiply by the transaction size, and the result yields the transaction fees. - final fee = BigInt.from(transactionSize) * (networkEstimate ~/ BigInt.from(1000)); + final fee = + BigInt.from(transactionSize) * (networkEstimate ~/ BigInt.from(1000)); /// change value final changeValue = sumOfUtxo - (sumOfOutputs + fee); @@ -128,12 +140,13 @@ void main() async { } //// if we have change value we back amount to account if (changeValue > BigInt.zero) { - outputs.add(BitcoinOutput(address: examplePublicKey2.toP2pkhAddress(), value: changeValue)); + outPuts.add(BitcoinOutput( + address: examplePublicKey2.toAddress(), value: changeValue)); } /// create transaction builder final builder = BitcoinTransactionBuilder( - outputs: outputs, + outPuts: outPuts, fee: fee, network: network, utxos: accountsUtxos, @@ -143,11 +156,12 @@ void main() async { enableRBF: true); /// create transaction and sign it - final transaction = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { + 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 @@ -157,7 +171,8 @@ void main() async { final raw = transaction.serialize(); /// send to network - await provider.request(ElectrumRequestBroadCastTransaction(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 diff --git a/example/lib/global/transfer_to_8_account_example.dart b/example/lib/global/transfer_to_8_account_example.dart index c36453f..b0c869c 100644 --- a/example/lib/global/transfer_to_8_account_example.dart +++ b/example/lib/global/transfer_to_8_account_example.dart @@ -1,12 +1,15 @@ import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:example/services_examples/electrum/electrum_ssl_service.dart'; + void main() async { /// connect to electrum service with websocket /// please see `services_examples` folder for how to create electrum websocket service - final service = ElectrumSSLService.connect(Uri.parse("testnet.aranguren.org:51002")); + final service = + await ElectrumSSLService.connect("testnet.aranguren.org:51002"); /// create provider with service - final provider = await ElectrumProvider.connect(service); + final provider = ElectrumProvider(service); /// spender details /// Define another private key from wif @@ -14,25 +17,26 @@ void main() async { 'cTALNpTpRbbxTCJ2A5Vq88UxT44w1PE2cYqiB3n4hRvzyCev1Wwo', netVersion: BitcoinNetwork.testnet.wifNetVer); final examplePublicKey2 = examplePrivateKey2.getPublic(); - final p2pkhAddress = examplePublicKey2.toP2pkhAddress(); + final p2pkhAddress = examplePublicKey2.toAddress(); /// receiver addresses i use public key for generate address - final examplePublicKey = - ECPublic.fromHex("032a4f8be9ebffb46e2c6a1c240702553b9c9c8ad9638650833d07d5d22f618621"); + final examplePublicKey = ECPublic.fromHex( + "032a4f8be9ebffb46e2c6a1c240702553b9c9c8ad9638650833d07d5d22f618621"); const network = BitcoinNetwork.testnet; /// Reads all UTXOs (Unspent Transaction Outputs) associated with the account - final electrumUtxos = await provider.request( - ElectrumRequestScriptHashListUnspent(scriptHash: examplePublicKey2.toAddress().pubKeyHash())); + final elctrumUtxos = await provider.request( + ElectrumRequestScriptHashListUnspent( + scriptHash: examplePublicKey2.toAddress().pubKeyHash())); /// Converts all UTXOs to a list of UtxoWithAddress, containing UTXO information along with address details. /// read spender utxos - final List utxos = electrumUtxos + final List utxos = elctrumUtxos .map((e) => UtxoWithAddress( utxo: e.toUtxo(p2pkhAddress.type), - ownerDetails: - UtxoAddressDetails(publicKey: examplePublicKey2.toHex(), address: p2pkhAddress))) + ownerDetails: UtxoAddressDetails( + publicKey: examplePublicKey2.toHex(), address: p2pkhAddress))) .toList(); /// get sum of values @@ -45,33 +49,46 @@ void main() async { /// P2pkhAddress.fromAddress(address: ".....", network: network); /// P2trAddress.fromAddress(address: "....", network: network) /// .... - final List outputs = [ - BitcoinOutput(address: examplePublicKey.toP2pkhAddress(), value: BtcUtils.toSatoshi("0.00001")), + final List outPuts = [ + BitcoinOutput( + address: examplePublicKey.toAddress(), + value: BtcUtils.toSatoshi("0.00001")), + BitcoinOutput( + address: examplePublicKey.toSegwitAddress(), + value: BtcUtils.toSatoshi("0.00001")), + BitcoinOutput( + address: examplePublicKey.toTaprootAddress(), + value: BtcUtils.toSatoshi("0.00001")), + BitcoinOutput( + address: examplePublicKey.toP2pkhInP2sh(), + value: BtcUtils.toSatoshi("0.00001")), + BitcoinOutput( + address: examplePublicKey.toP2pkInP2sh(), + value: BtcUtils.toSatoshi("0.00001")), BitcoinOutput( - address: examplePublicKey.toP2wpkhAddress(), value: BtcUtils.toSatoshi("0.00001")), + address: examplePublicKey.toP2wshAddress(), + value: BtcUtils.toSatoshi("0.00001")), BitcoinOutput( - address: examplePublicKey.toTaprootAddress(), value: BtcUtils.toSatoshi("0.00001")), - BitcoinOutput(address: examplePublicKey.toP2pkhInP2sh(), value: BtcUtils.toSatoshi("0.00001")), - BitcoinOutput(address: examplePublicKey.toP2pkInP2sh(), value: BtcUtils.toSatoshi("0.00001")), - BitcoinOutput(address: examplePublicKey.toP2wshAddress(), value: BtcUtils.toSatoshi("0.00001")), - BitcoinOutput(address: examplePublicKey.toP2wpkhInP2sh(), value: BtcUtils.toSatoshi("0.00001")), + address: examplePublicKey.toP2wpkhInP2sh(), + value: BtcUtils.toSatoshi("0.00001")), ]; /// OP_RETURN const String memo = "https://github.com/mrtnetwork"; /// SUM OF OUTOUT AMOUNTS - final sumOfOutputs = - outputs.fold(BigInt.zero, (previousValue, element) => previousValue + element.value); + final sumOfOutputs = outPuts.fold( + BigInt.zero, (previousValue, element) => previousValue + element.value); /// ESTIMATE TRANSACTION SIZE int estimateSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxos, outputs: [ - ...outputs, + ...outPuts, /// I add more output for change value to get correct transaction size - BitcoinOutput(address: examplePublicKey2.toP2pkhAddress(), value: BigInt.zero) + BitcoinOutput( + address: examplePublicKey2.toAddress(), value: BigInt.zero) ], /// network @@ -92,7 +109,8 @@ void main() async { } /// kb to bytes and mul with transaction size and now we have fee - final fee = BigInt.from(estimateSize) * (networkEstimate ~/ BigInt.from(1000)); + final fee = + BigInt.from(estimateSize) * (networkEstimate ~/ BigInt.from(1000)); /// change value final changeValue = sumOfUtxo - (sumOfOutputs + fee); @@ -100,14 +118,14 @@ void main() async { if (changeValue.isNegative) return; //// if we have change value we back amount to account if (changeValue > BigInt.zero) { - final changeOutput = - BitcoinOutput(address: examplePublicKey2.toP2pkhAddress(), value: changeValue); - outputs.add(changeOutput); + final changeOutput = BitcoinOutput( + address: examplePublicKey2.toAddress(), value: changeValue); + outPuts.add(changeOutput); } /// create transaction builder final builder = BitcoinTransactionBuilder( - outputs: outputs, + outPuts: outPuts, fee: fee, network: network, utxos: utxos, @@ -117,11 +135,12 @@ void main() async { enableRBF: true); /// create transaction and sign it - final transaction = builder.buildTransaction((trDigest, utxo, publicKey, sighash) { + 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 @@ -131,7 +150,8 @@ void main() async { final raw = transaction.serialize(); /// send to network - await provider.request(ElectrumRequestBroadCastTransaction(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/abab018f3d2b92bf30c63b4aca419cf6d6571692b3620f06311c7e5a21a88b56 diff --git a/example/lib/main.dart b/example/lib/main.dart index ed100b9..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 @@ -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(); @@ -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 @@ -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/methods.dart b/example/lib/musig/methods.dart index 2837658..e5c22ee 100644 --- a/example/lib/musig/methods.dart +++ b/example/lib/musig/methods.dart @@ -1,10 +1,14 @@ import 'package:bitcoin_base/bitcoin_base.dart'; +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 = - ElectrumSSLService.connect(Uri.parse("tcp://testnet4-electrumx.wakiyamap.dev:51002")); - return ElectrumProvider.connect(service); + await ElectrumSSLService.connect("testnet.aranguren.org:51002"); + return ElectrumProvider(service); } class PsbtUtxoRequest { @@ -48,8 +52,8 @@ Future> getPsbtUtxo( final provider = await getProvider(); final utxos = await Future.wait(addresses.map((e) async { - return await provider - .request(ElectrumRequestScriptHashListUnspent(scriptHash: e.address.pubKeyHash())); + return await provider.request(ElectrumRequestScriptHashListUnspent( + scriptHash: e.address.pubKeyHash())); })); final utxoss = List.generate(utxos.length, (i) async { @@ -72,7 +76,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/example/lib/musig/musig_example.dart b/example/lib/musig/musig_example.dart index 2a1aaad..fccd013 100644 --- a/example/lib/musig/musig_example.dart +++ b/example/lib/musig/musig_example.dart @@ -132,8 +132,8 @@ BtcTransaction _spendWithLeafC( scriptPubKeys: 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, scriptTree: treeScript); diff --git a/example/pubspec.lock b/example/pubspec.lock index a55e895..077b5e5 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -23,16 +23,15 @@ packages: path: ".." relative: true source: path - version: "6.1.0" + version: "6.5.0" blockchain_utils: dependency: "direct main" description: - path: "." - ref: cake-update-v4 - resolved-ref: "7bf4c263900a81fcddbd7797169f0e2fbd3a9f46" - url: "https://github.com/cake-tech/blockchain_utils" - source: git - version: "4.0.0" + name: blockchain_utils + sha256: fbddd2a7f1849d2244a35bf996b6fb00bf6bd557f0e13aed65ba0c16ad133cb7 + url: "https://pub.dev" + source: hosted + version: "5.0.0" boolean_selector: dependency: transitive description: @@ -147,14 +146,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - intl: - dependency: transitive - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" js: dependency: transitive description: @@ -235,14 +226,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" sky_engine: dependency: transitive description: flutter @@ -300,10 +283,10 @@ packages: dependency: transitive description: name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" vector_math: dependency: transitive description: @@ -329,5 +312,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index a192cc3..5612b28 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.2.3 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -37,11 +37,9 @@ dependencies: cupertino_icons: ^1.0.2 bitcoin_base: path: ../ - blockchain_utils: - git: - url: https://github.com/cake-tech/blockchain_utils - ref: cake-update-v4 - + # blockchain_utils: + # path: ../../blockchain_utils + blockchain_utils: ^5.0.0 http: ^1.2.0 dev_dependencies: diff --git a/lib/bitcoin_base.dart b/lib/bitcoin_base.dart index 3785814..6327c9a 100644 --- a/lib/bitcoin_base.dart +++ b/lib/bitcoin_base.dart @@ -1,35 +1,15 @@ -/// library bitcoin_base -/// a comprehensive and versatile Dart library for all your Bitcoin transaction needs. -/// offers robust support for various Bitcoin transaction types, -/// including spending transactions, Bitcoin address management, -/// Bitcoin Schnorr signatures, BIP-39 mnemonic phrase generation, -/// hierarchical deterministic (HD) wallet derivation, and Web3 Secret Storage Definition. library; export 'src/bitcoin/bitcoin.dart'; export 'src/exception/exception.dart'; -export 'src/bitcoin/address/util.dart'; - -export 'src/bitcoin/script/scripts.dart'; - -export 'src/bitcoin/amount/amount.dart'; - export 'src/crypto/crypto.dart'; export 'src/models/network.dart'; export 'src/provider/api_provider.dart'; -export 'src/provider/models/electrum/electrum_utxo.dart'; - -export 'src/provider/service/electrum/electrum.dart'; - -export 'src/provider/service/electrum/electrum_version.dart'; - -export 'src/utils/utils.dart'; - export 'src/utils/btc_utils.dart'; export 'src/cash_token/cash_token.dart'; @@ -40,4 +20,6 @@ export 'src/transaction_builder/builder.dart'; export 'src/psbt/psbt.dart'; -export 'src/bitcoin/silent_payments/silent_payments.dart'; +export 'package:bitcoin_base/src/bitcoin/silent_payments/silent_payments.dart'; + +export 'package:bitcoin_base/src/bitcoin/script/op_code/constant.dart'; diff --git a/lib/src/bitcoin/address/core.dart b/lib/src/bitcoin/address/core.dart index ee95f9c..89c4d78 100644 --- a/lib/src/bitcoin/address/core.dart +++ b/lib/src/bitcoin/address/core.dart @@ -9,34 +9,42 @@ abstract class BitcoinAddressType implements Enumerate { /// Factory method to create a BitcoinAddressType enum value from a name or value. static BitcoinAddressType fromValue(String? value) { return values.firstWhere((element) => element.value == value, - orElse: () => throw DartBitcoinPluginException('Unknown address type. $value')); + orElse: () => + throw DartBitcoinPluginException('Unknown address type. $value')); } - static BitcoinAddressType fromAddress(BitcoinBaseAddress address) { - if (address is P2pkhAddress) { - return P2pkhAddressType.p2pkh; - } else if (address is P2shAddress) { - return P2shAddressType.p2wpkhInP2sh; - } else if (address is P2wshAddress) { - return SegwitAddressType.p2wsh; - } else if (address is P2trAddress) { - return SegwitAddressType.p2tr; - } else if (address is SilentPaymentsAddresType) { - return SilentPaymentsAddresType.p2sp; - } else if (address is P2wpkhAddress) { - return SegwitAddressType.p2wpkh; - } - - throw DartBitcoinPluginException('Invalid BitcoinAddressType: $address'); - } + // static BitcoinAddressType fromAddress(BitcoinBaseAddress address) { + // if (address is P2pkhAddress) { + // return P2pkhAddressType.p2pkh; + // } else if (address is P2shAddress) { + // return P2shAddressType.p2wpkhInP2sh; + // } else if (address is P2wshAddress) { + // return SegwitAddressType.p2wsh; + // } else if (address is P2trAddress) { + // return SegwitAddressType.p2tr; + // } else if (address is SilentPaymentsAddresType) { + // return SilentPaymentsAddresType.p2sp; + // } else if (address is P2wpkhAddress) { + // return SegwitAddressType.p2wpkh; + // } + // + // throw DartBitcoinPluginException('Invalid BitcoinAddressType: $address'); + // } /// Check if the address type is Pay-to-Script-Hash (P2SH). bool get isP2sh; - int get hashLength; bool get isSegwit; bool get isP2tr => false; + 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, @@ -67,25 +75,32 @@ abstract class BitcoinAddressType implements Enumerate { } @override - String toString() => value; + String toString() { + return 'BitcoinAddressType.$value'; + } } abstract class BitcoinBaseAddress { const BitcoinBaseAddress(); - factory BitcoinBaseAddress.fromProgram( {required String addressProgram, required BitcoinAddressType type}) { if (type.isP2sh) { - return P2shAddress.fromHash160(h160: addressProgram, type: type.cast()); + return P2shAddress.fromHash160( + addrHash: addressProgram, type: type.cast()); } return switch (type) { - PubKeyAddressType.p2pk => P2pkAddress(publicKey: ECPublic.fromHex(addressProgram)), + PubKeyAddressType.p2pk => P2pkAddress(publicKey: addressProgram), P2pkhAddressType.p2pkh || P2pkhAddressType.p2pkhwt => - P2pkhAddress.fromHash160(h160: addressProgram, type: type.cast()), - SegwitAddressType.p2wpkh => P2wpkhAddress.fromProgram(program: addressProgram), - SegwitAddressType.p2wsh => P2wshAddress.fromProgram(program: addressProgram), - SegwitAddressType.p2tr => P2trAddress.fromProgram(program: addressProgram), + P2pkhAddress.fromHash160(addrHash: addressProgram, type: type.cast()), + SegwitAddressType.p2wpkh => + P2wpkhAddress.fromProgram(program: addressProgram), + SegwitAddressType.p2wsh => + P2wshAddress.fromProgram(program: addressProgram), + SegwitAddressType.p2tr => + P2trAddress.fromProgram(program: addressProgram), + SilentPaymentsAddresType.p2sp => + P2sp.fromProgram(program: addressProgram), _ => throw DartBitcoinPluginException("Unsuported bitcoin address type."), }; } @@ -137,7 +152,9 @@ class PubKeyAddressType extends BitcoinAddressType { @override int get hashLength => 20; @override - String toString() => value; + String toString() { + return 'PubKeyAddressType.$value'; + } } class P2pkhAddressType extends BitcoinAddressType { @@ -153,19 +170,22 @@ class P2pkhAddressType extends BitcoinAddressType { @override int get hashLength => 20; @override - String toString() => value; + String toString() { + return 'P2pkhAddressType.$value'; + } } class P2shAddressType extends BitcoinAddressType { - const P2shAddressType._(super.value, this.hashLength, this.withToken) : super._(); - static const P2shAddressType p2wshInP2sh = - P2shAddressType._("P2SH/P2WSH", _BitcoinAddressUtils.hash160DigestLength, false); - static const P2shAddressType p2wpkhInP2sh = - P2shAddressType._("P2SH/P2WPKH", _BitcoinAddressUtils.hash160DigestLength, false); - static const P2shAddressType p2pkhInP2sh = - P2shAddressType._("P2SH/P2PKH", _BitcoinAddressUtils.hash160DigestLength, false); - static const P2shAddressType p2pkInP2sh = - P2shAddressType._("P2SH/P2PK", _BitcoinAddressUtils.hash160DigestLength, false); + const P2shAddressType._(super.value, this.hashLength, this.withToken) + : super._(); + static const P2shAddressType p2wshInP2sh = P2shAddressType._( + 'P2SH/P2WSH', _BitcoinAddressUtils.hash160DigestLength, false); + static const P2shAddressType p2wpkhInP2sh = P2shAddressType._( + 'P2SH/P2WPKH', _BitcoinAddressUtils.hash160DigestLength, false); + static const P2shAddressType p2pkhInP2sh = P2shAddressType._( + 'P2SH/P2PKH', _BitcoinAddressUtils.hash160DigestLength, false); + static const P2shAddressType p2pkInP2sh = P2shAddressType._( + 'P2SH/P2PK', _BitcoinAddressUtils.hash160DigestLength, false); @override bool get isP2sh => true; @override @@ -177,37 +197,39 @@ class P2shAddressType extends BitcoinAddressType { /// specify BCH NETWORK for now! /// Pay-to-Script-Hash-32 - static const P2shAddressType p2pkhInP2sh32 = - P2shAddressType._("P2SH32/P2PKH", _BitcoinAddressUtils.scriptHashLenght, false); + static const P2shAddressType p2pkhInP2sh32 = P2shAddressType._( + 'P2SH32/P2PKH', _BitcoinAddressUtils.scriptHashLenght, false); //// Pay-to-Script-Hash-32 - static const P2shAddressType p2pkInP2sh32 = - P2shAddressType._("P2SH32/P2PK", _BitcoinAddressUtils.scriptHashLenght, false); + static const P2shAddressType p2pkInP2sh32 = P2shAddressType._( + 'P2SH32/P2PK', _BitcoinAddressUtils.scriptHashLenght, false); /// Pay-to-Script-Hash-32-with-token - static const P2shAddressType p2pkhInP2sh32wt = - P2shAddressType._("P2SH32WT/P2PKH", _BitcoinAddressUtils.scriptHashLenght, true); + static const P2shAddressType p2pkhInP2sh32wt = P2shAddressType._( + 'P2SH32WT/P2PKH', _BitcoinAddressUtils.scriptHashLenght, true); /// Pay-to-Script-Hash-32-with-token - static const P2shAddressType p2pkInP2sh32wt = - P2shAddressType._("P2SH32WT/P2PK", _BitcoinAddressUtils.scriptHashLenght, true); + static const P2shAddressType p2pkInP2sh32wt = P2shAddressType._( + 'P2SH32WT/P2PK', _BitcoinAddressUtils.scriptHashLenght, true); /// Pay-to-Script-Hash-with-token - static const P2shAddressType p2pkhInP2shwt = - P2shAddressType._("P2SHWT/P2PKH", _BitcoinAddressUtils.hash160DigestLength, true); + static const P2shAddressType p2pkhInP2shwt = P2shAddressType._( + 'P2SHWT/P2PKH', _BitcoinAddressUtils.hash160DigestLength, true); /// Pay-to-Script-Hash-with-token - static const P2shAddressType p2pkInP2shwt = - P2shAddressType._("P2SHWT/P2PK", _BitcoinAddressUtils.hash160DigestLength, true); + static const P2shAddressType p2pkInP2shwt = P2shAddressType._( + 'P2SHWT/P2PK', _BitcoinAddressUtils.hash160DigestLength, true); @override - String toString() => value; + String toString() { + return 'P2shAddressType.$value'; + } } class SegwitAddressType extends BitcoinAddressType { const SegwitAddressType._(super.value) : super._(); - static const SegwitAddressType p2wpkh = SegwitAddressType._("P2WPKH"); - static const SegwitAddressType p2tr = SegwitAddressType._("P2TR"); - static const SegwitAddressType p2wsh = SegwitAddressType._("P2WSH"); + static const SegwitAddressType p2wpkh = SegwitAddressType._('P2WPKH'); + static const SegwitAddressType p2tr = SegwitAddressType._('P2TR'); + static const SegwitAddressType p2wsh = SegwitAddressType._('P2WSH'); static const SegwitAddressType mweb = SegwitAddressType._("MWEB"); @override bool get isP2sh => false; @@ -230,22 +252,29 @@ class SegwitAddressType extends BitcoinAddressType { } @override - String toString() => value; + String toString() { + return 'SegwitAddressType.$value'; + } } -class SilentPaymentsAddresType extends BitcoinAddressType { - const SilentPaymentsAddresType._(super.value) : super._(); +class SilentPaymentsAddresType implements BitcoinAddressType { + const SilentPaymentsAddresType._(this.value); static const SilentPaymentsAddresType p2sp = SilentPaymentsAddresType._("P2SP"); @override bool get isP2sh => false; @override bool get isSegwit => true; + @override + final String value; + @override int get hashLength { return 32; } @override - String toString() => value; + String toString() { + return 'SilentPaymentsAddresType.$value'; + } } diff --git a/lib/src/bitcoin/address/legacy_address.dart b/lib/src/bitcoin/address/legacy_address.dart index 39c63fe..545583a 100644 --- a/lib/src/bitcoin/address/legacy_address.dart +++ b/lib/src/bitcoin/address/legacy_address.dart @@ -1,75 +1,32 @@ part of 'package:bitcoin_base/src/bitcoin/address/address.dart'; -abstract class LegacyAddress extends BitcoinBaseAddress { +abstract class LegacyAddress implements BitcoinBaseAddress { /// Represents a Bitcoin address /// /// [addressProgram] the addressProgram string representation of the address; hash160 represents - /// two consequtive hashes of the public key or the redeem script or SHA256 for BCH(P2SH), first + /// two consequtive hashes of the public key or the redeam script or SHA256 for BCH(P2SH), first /// a SHA-256 and then an RIPEMD-160 - LegacyAddress.fromHash160({ - required String h160, - required BitcoinAddressType type, - }) : _addressProgram = _BitcoinAddressUtils.validateAddressProgram(h160, type), - super(); - - LegacyAddress.fromAddress({required String address, required BasedUtxoNetwork network}) - : super() { - final decode = _BitcoinAddressUtils.decodeLegacyAddressWithNetworkAndType( - address: address, - type: type, - network: network, - ); - + LegacyAddress.fromHash160(String addrHash, BitcoinAddressType addressType) + : _addressProgram = + _BitcoinAddressUtils.validateAddressProgram(addrHash, addressType); + LegacyAddress.fromAddress( + {required String address, required BasedUtxoNetwork network}) { + final decode = _BitcoinAddressUtils.decodeLagacyAddressWithNetworkAndType( + address: address, type: type, network: network); if (decode == null) { - throw DartBitcoinPluginException('Invalid ${network.conf.coinName} address'); + throw DartBitcoinPluginException( + 'Invalid ${network.conf.coinName} address'); } - _addressProgram = decode; } - - LegacyAddress.fromPubkey({required ECPublic pubkey}) - : _pubkey = pubkey, - _addressProgram = _BitcoinAddressUtils.pubkeyToHash160(pubkey.toHex()); - - LegacyAddress.fromRedeemScript({required Script script}) + LegacyAddress.fromScript({required Script script}) : _addressProgram = _BitcoinAddressUtils.scriptToHash160(script); + LegacyAddress._(); - LegacyAddress.fromScriptSig({required Script script}) { - switch (type) { - case PubKeyAddressType.p2pk: - _signature = script.findScriptParam(0); - break; - case P2pkhAddressType.p2pkh: - if (script.script.length != 2) throw DartBitcoinPluginException('Input is invalid'); - - _signature = script.findScriptParam(0); - - if (!isCanonicalScriptSignature(BytesUtils.fromHexString(_signature!))) { - throw DartBitcoinPluginException('Input has invalid signature'); - } - - _pubkey = ECPublic.fromHex(script.findScriptParam(1)); - _addressProgram = _BitcoinAddressUtils.pubkeyToHash160(_pubkey!.toHex()); - break; - case P2shAddressType.p2wpkhInP2sh: - case P2shAddressType.p2wshInP2sh: - case P2shAddressType.p2pkhInP2sh: - case P2shAddressType.p2pkInP2sh: - _signature = script.findScriptParam(1); - _addressProgram = _BitcoinAddressUtils.scriptToHash160( - Script.fromRaw(hexData: script.findScriptParam(2))); - break; - default: - throw UnimplementedError(); - } - } - - ECPublic? _pubkey; - String? _signature; late final String _addressProgram; - ECPublic? get pubkey => _pubkey; - String? get signature => _signature; + // ECPublic? get pubkey => _pubkey; + // String? get signature => _signature; @override String get addressProgram { @@ -79,15 +36,8 @@ abstract class LegacyAddress extends BitcoinBaseAddress { @override String toAddress(BasedUtxoNetwork network) { - if (!network.supportedAddress.contains(type)) { - throw DartBitcoinPluginException("network does not support ${type.value} address"); - } - return _BitcoinAddressUtils.legacyToAddress( - network: network, - addressProgram: addressProgram, - type: type, - ); + network: network, addressProgram: addressProgram, type: type); } @override @@ -105,81 +55,36 @@ abstract class LegacyAddress extends BitcoinBaseAddress { } @override - int get hashCode => HashCodeGenerator.generateHashCode([_addressProgram, type]); + int get hashCode => + HashCodeGenerator.generateHashCode([_addressProgram, type]); } class P2shAddress extends LegacyAddress { - static final regex = RegExp(r'[23M][a-km-zA-HJ-NP-Z1-9]{25,34}'); - - P2shAddress.fromRedeemScript({ - required super.script, - this.type = P2shAddressType.p2pkInP2sh, - }) : super.fromRedeemScript(); + static RegExp get regex => RegExp(r'[23M][a-km-zA-HJ-NP-Z1-9]{25,34}'); - factory P2shAddress.fromRedeemScript32({ - required Script script, - P2shAddressType type = P2shAddressType.p2pkInP2sh32, - }) { - if (type.hashLength != 32) { + factory P2shAddress.fromScript32( + {required Script script, + P2shAddressType addressType = P2shAddressType.p2pkInP2sh32}) { + if (addressType.hashLength != 32) { throw DartBitcoinPluginException("Invalid P2sh 32 address type."); } - return P2shAddress.fromHash160( - h160: BytesUtils.toHexString(QuickCrypto.sha256DoubleHash(script.toBytes())), - ); - } - - P2shAddress.fromAddress({ - required super.address, - required super.network, - this.type = P2shAddressType.p2pkInP2sh, - }) : super.fromAddress(); - - P2shAddress.fromHash160({ - required super.h160, - this.type = P2shAddressType.p2pkInP2sh, - }) : super.fromHash160(type: type); - - factory P2shAddress.fromDerivation({ - required Bip32Base bip32, - required BitcoinDerivationInfo derivationInfo, - required bool isChange, - required int index, - P2shAddressType type = P2shAddressType.p2wpkhInP2sh, - }) { - final fullPath = derivationInfo.derivationPath - .addElem(Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(isChange))) - .addElem(Bip32KeyIndex(index)); - final pubkey = ECPublic.fromBip32(bip32.derive(fullPath).publicKey); - - switch (type) { - case P2shAddressType.p2pkInP2sh: - return pubkey.toP2pkInP2sh(); - case P2shAddressType.p2pkhInP2sh: - return pubkey.toP2pkhInP2sh(); - case P2shAddressType.p2wshInP2sh: - return pubkey.toP2wshInP2sh(); - case P2shAddressType.p2wpkhInP2sh: - return pubkey.toP2wpkhInP2sh(); - default: - throw UnimplementedError(); - } - } - - factory P2shAddress.fromPath({required Bip32Base bip32, required Bip32Path path}) { - return ECPublic.fromBip32(bip32.derive(path).publicKey).toP2wpkhInP2sh(); + addrHash: BytesUtils.toHexString( + QuickCrypto.sha256DoubleHash(script.toBytes())), + type: addressType); } + P2shAddress.fromScript( + {required super.script, this.type = P2shAddressType.p2pkInP2sh}) + : super.fromScript(); - factory P2shAddress.fromScriptPubkey({ - required Script script, - type = P2shAddressType.p2pkInP2sh, - }) { - if (script.getAddressType() is! P2shAddressType) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); - } - - return P2shAddress.fromHash160(h160: script.findScriptParam(1), type: type); - } + P2shAddress.fromAddress( + {required super.address, + required super.network, + this.type = P2shAddressType.p2pkInP2sh}) + : super.fromAddress(); + P2shAddress.fromHash160( + {required String addrHash, this.type = P2shAddressType.p2pkInP2sh}) + : super.fromHash160(addrHash, type); @override final P2shAddressType type; @@ -187,7 +92,8 @@ class P2shAddress extends LegacyAddress { @override String toAddress(BasedUtxoNetwork network) { if (!network.supportedAddress.contains(type)) { - throw DartBitcoinPluginException('network does not support ${type.value} address.'); + throw DartBitcoinPluginException( + 'network does not support ${type.value} address.'); } return super.toAddress(network); } @@ -195,15 +101,18 @@ class P2shAddress extends LegacyAddress { /// Returns the scriptPubKey (P2SH) that corresponds to this address @override Script toScriptPubKey() { - if (addressProgram.length == 64) { - return Script( - script: [BitcoinOpCodeConst.OP_HASH256, addressProgram, BitcoinOpCodeConst.OP_EQUAL], - ); - } else { - return Script( - script: [BitcoinOpCodeConst.OP_HASH160, addressProgram, BitcoinOpCodeConst.OP_EQUAL], - ); + if (addressProgram.length == QuickCrypto.sha256DigestSize * 2) { + return Script(script: [ + BitcoinOpcode.opHash256, + addressProgram, + BitcoinOpcode.opEqual + ]); } + return Script(script: [ + BitcoinOpcode.opHash160, + addressProgram, + BitcoinOpcode.opEqual + ]); } @override @@ -219,111 +128,74 @@ class P2shAddress extends LegacyAddress { } class P2pkhAddress extends LegacyAddress { - static final regex = RegExp(r'[1mnL][a-km-zA-HJ-NP-Z1-9]{25,34}'); - - factory P2pkhAddress.fromScriptPubkey({ - required Script script, - P2pkhAddressType type = P2pkhAddressType.p2pkh, - }) { - if (script.getAddressType() != P2pkhAddressType.p2pkh) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); - } - - return P2pkhAddress.fromHash160(h160: script.findScriptParam(2), type: type); - } - - P2pkhAddress.fromAddress({ - required super.address, - required super.network, - this.type = P2pkhAddressType.p2pkh, - }) : super.fromAddress(); - - P2pkhAddress.fromHash160({required super.h160, this.type = P2pkhAddressType.p2pkh}) - : super.fromHash160(type: type); + static RegExp get regex => RegExp(r'[1mnL][a-km-zA-HJ-NP-Z1-9]{25,34}'); - P2pkhAddress.fromScriptSig({required super.script, this.type = P2pkhAddressType.p2pkh}) - : super.fromScriptSig(); - - factory P2pkhAddress.fromDerivation({ - required Bip32Base bip32, - required BitcoinDerivationInfo derivationInfo, - required bool isChange, - required int index, - }) { - final fullPath = derivationInfo.derivationPath - .addElem(Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(isChange))) - .addElem(Bip32KeyIndex(index)); - return ECPublic.fromBip32(bip32.derive(fullPath).publicKey).toP2pkhAddress(); - } - - factory P2pkhAddress.fromPath({required Bip32Base bip32, required Bip32Path path}) { - return ECPublic.fromBip32(bip32.derive(path).publicKey).toP2pkhAddress(); - } + P2pkhAddress.fromScript( + {required super.script, this.type = P2pkhAddressType.p2pkh}) + : super.fromScript(); + P2pkhAddress.fromAddress( + {required super.address, + required super.network, + this.type = P2pkhAddressType.p2pkh}) + : super.fromAddress(); + P2pkhAddress.fromHash160( + {required String addrHash, this.type = P2pkhAddressType.p2pkh}) + : super.fromHash160(addrHash, type); @override Script toScriptPubKey() { return Script(script: [ - BitcoinOpCodeConst.OP_DUP, - BitcoinOpCodeConst.OP_HASH160, - _addressProgram, - BitcoinOpCodeConst.OP_EQUALVERIFY, - BitcoinOpCodeConst.OP_CHECKSIG + BitcoinOpcode.opDup, + BitcoinOpcode.opHash160, + addressProgram, + BitcoinOpcode.opEqualVerify, + BitcoinOpcode.opCheckSig ]); } @override final P2pkhAddressType type; - Script toScriptSig() { - return Script(script: [_signature, _pubkey]); - } + // Script toScriptSig() { + // return Script(script: [_signature, _pubkey]); + // } } class P2pkAddress extends LegacyAddress { static RegExp get regex => RegExp(r'1([A-Za-z0-9]{34})'); - P2pkAddress({required ECPublic publicKey}) - : _pubkeyHex = publicKey.toHex(), - super.fromPubkey(pubkey: publicKey); - - factory P2pkAddress.fromPubkey({required ECPublic pubkey}) => pubkey.toP2pkAddress(); - - P2pkAddress.fromAddress({required super.address, required super.network}) : super.fromAddress(); - - factory P2pkAddress.fromScriptPubkey({required Script script}) { - if (script.getAddressType() is! PubKeyAddressType) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); + P2pkAddress._(this.publicKey) : super._(); + factory P2pkAddress({required String publicKey}) { + final toBytes = BytesUtils.fromHexString(publicKey); + if (!Secp256k1PublicKey.isValidBytes(toBytes)) { + throw const DartBitcoinPluginException('Invalid Public key.'); } - - return P2pkAddress.fromPubkey(pubkey: ECPublic.fromHex(script.script[0])); + return P2pkAddress._(StringUtils.strip0x(publicKey.toLowerCase())); } - - late final String _pubkeyHex; + final String publicKey; @override Script toScriptPubKey() { - return Script(script: [_pubkeyHex, BitcoinOpCodeConst.OP_CHECKSIG]); + return Script(script: [publicKey, BitcoinOpcode.opCheckSig]); } @override String toAddress(BasedUtxoNetwork network) { return _BitcoinAddressUtils.legacyToAddress( - network: network, - addressProgram: _BitcoinAddressUtils.pubkeyToHash160(_pubkeyHex), - type: type, - ); + network: network, + addressProgram: _BitcoinAddressUtils.pubkeyToHash160(publicKey), + type: type); } @override final PubKeyAddressType type = PubKeyAddressType.p2pk; - @override operator ==(other) { if (identical(this, other)) return true; if (other is! P2pkAddress) return false; - return _pubkeyHex == other._pubkeyHex; + return publicKey == other.publicKey; } @override - int get hashCode => HashCodeGenerator.generateHashCode([_pubkeyHex, type]); + int get hashCode => HashCodeGenerator.generateHashCode([publicKey, type]); } diff --git a/lib/src/bitcoin/address/network_address.dart b/lib/src/bitcoin/address/network_address.dart index 2a8e1bb..a451bf7 100644 --- a/lib/src/bitcoin/address/network_address.dart +++ b/lib/src/bitcoin/address/network_address.dart @@ -3,14 +3,119 @@ part of 'package:bitcoin_base/src/bitcoin/address/address.dart'; /// An abstract class representing a forked address for a specific network. abstract class BitcoinNetworkAddress { const BitcoinNetworkAddress._( - {required this.address, required this.network, required this.baseAddress}); + {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; + 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; /// Converts the address to a string representation for the specified network [T]. String toAddress([T? updateNetwork]) { - return updateNetwork == null ? address : baseAddress.toAddress(updateNetwork); + return updateNetwork == null + ? address + : baseAddress.toAddress(updateNetwork); } /// The type of the Bitcoin address. @@ -24,24 +129,31 @@ abstract class BitcoinNetworkAddress { /// A concrete implementation of [BitcoinNetworkAddress] for Bitcoin network. class BitcoinAddress extends BitcoinNetworkAddress { - const BitcoinAddress._(BitcoinBaseAddress baseAddress, String address, BitcoinNetwork network) + const BitcoinAddress._( + BitcoinBaseAddress baseAddress, String address, BitcoinNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); - factory BitcoinAddress(String address, {BitcoinNetwork network = BitcoinNetwork.mainnet}) { - return BitcoinAddress._(_BitcoinAddressUtils.decodeAddress(address, network), address, network); + factory BitcoinAddress(String address, + {BitcoinNetwork network = BitcoinNetwork.mainnet}) { + return BitcoinAddress._( + _BitcoinAddressUtils.decodeAddress(address, network), address, network); } factory BitcoinAddress.fromBaseAddress(BitcoinBaseAddress address, {BitcoinNetwork network = BitcoinNetwork.mainnet}) { final baseAddress = _BitcoinAddressUtils.validateAddress(address, network); - return BitcoinAddress._(baseAddress, baseAddress.toAddress(network), network); + return BitcoinAddress._( + baseAddress, baseAddress.toAddress(network), network); } } /// A concrete implementation of [BitcoinNetworkAddress] for Doge network. class DogeAddress extends BitcoinNetworkAddress { - const DogeAddress._(BitcoinBaseAddress baseAddress, String address, DogecoinNetwork network) + const DogeAddress._( + BitcoinBaseAddress baseAddress, String address, DogecoinNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); - factory DogeAddress(String address, {DogecoinNetwork network = DogecoinNetwork.mainnet}) { - return DogeAddress._(_BitcoinAddressUtils.decodeAddress(address, network), address, network); + factory DogeAddress(String address, + {DogecoinNetwork network = DogecoinNetwork.mainnet}) { + return DogeAddress._( + _BitcoinAddressUtils.decodeAddress(address, network), address, network); } factory DogeAddress.fromBaseAddress(BitcoinBaseAddress address, {DogecoinNetwork network = DogecoinNetwork.mainnet}) { @@ -52,10 +164,13 @@ class DogeAddress extends BitcoinNetworkAddress { /// A concrete implementation of [BitcoinNetworkAddress] for Pepecoin network. class PepeAddress extends BitcoinNetworkAddress { - const PepeAddress._(BitcoinBaseAddress baseAddress, String address, PepeNetwork network) + const PepeAddress._( + BitcoinBaseAddress baseAddress, String address, PepeNetwork network) : super._(address: address, network: network, baseAddress: baseAddress); - factory PepeAddress(String address, {PepeNetwork network = PepeNetwork.mainnet}) { - return PepeAddress._(_BitcoinAddressUtils.decodeAddress(address, network), address, network); + factory PepeAddress(String address, + {PepeNetwork network = PepeNetwork.mainnet}) { + return PepeAddress._( + _BitcoinAddressUtils.decodeAddress(address, network), address, network); } factory PepeAddress.fromBaseAddress(BitcoinBaseAddress address, {PepeNetwork network = PepeNetwork.mainnet}) { @@ -66,32 +181,33 @@ class PepeAddress extends BitcoinNetworkAddress { /// A concrete implementation of [BitcoinNetworkAddress] for Litecoin network. class LitecoinAddress extends BitcoinNetworkAddress { - const LitecoinAddress._(BitcoinBaseAddress baseAddress, String address, LitecoinNetwork network) + const LitecoinAddress._( + BitcoinBaseAddress baseAddress, String address, LitecoinNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); - factory LitecoinAddress(String address, {LitecoinNetwork network = LitecoinNetwork.mainnet}) { + factory LitecoinAddress(String address, + {LitecoinNetwork network = LitecoinNetwork.mainnet}) { return LitecoinAddress._( _BitcoinAddressUtils.decodeAddress(address, network), address, network); } factory LitecoinAddress.fromBaseAddress(BitcoinBaseAddress address, {LitecoinNetwork network = LitecoinNetwork.mainnet}) { final baseAddress = _BitcoinAddressUtils.validateAddress(address, network); - return LitecoinAddress._(baseAddress, baseAddress.toAddress(network), network); + return LitecoinAddress._( + baseAddress, baseAddress.toAddress(network), network); } } /// A concrete implementation of [BitcoinNetworkAddress] for Bitcoin cash network. class BitcoinCashAddress extends BitcoinNetworkAddress { - const BitcoinCashAddress._( - BitcoinBaseAddress baseAddress, String address, BitcoinCashNetwork network) + const BitcoinCashAddress._(BitcoinBaseAddress baseAddress, String address, + BitcoinCashNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); factory BitcoinCashAddress(String address, {BitcoinCashNetwork network = BitcoinCashNetwork.mainnet, bool validateNetworkPrefix = false}) { final decodeAddress = _BitcoinAddressUtils.decodeBchAddress( - address, - network, - validateNetworkHRP: validateNetworkPrefix, - ); + address, network, + validateNetworkHRP: validateNetworkPrefix); if (decodeAddress == null) { throw DartBitcoinPluginException('Invalid ${network.value} address.'); } @@ -100,7 +216,8 @@ class BitcoinCashAddress extends BitcoinNetworkAddress { factory BitcoinCashAddress.fromBaseAddress(BitcoinBaseAddress address, {BitcoinCashNetwork network = BitcoinCashNetwork.mainnet}) { final baseAddress = _BitcoinAddressUtils.validateAddress(address, network); - return BitcoinCashAddress._(baseAddress, baseAddress.toAddress(network), network); + return BitcoinCashAddress._( + baseAddress, baseAddress.toAddress(network), network); } @override @@ -114,10 +231,13 @@ class BitcoinCashAddress extends BitcoinNetworkAddress { /// A concrete implementation of [BitcoinNetworkAddress] for Dash network. class DashAddress extends BitcoinNetworkAddress { - const DashAddress._(BitcoinBaseAddress baseAddress, String address, DashNetwork network) + const DashAddress._( + BitcoinBaseAddress baseAddress, String address, DashNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); - factory DashAddress(String address, {DashNetwork network = DashNetwork.mainnet}) { - return DashAddress._(_BitcoinAddressUtils.decodeAddress(address, network), address, network); + factory DashAddress(String address, + {DashNetwork network = DashNetwork.mainnet}) { + return DashAddress._( + _BitcoinAddressUtils.decodeAddress(address, network), address, network); } factory DashAddress.fromBaseAddress(BitcoinBaseAddress address, {DashNetwork network = DashNetwork.mainnet}) { @@ -128,23 +248,27 @@ class DashAddress extends BitcoinNetworkAddress { /// A concrete implementation of [BitcoinNetworkAddress] for bitcoinSV network. class BitcoinSVAddress extends BitcoinNetworkAddress { - const BitcoinSVAddress._(BitcoinBaseAddress baseAddress, String address, BitcoinSVNetwork network) + const BitcoinSVAddress._( + BitcoinBaseAddress baseAddress, String address, BitcoinSVNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); - factory BitcoinSVAddress(String address, {BitcoinSVNetwork network = BitcoinSVNetwork.mainnet}) { + factory BitcoinSVAddress(String address, + {BitcoinSVNetwork network = BitcoinSVNetwork.mainnet}) { return BitcoinSVAddress._( _BitcoinAddressUtils.decodeAddress(address, network), address, network); } factory BitcoinSVAddress.fromBaseAddress(BitcoinBaseAddress address, {BitcoinSVNetwork network = BitcoinSVNetwork.mainnet}) { final baseAddress = _BitcoinAddressUtils.validateAddress(address, network); - return BitcoinSVAddress._(baseAddress, baseAddress.toAddress(network), network); + return BitcoinSVAddress._( + baseAddress, baseAddress.toAddress(network), network); } } /// A concrete implementation of [BitcoinNetworkAddress] for Electra protocol network. -class ElectraProtocolAddress extends BitcoinNetworkAddress { - const ElectraProtocolAddress._( - BitcoinBaseAddress baseAddress, String address, ElectraProtocolNetwork network) +class ElectraProtocolAddress + extends BitcoinNetworkAddress { + const ElectraProtocolAddress._(BitcoinBaseAddress baseAddress, String address, + ElectraProtocolNetwork network) : super._(address: address, baseAddress: baseAddress, network: network); factory ElectraProtocolAddress(String address, {ElectraProtocolNetwork network = ElectraProtocolNetwork.mainnet}) { @@ -154,6 +278,7 @@ class ElectraProtocolAddress extends BitcoinNetworkAddress HashCodeGenerator.generateHashCode([addressProgram, segwitVersion, type]); + int get hashCode => + HashCodeGenerator.generateHashCode([addressProgram, segwitVersion, type]); } class P2wpkhAddress extends SegwitAddress { - static final regex = RegExp(r'(bc|tb|ltc)1q[ac-hj-np-z02-9]{25,39}'); + static RegExp get regex => RegExp(r'(bc|tb|ltc)1q[ac-hj-np-z02-9]{25,39}'); P2wpkhAddress.fromAddress({required super.address, required super.network}) : super.fromAddress(segwitVersion: _BitcoinAddressUtils.segwitV0); P2wpkhAddress.fromProgram({required super.program}) : super.fromProgram( - segwitVersion: _BitcoinAddressUtils.segwitV0, - addressType: SegwitAddressType.p2wpkh, - ); - - P2wpkhAddress.fromRedeemScript({required super.script}) - : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - - factory P2wpkhAddress.fromDerivation({ - required Bip32Base bip32, - required BitcoinDerivationInfo derivationInfo, - required bool isChange, - required int index, - }) { - final fullPath = derivationInfo.derivationPath - .addElem(Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(isChange))) - .addElem(Bip32KeyIndex(index)); + segwitVersion: _BitcoinAddressUtils.segwitV0, + addresType: SegwitAddressType.p2wpkh); - return ECPublic.fromBip32(bip32.derive(fullPath).publicKey).toP2wpkhAddress(); - } - factory P2wpkhAddress.fromPath({required Bip32Base bip32, required Bip32Path path}) { - return ECPublic.fromBip32(bip32.derive(path).publicKey).toP2wpkhAddress(); - } + // P2wpkhAddress.fromRedeemScript({required super.script, super.network}) + // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - factory P2wpkhAddress.fromScriptPubkey({required Script script}) { + factory P2wpkhAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { if (script.getAddressType() != SegwitAddressType.p2wpkh) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); + throw ArgumentError("Invalid scriptPubKey"); } return P2wpkhAddress.fromProgram(program: script.findScriptParam(1)); @@ -105,7 +86,7 @@ class P2wpkhAddress extends SegwitAddress { /// returns the scriptPubKey of a P2WPKH witness script @override Script toScriptPubKey() { - return Script(script: [BitcoinOpCodeConst.OP_0, addressProgram]); + return Script(script: [BitcoinOpcode.op0, addressProgram]); } /// returns the type of address @@ -114,60 +95,34 @@ class P2wpkhAddress extends SegwitAddress { } class P2trAddress extends SegwitAddress { - static final regex = + static RegExp get regex => RegExp(r'(bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89})'); + P2trAddress.fromAddress({required super.address, required super.network}) + : super.fromAddress(segwitVersion: _BitcoinAddressUtils.segwitV1); + P2trAddress.fromProgram({required super.program}) + : super.fromProgram( + segwitVersion: _BitcoinAddressUtils.segwitV1, + addresType: SegwitAddressType.p2tr); P2trAddress.fromInternalKey({ required List internalKey, TaprootTree? treeScript, List? merkleRoot, - bool tweak = true, }) : super.fromProgram( - program: tweak - ? BytesUtils.toHexString( - TaprootUtils.tweakPublicKey( + program: BytesUtils.toHexString(TaprootUtils.tweakPublicKey( internalKey, treeScript: treeScript, - merkleRoot: merkleRoot, - ).toXonly(), - ) - : BytesUtils.toHexString(internalKey), - pubkey: ECPublic.fromBytes(internalKey), - segwitVersion: _BitcoinAddressUtils.segwitV1, - addressType: SegwitAddressType.p2tr, - ); - - P2trAddress.fromAddress({required super.address, required super.network}) - : super.fromAddress(segwitVersion: _BitcoinAddressUtils.segwitV1); + merkleRoot: merkleRoot) + .toXonly()), + segwitVersion: _BitcoinAddressUtils.segwitV1, + addresType: SegwitAddressType.p2tr); - P2trAddress.fromProgram({required super.program, super.pubkey}) - : super.fromProgram( - segwitVersion: _BitcoinAddressUtils.segwitV1, - addressType: SegwitAddressType.p2tr, - ); - - P2trAddress.fromRedeemScript({required super.script}) - : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV1); - - factory P2trAddress.fromDerivation({ - required Bip32Base bip32, - required BitcoinDerivationInfo derivationInfo, - required bool isChange, - required int index, - }) { - final fullPath = derivationInfo.derivationPath - .addElem(Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(isChange))) - .addElem(Bip32KeyIndex(index)); - return ECPublic.fromBip32(bip32.derive(fullPath).publicKey).toP2trAddress(); - } + // P2trAddress.fromRedeemScript({required super.script, super.network}) + // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV1); - factory P2trAddress.fromPath({required Bip32Base bip32, required Bip32Path path}) { - return ECPublic.fromBip32(bip32.derive(path).publicKey).toP2trAddress(); - } - - factory P2trAddress.fromScriptPubkey({required Script script}) { + factory P2trAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { if (script.getAddressType() != SegwitAddressType.p2tr) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); + throw ArgumentError("Invalid scriptPubKey"); } return P2trAddress.fromProgram(program: script.findScriptParam(1)); @@ -176,7 +131,7 @@ class P2trAddress extends SegwitAddress { /// returns the scriptPubKey of a P2TR witness script @override Script toScriptPubKey() { - return Script(script: [BitcoinOpCodeConst.OP_1, addressProgram]); + return Script(script: [BitcoinOpcode.op1, addressProgram]); } /// returns the type of address @@ -185,35 +140,23 @@ class P2trAddress extends SegwitAddress { } class P2wshAddress extends SegwitAddress { - static final regex = RegExp(r'(bc|tb)1q[ac-hj-np-z02-9]{40,80}'); + static RegExp get regex => RegExp(r'(bc|tb)1q[ac-hj-np-z02-9]{40,80}'); P2wshAddress.fromAddress({required super.address, required super.network}) : super.fromAddress(segwitVersion: _BitcoinAddressUtils.segwitV0); - P2wshAddress.fromProgram({required super.program}) : super.fromProgram( - segwitVersion: _BitcoinAddressUtils.segwitV0, - addressType: SegwitAddressType.p2wsh, - ); + segwitVersion: _BitcoinAddressUtils.segwitV0, + addresType: SegwitAddressType.p2wsh); + P2wshAddress.fromScript({required super.script}) + : super.fromScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - P2wshAddress.fromRedeemScript({required super.script}) - : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - - factory P2wshAddress.fromDerivation({ - required Bip32Base bip32, - required BitcoinDerivationInfo derivationInfo, - required bool isChange, - required int index, - }) { - final fullPath = derivationInfo.derivationPath - .addElem(Bip32KeyIndex(BitcoinAddressUtils.getAccountFromChange(isChange))) - .addElem(Bip32KeyIndex(index)); - return ECPublic.fromBip32(bip32.derive(fullPath).publicKey).toP2wshAddress(); - } + // P2wshAddress.fromRedeemScript({required super.script, super.network}) + // : super.fromRedeemScript(segwitVersion: _BitcoinAddressUtils.segwitV0); - factory P2wshAddress.fromScriptPubkey({required Script script}) { + factory P2wshAddress.fromScriptPubkey({required Script script, BasedUtxoNetwork? network}) { if (script.getAddressType() != SegwitAddressType.p2wsh) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); + throw ArgumentError("Invalid scriptPubKey"); } return P2wshAddress.fromProgram(program: script.findScriptParam(1)); @@ -222,7 +165,7 @@ class P2wshAddress extends SegwitAddress { /// Returns the scriptPubKey of a P2WPKH witness script @override Script toScriptPubKey() { - return Script(script: [BitcoinOpCodeConst.OP_0, addressProgram]); + return Script(script: [BitcoinOpcode.op0, addressProgram]); } /// Returns the type of address @@ -233,7 +176,7 @@ class P2wshAddress extends SegwitAddress { class MwebAddress extends SegwitAddress { static RegExp get regex => RegExp(r'(ltc|t)mweb1q[ac-hj-np-z02-9]{90,120}'); - factory MwebAddress.fromAddress({required String address}) { + factory MwebAddress.fromAddress({required String address, required BasedUtxoNetwork network}) { final decoded = Bech32DecoderBase.decodeBech32( address, Bech32Const.separator, @@ -242,15 +185,14 @@ class MwebAddress extends SegwitAddress { final hrp = decoded.item1; final data = decoded.item2; if (hrp != 'ltcmweb') { - throw DartBitcoinPluginException( - 'Invalid format (HRP not valid, expected ltcmweb, got $hrp)'); + throw ArgumentException('Invalid format (HRP not valid, expected ltcmweb, got $hrp)'); } if (data[0] != _BitcoinAddressUtils.segwitV0) { - throw DartBitcoinPluginException("Invalid segwit version"); + throw const ArgumentException("Invalid segwit version"); } final convData = Bech32BaseUtils.convertFromBase32(data.sublist(1)); if (convData.length != 66) { - throw DartBitcoinPluginException( + throw ArgumentException( 'Invalid format (witness program length not valid: ${convData.length})'); } @@ -260,14 +202,14 @@ class MwebAddress extends SegwitAddress { MwebAddress.fromProgram({required super.program}) : super.fromProgram( segwitVersion: _BitcoinAddressUtils.segwitV0, - addressType: SegwitAddressType.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 = SegwitAddressType.mweb}) { if (script.getAddressType() != SegwitAddressType.mweb) { - throw DartBitcoinPluginException("Invalid scriptPubKey"); + throw ArgumentError("Invalid scriptPubKey"); } return MwebAddress.fromProgram(program: BytesUtils.toHexString(script.script as List)); } 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/address/utils/address_utils.dart b/lib/src/bitcoin/address/utils/address_utils.dart index 881634e..784b8c3 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; @@ -18,13 +18,15 @@ class _BitcoinAddressUtils { /// /// [address]: The legacy Bitcoin address to be decoded. /// Returns a tuple with script bytes and version if decoding is successful, otherwise null. - static Tuple, List>? decodeLagacyAddress({required String address}) { + static Tuple, List>? decodeLagacyAddress( + {required String address}) { try { /// Decode the base58-encoded address. final decode = List.unmodifiable(Base58Decoder.decode(address)); /// Extract script bytes excluding version and checksum. - final scriptBytes = decode.sublist(1, decode.length - Base58Const.checksumByteLen); + final scriptBytes = + decode.sublist(1, decode.length - Base58Const.checksumByteLen); /// Ensure the script bytes have the expected length. if (scriptBytes.length != hash160DigestLength) { @@ -33,11 +35,14 @@ class _BitcoinAddressUtils { /// Extract version, data, and checksum. final version = [decode[0]]; - final data = decode.sublist(0, decode.length - Base58Const.checksumByteLen); - final checksum = decode.sublist(decode.length - Base58Const.checksumByteLen); + final data = + decode.sublist(0, decode.length - Base58Const.checksumByteLen); + final checksum = + decode.sublist(decode.length - Base58Const.checksumByteLen); /// Verify the checksum. - final hash = QuickCrypto.sha256DoubleHash(data).sublist(0, Base58Const.checksumByteLen); + final hash = QuickCrypto.sha256DoubleHash(data) + .sublist(0, Base58Const.checksumByteLen); if (!BytesUtils.bytesEqual(checksum, hash)) { return null; } @@ -61,9 +66,9 @@ class _BitcoinAddressUtils { } final decodedHex = BytesUtils.toHexString(decode.item1); if (BytesUtils.bytesEqual(decode.item2, networks.p2pkhNetVer)) { - return P2pkhAddress.fromHash160(h160: decodedHex); + return P2pkhAddress.fromHash160(addrHash: decodedHex); } else if (BytesUtils.bytesEqual(decode.item2, networks.p2shNetVer)) { - return P2shAddress.fromHash160(h160: decodedHex); + return P2shAddress.fromHash160(addrHash: decodedHex); } return null; } @@ -78,7 +83,9 @@ class _BitcoinAddressUtils { /// /// Throws a [MessageException] if the witness version does not match the specified version. static String toSegwitProgramWithVersionAndNetwork( - {required String address, required BasedUtxoNetwork network, required int version}) { + {required String address, + required BasedUtxoNetwork network, + required int version}) { final convert = SegwitBech32Decoder.decode(network.p2wpkhHrp, address); final witnessVersion = convert.item1; if (witnessVersion != version) { @@ -95,11 +102,8 @@ class _BitcoinAddressUtils { /// /// Returns a SegwitAddress instance representing the converted SegWit address, /// or null if the conversion is not successful. - static SegwitAddress? toSegwitAddress(String address, BasedUtxoNetwork network) { - return toP2wpkhAddress(address, network); - } - - static SegwitAddress? toP2wpkhAddress(String address, BasedUtxoNetwork network) { + static SegwitAddress? toSegwitAddress( + String address, BasedUtxoNetwork network) { try { final convert = SegwitBech32Decoder.decode(network.p2wpkhHrp, address); final witnessVersion = convert.item1; @@ -128,7 +132,8 @@ class _BitcoinAddressUtils { /// Returns the validated Bitcoin base address if it belongs to a supported type for the given network. /// /// Throws a [MessageException] if the address type is not supported by the specified network. - static BitcoinBaseAddress validateAddress(BitcoinBaseAddress address, BasedUtxoNetwork network) { + static BitcoinBaseAddress validateAddress( + BitcoinBaseAddress address, BasedUtxoNetwork network) { if (network.supportedAddress.contains(address.type)) { return address; } @@ -145,14 +150,15 @@ class _BitcoinAddressUtils { /// Returns a BitcoinBaseAddress instance representing the decoded address. /// /// Throws a [MessageException] if the address is invalid or not supported by the network. - static BitcoinBaseAddress decodeAddress(String address, BasedUtxoNetwork network) { + static BitcoinBaseAddress decodeAddress( + String address, BasedUtxoNetwork network) { BitcoinBaseAddress? baseAddress; if (network.supportedAddress.contains(SegwitAddressType.p2wpkh)) { baseAddress = toSegwitAddress(address, network); } baseAddress ??= toLegacy(address, network); if (baseAddress == null) { - throw const DartBitcoinPluginException('Invalid Bitcoin address'); + throw const DartBitcoinPluginException('Invalid Bitcoin address.'); } return validateAddress(baseAddress, network); } @@ -165,14 +171,14 @@ class _BitcoinAddressUtils { /// Returns the validated hash160 value if its length matches the expected length for the specified address type. /// /// Throws a [MessageException] if the hash160 value is invalid or has an incorrect length. - static String validateAddressProgram(String hash160, BitcoinAddressType addressType) { + static String validateAddressProgram( + String hash160, BitcoinAddressType addressType) { try { final toBytes = BytesUtils.fromHexString(hash160); 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)'); } @@ -186,16 +192,19 @@ class _BitcoinAddressUtils { /// /// Returns a LegacyAddress instance representing the decoded BCH address if successful, /// or null if the decoding process fails. - static LegacyAddress? decodeBchAddress(String address, BitcoinCashNetwork network, + static LegacyAddress? decodeBchAddress( + String address, BitcoinCashNetwork network, {bool validateNetworkHRP = false}) { try { - final hrp = - validateNetworkHRP ? network.networkHRP : address.substring(0, address.indexOf(':')); + final hrp = validateNetworkHRP + ? network.networkHRP + : address.substring(0, address.indexOf(':')); final decode = BchBech32Decoder.decode(hrp, address); final scriptBytes = decode.item2; final version = decode.item1; - return _validateBchScriptBytes(network: network, scriptBytes: scriptBytes, version: version); - } catch (e) { + return _validateBchScriptBytes( + network: network, scriptBytes: scriptBytes, version: version); + } catch (_) { return null; } } @@ -214,7 +223,8 @@ class _BitcoinAddressUtils { required BitcoinCashNetwork network}) { final scriptHex = BytesUtils.toHexString(scriptBytes); final scriptLength = scriptBytes.length; - if (scriptLength != hash160DigestLength && scriptLength != scriptHashLenght) { + if (scriptLength != hash160DigestLength && + scriptLength != scriptHashLenght) { return null; } if (scriptLength == hash160DigestLength) { @@ -223,22 +233,28 @@ class _BitcoinAddressUtils { if (BytesUtils.bytesEqual(network.p2pkhNetVer, version) || BytesUtils.bytesEqual(network.p2pkhWtNetVer, version)) { return P2pkhAddress.fromHash160( - h160: scriptHex, type: legacyP2pk ? P2pkhAddressType.p2pkh : P2pkhAddressType.p2pkhwt); + addrHash: scriptHex, + type: + legacyP2pk ? P2pkhAddressType.p2pkh : P2pkhAddressType.p2pkhwt); } final legacyP2sh = BytesUtils.bytesEqual(network.p2shNetVer, version); if (BytesUtils.bytesEqual(network.p2shNetVer, version) || BytesUtils.bytesEqual(network.p2shwt20NetVer, version)) { return P2shAddress.fromHash160( - h160: scriptHex, - type: legacyP2sh ? P2shAddressType.p2pkhInP2sh : P2shAddressType.p2pkhInP2shwt); + addrHash: scriptHex, + type: legacyP2sh + ? P2shAddressType.p2pkhInP2sh + : P2shAddressType.p2pkhInP2shwt); } } else { final legacyP2sh = BytesUtils.bytesEqual(network.p2sh32NetVer, version); if (BytesUtils.bytesEqual(network.p2sh32NetVer, version) || BytesUtils.bytesEqual(network.p2shwt32NetVer, version)) { return P2shAddress.fromHash160( - h160: scriptHex, - type: legacyP2sh ? P2shAddressType.p2pkhInP2sh32 : P2shAddressType.p2pkhInP2sh32wt); + addrHash: scriptHex, + type: legacyP2sh + ? P2shAddressType.p2pkhInP2sh32 + : P2shAddressType.p2pkhInP2sh32wt); } } return null; @@ -254,7 +270,7 @@ class _BitcoinAddressUtils { /// Returns the address program in hexadecimal format if successful, or null if decoding or validation fails. /// /// Throws a [MessageException] if the specified network does not support the given address type. - static String? decodeLegacyAddressWithNetworkAndType( + static String? decodeLagacyAddressWithNetworkAndType( {required String address, required BitcoinAddressType type, required BasedUtxoNetwork network}) { @@ -276,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)) { @@ -319,7 +334,8 @@ class _BitcoinAddressUtils { required BasedUtxoNetwork network, required int segwitVersion}) { final programBytes = BytesUtils.fromHexString(addressProgram); - return SegwitBech32Encoder.encode(network.p2wpkhHrp, segwitVersion, programBytes); + return SegwitBech32Encoder.encode( + network.p2wpkhHrp, segwitVersion, programBytes); } /// Converts a Bitcoin legacy address program to its corresponding Bitcoin Cash (BCH) address. @@ -334,10 +350,11 @@ class _BitcoinAddressUtils { required String addressProgram, required BitcoinAddressType type}) { final programBytes = BytesUtils.fromHexString(addressProgram); - final netVersion = - _getBchNetVersion(network: network, type: type, secriptLength: programBytes.length); + final netVersion = _getBchNetVersion( + network: network, type: type, secriptLength: programBytes.length); - return BchBech32Encoder.encode(network.networkHRP, netVersion, programBytes); + return BchBech32Encoder.encode( + network.networkHRP, netVersion, programBytes); } /// Helper method to obtain the Bitcoin Cash network version bytes based on the address type and script length. @@ -384,7 +401,8 @@ class _BitcoinAddressUtils { required String addressProgram, required BitcoinAddressType type}) { if (network is BitcoinCashNetwork) { - return legacyToBchAddress(addressProgram: addressProgram, network: network, type: type); + return legacyToBchAddress( + addressProgram: addressProgram, network: network, type: type); } var programBytes = BytesUtils.fromHexString(addressProgram); switch (type) { @@ -428,7 +446,7 @@ class _BitcoinAddressUtils { /// Returns the hexadecimal representation of the reversed SHA-256 hash160 of the script's static String pubKeyHash(Script scriptPubKey) { - return BytesUtils.toHexString( - List.from(QuickCrypto.sha256Hash(scriptPubKey.toBytes()).reversed)); + return BytesUtils.toHexString(List.from( + QuickCrypto.sha256Hash(scriptPubKey.toBytes()).reversed)); } } diff --git a/lib/src/bitcoin/script/input.dart b/lib/src/bitcoin/script/input.dart index e0e565e..ea70f26 100644 --- a/lib/src/bitcoin/script/input.dart +++ b/lib/src/bitcoin/script/input.dart @@ -1,6 +1,9 @@ import 'dart:typed_data'; import 'package:bitcoin_base/src/bitcoin/script/op_code/constant.dart'; +import 'package:bitcoin_base/src/exception/exception.dart'; +import 'package:blockchain_utils/crypto/quick_crypto.dart'; +import 'package:blockchain_utils/helper/extensions/extensions.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'script.dart'; @@ -11,26 +14,43 @@ import 'script.dart'; /// [scriptSig] the script that satisfies the locking conditions /// [sequence] the input sequence (for timelocks, RBF, etc.) class TxInput { - TxInput({ - required this.txId, - required this.txIndex, - Script? scriptSig, - List? sequence, - }) : sequence = List.unmodifiable( - sequence ?? BitcoinOpCodeConst.defaultTxSequence, - ), + TxInput._( + {required this.txId, + required this.txIndex, + Script? scriptSig, + List? sequance}) + : sequence = + (sequance ?? BitcoinOpCodeConst.defaultTxSequence).asImmutableBytes, scriptSig = scriptSig ?? Script(script: []); - TxInput copyWith({ - String? txId, - int? txIndex, - Script? scriptSig, - List? sequence, - }) { + factory TxInput( + {required String txId, + required int txIndex, + Script? scriptSig, + List? sequance}) { + if (sequance != null && + sequance.length != BitcoinOpCodeConst.sequenceLengthInBytes) { + throw DartBitcoinPluginException( + "Invalid sequence length: expected ${BitcoinOpCodeConst.sequenceLengthInBytes}, but got ${sequance.length}."); + } + final txBytes = BytesUtils.tryFromHexString(txId); + if (txBytes?.length != QuickCrypto.sha256DigestSize) { + throw DartBitcoinPluginException( + "Invalid transaction ID: Expected ${QuickCrypto.sha256DigestSize} bytes, but received a different length.", + details: {"transactionID": txId}); + } + return TxInput._( + txId: StringUtils.strip0x(txId.toLowerCase()), + txIndex: txIndex, + sequance: sequance, + scriptSig: scriptSig); + } + TxInput copyWith( + {String? txId, int? txIndex, Script? scriptSig, List? sequence}) { return TxInput( txId: txId ?? this.txId, txIndex: txIndex ?? this.txIndex, scriptSig: scriptSig ?? this.scriptSig, - sequence: sequence ?? this.sequence); + sequance: sequence ?? this.sequence); } final String txId; @@ -40,19 +60,15 @@ class TxInput { /// creates a copy of the object TxInput clone() { - return copy(); - } - - TxInput copy() { - return TxInput(txId: txId, txIndex: txIndex, scriptSig: scriptSig, sequence: sequence); + return TxInput( + txId: txId, txIndex: txIndex, scriptSig: scriptSig, sequance: sequence); } /// serializes TxInput to bytes List toBytes() { final txidBytes = BytesUtils.fromHexString(txId).reversed.toList(); - - final txoutBytes = IntUtils.toBytes(txIndex, length: 4, byteOrder: Endian.little); - // writeUint32LE(txIndex, txoutBytes); + final txoutBytes = + IntUtils.toBytes(txIndex, length: 4, byteOrder: Endian.little); final scriptSigBytes = scriptSig.toBytes(); final scriptSigLengthVarint = IntUtils.encodeVarint(scriptSigBytes.length); final data = List.from([ @@ -60,30 +76,17 @@ class TxInput { ...txoutBytes, ...scriptSigLengthVarint, ...scriptSigBytes, - ...sequence, + ...sequence ]); return data; } - static Tuple deserialize({ - required List bytes, - String? raw, - int cursor = 0, - bool hasSegwit = false, - }) { - return fromRaw(bytes: bytes, cursor: cursor, hasSegwit: hasSegwit); - } - - static Tuple fromRaw({ - List? bytes, - String? raw, - int cursor = 0, - bool hasSegwit = false, - }) { - bytes ??= BytesUtils.fromHexString(raw!); + static Tuple deserialize( + {required List bytes, int cursor = 0}) { final inpHash = bytes.sublist(cursor, cursor + 32).reversed.toList(); cursor += 32; - final outputN = IntUtils.fromBytes(bytes.sublist(cursor, cursor + 4), byteOrder: Endian.little); + final outputN = IntUtils.fromBytes(bytes.sublist(cursor, cursor + 4), + byteOrder: Endian.little); cursor += 4; final vi = IntUtils.decodeVarint(bytes.sublist(cursor)); cursor += vi.item2; @@ -95,8 +98,8 @@ class TxInput { TxInput( txId: BytesUtils.toHexString(inpHash), txIndex: outputN, - scriptSig: Script.deserialize(bytes: unlockingScript, hasSegwit: hasSegwit), - sequence: sequenceNumberData), + scriptSig: Script.deserialize(bytes: unlockingScript), + sequance: sequenceNumberData), cursor); } @@ -113,7 +116,7 @@ class TxInput { 'txid': txId, 'txIndex': txIndex, 'scriptSig': scriptSig.toJson(), - 'sequence': BytesUtils.toHexString(sequence), + 'sequance': BytesUtils.toHexString(sequence), }; } @@ -132,5 +135,6 @@ class TxInput { } @override - int get hashCode => HashCodeGenerator.generateHashCode([txIndex, txId, sequence]); + int get hashCode => + HashCodeGenerator.generateHashCode([txIndex, txId, sequence]); } diff --git a/lib/src/bitcoin/script/op_code/constant.dart b/lib/src/bitcoin/script/op_code/constant.dart index 205b0c2..59ac86e 100644 --- a/lib/src/bitcoin/script/op_code/constant.dart +++ b/lib/src/bitcoin/script/op_code/constant.dart @@ -117,7 +117,6 @@ enum BitcoinOpcode { /// ignore_for_file: constant_identifier_names, equal_keys_in_map, non_constant_identifier_names /// Constants and identifiers used in the Bitcoin-related code. // ignore_for_file: constant_identifier_names, non_constant_identifier_names, equal_keys_in_map - class BitcoinOpCodeConst { static const int opPushData1 = 0x4c; static const int opPushData2 = 0x4d; @@ -128,124 +127,6 @@ class BitcoinOpCodeConst { byte == BitcoinOpCodeConst.opPushData4; } - static const OP_0 = "OP_0"; - static const OP_FALSE = "OP_FALSE"; - static const OP_PUSHDATA1 = "OP_PUSHDATA1"; - static const OP_PUSHDATA2 = "OP_PUSHDATA2"; - static const OP_PUSHDATA4 = "OP_PUSHDATA4"; - static const OP_1NEGATE = "OP_1NEGATE"; - static const OP_1 = "OP_1"; - static const OP_TRUE = "OP_TRUE"; - static const OP_2 = "OP_2"; - static const OP_3 = "OP_3"; - static const OP_4 = "OP_4"; - static const OP_5 = "OP_5"; - static const OP_6 = "OP_6"; - static const OP_7 = "OP_7"; - static const OP_8 = "OP_8"; - static const OP_9 = "OP_9"; - static const OP_10 = "OP_10"; - static const OP_11 = "OP_11"; - static const OP_12 = "OP_12"; - static const OP_13 = "OP_13"; - static const OP_14 = "OP_14"; - static const OP_15 = "OP_15"; - static const OP_16 = "OP_16"; - - /// flow control - static const OP_NOP = "OP_NOP"; - static const OP_IF = "OP_IF"; - static const OP_NOTIF = "OP_NOTIF"; - static const OP_ELSE = "OP_ELSE"; - static const OP_ENDIF = "OP_ENDIF"; - static const OP_VERIFY = "OP_VERIFY"; - static const OP_RETURN = "OP_RETURN"; - - /// stack - static const OP_TOALTSTACK = "OP_TOALTSTACK"; - static const OP_FROMALTSTACK = "OP_FROMALTSTACK"; - static const OP_IFDUP = "OP_IFDUP"; - static const OP_DEPTH = "OP_DEPTH"; - static const OP_DROP = "OP_DROP"; - static const OP_DUP = "OP_DUP"; - static const OP_NIP = "OP_NIP"; - static const OP_OVER = "OP_OVER"; - static const OP_PICK = "OP_PICK"; - static const OP_ROLL = "OP_ROLL"; - static const OP_ROT = "OP_ROT"; - static const OP_SWAP = "OP_SWAP"; - static const OP_TUCK = "OP_TUCK"; - static const OP_2DROP = "OP_2DROP"; - static const OP_2DUP = "OP_2DUP"; - static const OP_3DUP = "OP_3DUP"; - static const OP_2OVER = "OP_2OVER"; - static const OP_2ROT = "OP_2ROT"; - static const OP_2SWAP = "OP_2SWAP"; - - /// splice - /// 'OP_CAT': [0x7e], - /// 'OP_SUBSTR': [0x7f], - /// 'OP_LEFT': [0x80], - /// 'OP_RIGHT': [0x81], - static const OP_SIZE = "OP_SIZE"; - - /// bitwise logic - /// 'OP_INVERT': [0x83], - /// 'OP_AND': [0x84], - /// 'OP_OR': [0x85], - /// 'OP_XOR': [0x86], - static const OP_EQUAL = "OP_EQUAL"; - static const OP_EQUALVERIFY = "OP_EQUALVERIFY"; - - /// arithmetic - static const OP_1ADD = "OP_1ADD"; - static const OP_1SUB = "OP_1SUB"; - - /// 'OP_2MUL': [0x8d], - /// 'OP_2DIV': [0x8e], - static const OP_NEGATE = "OP_NEGATE"; - static const OP_ABS = "OP_ABS"; - static const OP_NOT = "OP_NOT"; - static const OP_0NOTEQUAL = "OP_0NOTEQUAL"; - static const OP_ADD = "OP_ADD"; - static const OP_SUB = "OP_SUB"; - - /// 'OP_MUL': [0x95], - /// 'OP_DIV': [0x96], - /// 'OP_MOD': [0x97], - /// 'OP_LSHIFT': [0x98], - /// 'OP_RSHIFT': [0x99], - static const OP_BOOLAND = "OP_BOOLAND"; - static const OP_BOOLOR = "OP_BOOLOR"; - static const OP_NUMEQUAL = "OP_NUMEQUAL"; - static const OP_NUMEQUALVERIFY = "OP_NUMEQUALVERIFY"; - static const OP_NUMNOTEQUAL = "OP_NUMNOTEQUAL"; - static const OP_LESSTHAN = "OP_LESSTHAN"; - static const OP_GREATERTHAN = "OP_GREATERTHAN"; - static const OP_LESSTHANOREQUAL = "OP_LESSTHANOREQUAL"; - static const OP_GREATERTHANOREQUAL = "OP_GREATERTHANOREQUAL"; - static const OP_MIN = "OP_MIN"; - static const OP_MAX = "OP_MAX"; - static const OP_WITHIN = "OP_WITHIN"; - - /// crypto - static const OP_RIPEMD160 = "OP_RIPEMD160"; - static const OP_SHA1 = "OP_SHA1"; - static const OP_SHA256 = "OP_SHA256"; - static const OP_HASH160 = "OP_HASH160"; - static const OP_HASH256 = "OP_HASH256"; - static const OP_CODESEPARATOR = "OP_CODESEPARATOR"; - static const OP_CHECKSIG = "OP_CHECKSIG"; - static const OP_CHECKSIGVERIFY = "OP_CHECKSIGVERIFY"; - static const OP_CHECKMULTISIG = "OP_CHECKMULTISIG"; - static const OP_CHECKMULTISIGVERIFY = "OP_CHECKMULTISIGVERIFY"; - - /// locktime - static const OP_NOP2 = "OP_NOP2"; - static const OP_CHECKLOCKTIMEVERIFY = "OP_CHECKLOCKTIMEVERIFY"; - static const OP_NOP3 = "OP_NOP3"; - static const OP_CHECKSEQUENCEVERIFY = "OP_CHECKSEQUENCEVERIFY"; - static const int sighashSingle = 0x03; static const int sighashAnyoneCanPay = 0x80; static const int sighashAll = 0x01; @@ -284,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/output.dart b/lib/src/bitcoin/script/output.dart index 572475e..fc172f2 100644 --- a/lib/src/bitcoin/script/output.dart +++ b/lib/src/bitcoin/script/output.dart @@ -13,36 +13,29 @@ import 'package:bitcoin_base/src/bitcoin/script/op_code/constant.dart'; class TxOutput { factory TxOutput.negativeOne() { return TxOutput._( - amount: BitcoinOpCodeConst.negativeSatoshi, - scriptPubKey: Script(script: []), - ); + amount: BitcoinOpCodeConst.negativeSatoshi, + scriptPubKey: Script(script: [])); } // BitcoinOpCodeConst.negativeSatoshi - const TxOutput._({ - required this.amount, - required this.scriptPubKey, - this.cashToken, - this.isSilentPayment = false, - this.isChange = false, - }); - factory TxOutput({ - required BigInt amount, - required Script scriptPubKey, - CashToken? cashToken, - bool isSilentPayment = false, - bool isChange = false, - }) { + const TxOutput._( + {required this.amount, required this.scriptPubKey, this.cashToken, this.isSilentPayment = false, this.isChange = false}); + factory TxOutput( + {required BigInt amount, + required Script scriptPubKey, + CashToken? cashToken, + bool isSilentPayment = false, + bool isChange = false, + }) { try { return TxOutput._( - amount: amount.asUint64, - scriptPubKey: scriptPubKey, - cashToken: cashToken, - isSilentPayment: isSilentPayment, - isChange: isChange, + amount: amount.asInt64, + scriptPubKey: scriptPubKey, + cashToken: cashToken, + isSilentPayment: isSilentPayment, + isChange: isChange, ); } catch (_) { - throw DartBitcoinPluginException( - "Invalid output amount: must be a non-negative 64-bit integer."); + throw DartBitcoinPluginException("Invalid output amount."); } } final CashToken? cashToken; @@ -51,20 +44,8 @@ class TxOutput { final bool isSilentPayment; final bool isChange; - Map toJson() { - return { - 'cashToken': cashToken?.toJson(), - 'amount': amount.toString(), - 'scriptPubKey': scriptPubKey.script - }; - } - /// creates a copy of the object TxOutput clone() { - return copy(); - } - - TxOutput copy() { return TxOutput( amount: amount, scriptPubKey: Script(script: List.from(scriptPubKey.script)), @@ -75,43 +56,25 @@ class TxOutput { } List toBytes() { - final amountBytes = BigintUtils.toBytes( - amount, - length: 8, - order: Endian.little, - ); + final amountBytes = + BigintUtils.toBytes(amount, length: 8, order: Endian.little); final scriptBytes = [ ...cashToken?.toBytes() ?? [], - ...scriptPubKey.toBytes(), + ...scriptPubKey.toBytes() ]; final data = [ ...amountBytes, ...IntUtils.encodeVarint(scriptBytes.length), - ...scriptBytes, + ...scriptBytes ]; return data; } - static Tuple deserialize({ - required int cursor, - List? bytes, - String? raw, - bool hasSegwit = false, - }) { - return fromRaw(bytes: bytes, cursor: cursor, hasSegwit: hasSegwit); - } - - static Tuple fromRaw({ - required int cursor, - List? bytes, - String? raw, - bool hasSegwit = false, - }) { - bytes ??= BytesUtils.fromHexString(raw!); - final value = BigintUtils.fromBytes( - bytes.sublist(cursor, cursor + 8), - byteOrder: Endian.little, - ).toSigned(64); + static Tuple deserialize( + {required List bytes, required int cursor}) { + final value = BigintUtils.fromBytes(bytes.sublist(cursor, cursor + 8), + byteOrder: Endian.little) + .toSigned(64); cursor += 8; final vi = IntUtils.decodeVarint(bytes.sublist(cursor)); @@ -128,9 +91,17 @@ class TxOutput { cursor); } + Map toJson() { + return { + 'cashToken': cashToken?.toJson(), + 'amount': amount.toString(), + 'scriptPubKey': scriptPubKey.script + }; + } + @override String toString() { - return 'TxOutput{cashToken: ${cashToken?.toString()}, amount: $amount, script: ${scriptPubKey.toString()}}'; + return 'TxOutput{cashToken: ${cashToken?.toString()}}, amount: $amount, script: ${scriptPubKey.toString()}}'; } @override @@ -143,5 +114,6 @@ class TxOutput { } @override - int get hashCode => HashCodeGenerator.generateHashCode([amount, scriptPubKey, cashToken]); + int get hashCode => + HashCodeGenerator.generateHashCode([amount, scriptPubKey, cashToken]); } diff --git a/lib/src/bitcoin/script/script.dart b/lib/src/bitcoin/script/script.dart index 75aadaf..9b088fc 100644 --- a/lib/src/bitcoin/script/script.dart +++ b/lib/src/bitcoin/script/script.dart @@ -1,37 +1,59 @@ 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:bitcoin_base/src/models/network.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -/// A Script contains just a list of OP_CODES and also knows how to serialize into bytes -/// -/// [script] the list with all the script OP_CODES and data class Script { static final Script empty = Script(); - Script._({required List script}) : script = script.immutable; - - final List script; + Script._({required List script}) + : script = script.immutable, + _scriptBytes = _toBytes(script).asImmutableBytes; + factory Script.fromJson(Map json) { + return Script(script: json["script"]); + } + factory Script({List script = const []}) { + for (final i in script) { + if (i is! String && i is! int && i is! BitcoinOpcode) { + throw DartBitcoinPluginException( + "A valid script is a composition of opcodes, hexadecimal strings, and integers arranged in a structured list."); + } + } + List scripts = []; + for (final token in script) { + if (token is BitcoinOpcode) { + if (token.isOpPushData) continue; + scripts.add(token.name); + continue; + } + final opcode = BitcoinOpcode.findByName(token.toString()); + if (opcode != null) { + scripts.add(opcode.name); + } else if (token is int && token >= 0 && token <= 16) { + scripts.add('OP_$token'); + } else { + if (token is int) { + final opcode = BitcoinOpcode.findByValue(token); + if (opcode?.isOpPushData ?? false) continue; + scripts.add(token); + } else { + final tokenBytes = BytesUtils.tryFromHexString(token); + if (tokenBytes == null) { + throw DartBitcoinPluginException( + "A valid script is a composition of opcodes, hexadecimal strings, and integers arranged in a structured list."); + } + scripts.add(StringUtils.strip0x((token as String).toLowerCase())); + } + } + } - factory Script.deserialize({ - List? bytes, - String? hexData, - bool hasSegwit = false, - }) { - return Script.fromRaw(bytes: bytes, hexData: hexData, hasSegwit: hasSegwit); + return Script._(script: scripts); } + final List script; + final List _scriptBytes; - factory Script.fromRaw({ - List? bytes, - String? hexData, - bool hasSegwit = false, - }) { + factory Script.deserialize({required List bytes}) { final List commands = []; int index = 0; - bytes ??= BytesUtils.fromHexString(hexData!); - if (bytes == null) { - throw DartBitcoinPluginException("Invalid script"); - } while (index < bytes.length) { final byte = bytes[index]; final opcode = BitcoinOpcode.findByValue(byte); @@ -47,7 +69,8 @@ class Script { final bytesToRead = bytes[index]; // skip len index = index + 1; - commands.add(BytesUtils.toHexString(bytes.sublist(index, index + bytesToRead))); + commands.add(BytesUtils.toHexString( + bytes.sublist(index, index + bytesToRead))); /// add length index = index + bytesToRead; @@ -55,28 +78,56 @@ class Script { /// get len final bytesToRead = readUint16LE(bytes, index); index = index + 2; - commands.add(BytesUtils.toHexString(bytes.sublist(index, index + bytesToRead))); + commands.add(BytesUtils.toHexString( + bytes.sublist(index, index + bytesToRead))); index = index + bytesToRead; } else if (opcode == BitcoinOpcode.opPushData4) { final bytesToRead = readUint32LE(bytes, index); index = index + 4; - commands.add(BytesUtils.toHexString(bytes.sublist(index, index + bytesToRead))); + commands.add(BytesUtils.toHexString( + bytes.sublist(index, index + bytesToRead))); index = index + bytesToRead; } } else { final viAndSize = IntUtils.decodeVarint(bytes.sublist(index)); final dataSize = viAndSize.item1; final size = viAndSize.item2; - final lastIndex = - (index + size + dataSize) > bytes.length ? bytes.length : (index + size + dataSize); - commands.add(BytesUtils.toHexString(bytes.sublist(index + size, lastIndex))); + final lastIndex = (index + size + dataSize) > bytes.length + ? bytes.length + : (index + size + dataSize); + commands.add( + BytesUtils.toHexString(bytes.sublist(index + size, lastIndex))); index = index + dataSize + size; } } return Script(script: commands); } + static List _toBytes(List script) { + if (script.isEmpty) return []; + final scriptBytes = DynamicByteTracker(); + for (final token in script) { + final opcode = BitcoinOpcode.findByName(token.toString()); + if (opcode != null) { + scriptBytes.add([opcode.value]); + } else { + if (token is int) { + scriptBytes.add(BitcoinScriptUtils.pushInteger(token)); + } else { + final tokenBytes = BytesUtils.tryFromHexString(token); + if (tokenBytes == null) { + throw DartBitcoinPluginException( + "A valid script is a composition of opcodes, hexadecimal strings, and integers arranged in a structured list."); + } + scriptBytes.add(BitcoinScriptUtils.opPushData(tokenBytes)); + } + } + } + + return scriptBytes.toBytes(); + } + dynamic findScriptParam(int index) { if (index < script.length) { return script[index]; @@ -101,7 +152,7 @@ class Script { } if (first == "OP_0") { - final lockingScriptBytes = BitcoinScriptUtils.opPushData(null, sec); + final lockingScriptBytes = opPushData(sec); if (lockingScriptBytes.length == 21) { return SegwitAddressType.p2wpkh; @@ -109,7 +160,7 @@ class Script { return SegwitAddressType.p2wsh; } } else if (first == "OP_1") { - final lockingScriptBytes = BitcoinScriptUtils.opPushData(null, sec); + final lockingScriptBytes = opPushData(sec); if (lockingScriptBytes.length == 33) { return SegwitAddressType.p2tr; @@ -121,14 +172,12 @@ class Script { final fifth = findScriptParam(4); if (first == "OP_DUP") { if (sec == "OP_HASH160" && - BitcoinScriptUtils.opPushData(null, third).length == 21 && + opPushData(third).length == 21 && fourth == "OP_EQUALVERIFY" && fifth == "OP_CHECKSIG") { return P2pkhAddressType.p2pkh; } - } else if (first == "OP_HASH160" && - BitcoinScriptUtils.opPushData(null, sec).length == 21 && - third == "OP_EQUAL") { + } else if (first == "OP_HASH160" && opPushData(sec).length == 21 && third == "OP_EQUAL") { return P2shAddressType.p2pkhInP2sh; } else if (sec == "OP_CHECKSIG") { if (first.length == 66) { @@ -139,94 +188,8 @@ class Script { return null; } - String toAddress() { - final addressType = getAddressType(); - if (addressType == null) { - throw DartBitcoinPluginException("Invalid script"); - } - - switch (addressType) { - case P2pkhAddressType.p2pkh: - return P2pkhAddress.fromScriptPubkey(script: this).toAddress(BitcoinNetwork.mainnet); - case P2shAddressType.p2pkhInP2sh: - return P2shAddress.fromScriptPubkey(script: this).toAddress(BitcoinNetwork.mainnet); - case SegwitAddressType.p2wpkh: - return P2wpkhAddress.fromScriptPubkey(script: this).toAddress(BitcoinNetwork.mainnet); - case SegwitAddressType.p2wsh: - return P2wshAddress.fromScriptPubkey(script: this).toAddress(BitcoinNetwork.mainnet); - case SegwitAddressType.p2tr: - return P2trAddress.fromScriptPubkey(script: this).toAddress(BitcoinNetwork.mainnet); - } - - throw DartBitcoinPluginException("Invalid script"); - } - - /// returns a serialized byte version of the script List toBytes() { - if (script.isEmpty) return []; - if (script.every((x) => x is int)) return script.cast(); - final bytes = DynamicByteTracker(); - for (final token in script) { - final opcode = BitcoinOpcode.findByName(token.toString()); - if (opcode != null) { - bytes.add([opcode.value]); - } else { - if (token is int) { - bytes.add(BitcoinScriptUtils.pushInteger(token)); - } else { - final tokenBytes = BytesUtils.tryFromHexString(token); - if (tokenBytes == null) { - throw DartBitcoinPluginException( - "A valid script is a composition of opcodes, hexadecimal strings, and integers arranged in a structured list."); - } - bytes.add(BitcoinScriptUtils.opPushData(tokenBytes)); - } - } - } - - return bytes.toBytes(); - } - - factory Script.fromJson(Map json) { - return Script(script: json["script"]); - } - - factory Script({List script = const []}) { - for (final i in script) { - if (i is! String && i is! int && i is! BitcoinOpcode) { - throw DartBitcoinPluginException( - "A valid script is a composition of opcodes, hexadecimal strings, and integers arranged in a structured list."); - } - } - List scripts = []; - for (final token in script) { - if (token is BitcoinOpcode) { - if (token.isOpPushData) continue; - scripts.add(token.name); - continue; - } - final opcode = BitcoinOpcode.findByName(token.toString()); - if (opcode != null) { - scripts.add(opcode.name); - } else if (token is int && token >= 0 && token <= 16) { - scripts.add('OP_$token'); - } else { - if (token is int) { - final opcode = BitcoinOpcode.findByValue(token); - if (opcode?.isOpPushData ?? false) continue; - scripts.add(token); - } else { - final tokenBytes = BytesUtils.tryFromHexString(token); - if (tokenBytes == null) { - throw DartBitcoinPluginException( - "A valid script is a composition of opcodes, hexadecimal strings, and integers arranged in a structured list."); - } - scripts.add(StringUtils.strip0x((token as String).toLowerCase())); - } - } - } - - return Script._(script: scripts); + return _scriptBytes.clone(); } String toHex() { @@ -246,11 +209,11 @@ class Script { operator ==(other) { if (identical(this, other)) return true; if (other is Script) { - return BytesUtils.bytesEqual(toBytes(), other.toBytes()); + return BytesUtils.bytesEqual(_scriptBytes, other._scriptBytes); } return false; } @override - int get hashCode => HashCodeGenerator.generateBytesHashCode(toBytes()); + int get hashCode => HashCodeGenerator.generateBytesHashCode(_scriptBytes); } diff --git a/lib/src/bitcoin/script/scripts.dart b/lib/src/bitcoin/script/scripts.dart index 3777a0d..710c963 100644 --- a/lib/src/bitcoin/script/scripts.dart +++ b/lib/src/bitcoin/script/scripts.dart @@ -1,9 +1,9 @@ -export 'utils.dart'; export 'input.dart'; export 'output.dart'; export 'script.dart'; export 'sequence.dart'; export 'transaction.dart'; export 'witness.dart'; +export 'utils.dart'; export 'op_code/constant.dart'; export 'outpoint.dart'; diff --git a/lib/src/bitcoin/script/transaction.dart b/lib/src/bitcoin/script/transaction.dart index 5fa0dfa..31abbf9 100644 --- a/lib/src/bitcoin/script/transaction.dart +++ b/lib/src/bitcoin/script/transaction.dart @@ -18,36 +18,33 @@ import 'witness.dart'; /// [outputs] A list of all the transaction outputs /// [locktime] The transaction's locktime parameter /// [version] The transaction version -/// [hasSegwit] Specifies a tx that includes segwit inputs /// [witnesses] The witness structure that corresponds to the inputs class BtcTransaction { BtcTransaction._( - {required List inputs, - required List outputs, + {List inputs = const [], + List outputs = const [], List witnesses = const [], this.hasSegwit = false, this.canReplaceByFee = false, this.mwebBytes, + this.hasSilentPayment = false, required List locktime, - required List version, - this.hasSilentPayment = false}) + required List version}) : locktime = locktime.asImmutableBytes, version = version.asImmutableBytes, inputs = inputs.immutable, outputs = outputs.immutable, witnesses = witnesses.immutable; - - factory BtcTransaction({ - List inputs = const [], - List outputs = const [], - List witnesses = const [], - List locktime = BitcoinOpCodeConst.defaultTxLocktime, - List version = BitcoinOpCodeConst.defaultTxVersion, - bool hasSegwit = false, - bool canReplaceByFee = false, - List? mwebBytes, - bool hasSilentPayment = false, - }) { + factory BtcTransaction( + {List inputs = const [], + List outputs = const [], + List witnesses = const [], + List locktime = BitcoinOpCodeConst.defaultTxLocktime, + 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}."); @@ -61,11 +58,11 @@ class BtcTransaction { outputs: outputs, witnesses: witnesses, version: version, - locktime: locktime, hasSegwit: hasSegwit, canReplaceByFee: canReplaceByFee, + hasSilentPayment: hasSilentPayment, mwebBytes: mwebBytes, - hasSilentPayment: hasSilentPayment); + locktime: locktime); } final List inputs; final List outputs; @@ -84,43 +81,39 @@ class BtcTransaction { bool? hasSegwit, List? locktime, List? version, - List? mwebBytes, - bool? hasSilentPayment, }) { 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(), hasSegwit: hasSegwit ?? this.hasSegwit, - mwebBytes: mwebBytes ?? this.mwebBytes, + mwebBytes: mwebBytes, locktime: locktime ?? this.locktime, version: version ?? this.version, - hasSilentPayment: hasSilentPayment ?? this.hasSilentPayment); + hasSilentPayment: hasSilentPayment, + ); } /// creates a copy of the object (classmethod) static BtcTransaction clone(BtcTransaction tx) { - return copy(tx); - } - - static BtcTransaction copy(BtcTransaction tx) { return BtcTransaction( + inputs: tx.inputs.map((e) => e.clone()).toList(), + outputs: tx.outputs.map((e) => e.clone()).toList(), + witnesses: tx.witnesses.map((e) => e.clone()).toList(), + locktime: tx.locktime, hasSegwit: tx.hasSegwit, - inputs: tx.inputs.map((e) => e.copy()).toList(), - outputs: tx.outputs.map((e) => e.copy()).toList(), - witnesses: tx.witnesses.map((e) => e.copy()).toList(), mwebBytes: tx.mwebBytes, - locktime: tx.locktime, version: tx.version); } - /// Instantiates a Transaction from serialized raw hexadacimal data (classmethod) - static BtcTransaction fromRaw(String raw, {bool allowWitness = true}) { - final rawtx = BytesUtils.fromHexString(raw); - return deserialize(rawtx, allowWitness: allowWitness); + static BtcTransaction fromRaw(String raw) { + final txBytes = BytesUtils.fromHexString(raw); + return deserialize(txBytes); } - static BtcTransaction deserialize(List txBytes, {bool allowWitness = true}) { + /// Instantiates a Transaction from serialized raw hexadacimal data (classmethod) + static BtcTransaction deserialize(List txBytes, + {bool allowWitness = true}) { try { final version = txBytes.sublist(0, 4); int cursor = 4; @@ -129,7 +122,7 @@ class BtcTransaction { bool hasMweb = false; if (txBytes[4] == 0) { flag = List.from(txBytes.sublist(5, 6)); - if (flag[0] & 1 > 0) { + if (allowWitness && (flag[0] & 1 > 0)) { hasWitness = true; } if (flag[0] & 8 > 0) { @@ -141,17 +134,15 @@ class BtcTransaction { cursor += vi.item2; bool canReplaceByFee = false; - final inputs = []; + final List inputs = []; for (int index = 0; index < vi.item1; index++) { - final inp = TxInput.deserialize(bytes: txBytes, hasSegwit: hasWitness, cursor: cursor); - - final input = inp.item1; - inputs.add(input); + final inp = TxInput.deserialize(bytes: txBytes, cursor: cursor); + inputs.add(inp.item1); cursor = inp.item2; if (canReplaceByFee == false) { canReplaceByFee = - const ListEquality().equals(input.sequence, BitcoinOpCodeConst.replaceByFeeSequence); + const ListEquality().equals(inp.item1.sequence, BitcoinOpCodeConst.replaceByFeeSequence); } } final outputs = []; @@ -162,7 +153,7 @@ class BtcTransaction { outputs.add(inp.item1); cursor = inp.item2; } - final witnesses = []; + final List witnesses = []; if (hasWitness) { if (cursor + 4 < txBytes.length) { for (int n = 0; n < inputs.length; n++) { @@ -173,7 +164,8 @@ class BtcTransaction { List witness = []; final wtVi = IntUtils.decodeVarint(txBytes.sublist(cursor)); if (wtVi.item1 != 0) { - witness = txBytes.sublist(cursor + wtVi.item2, cursor + wtVi.item1 + wtVi.item2); + witness = txBytes.sublist( + cursor + wtVi.item2, cursor + wtVi.item1 + wtVi.item2); } cursor += wtVi.item1 + wtVi.item2; witnessesTmp.add(BytesUtils.toHexString(witness)); @@ -187,23 +179,25 @@ class BtcTransaction { if (hasMweb) { mwebBytes = txBytes.sublist(cursor, txBytes.length - 4); } + // TODO: should this be added + // cursor = rawtx.length - 4; List locktime = BitcoinOpCodeConst.defaultTxLocktime; if ((txBytes.length - cursor) >= 4) { locktime = txBytes.sublist(cursor, cursor + 4); cursor += 4; } - assert(txBytes.length == cursor, "Transaction deserialization failed. Unexpected bytes."); + assert(txBytes.length == cursor, + "Transaction deserialization failed. Unexpected bytes."); return BtcTransaction( - inputs: inputs, - outputs: outputs, - witnesses: witnesses, - hasSegwit: hasWitness, - canReplaceByFee: canReplaceByFee, - mwebBytes: mwebBytes, - version: version, - locktime: locktime, - ); + inputs: inputs, + outputs: outputs, + witnesses: witnesses, + hasSegwit: hasWitness, + canReplaceByFee: canReplaceByFee, + mwebBytes: mwebBytes, + version: version, + locktime: locktime); } catch (e) { throw DartBitcoinPluginException("Transaction deserialization failed.", details: {"error": e.toString()}); @@ -255,7 +249,7 @@ class BtcTransaction { txForSign = [ ...txForSign, - ...IntUtils.toBytes(sighash, length: 4, byteOrder: Endian.little), + ...IntUtils.toBytes(sighash, length: 4, byteOrder: Endian.little) ]; return QuickCrypto.sha256DoubleHash(txForSign); } @@ -264,13 +258,11 @@ class BtcTransaction { List toBytes({bool allowWitness = true}) { final data = DynamicByteTracker(); data.add(version); - if (allowWitness && witnesses.isNotEmpty) { - var flag = 0; - if (allowWitness) flag |= 1; - if (mwebBytes != null && mwebBytes!.isNotEmpty) flag |= 8; - if (flag > 0) { - data.add([0x00, flag]); - } + var flag = 0; + if (allowWitness && witnesses.isNotEmpty) flag |= 1; + if (mwebBytes != null) flag |= 8; + if (flag > 0) { + data.add([0x00, flag]); } final txInCountBytes = IntUtils.encodeVarint(inputs.length); final txOutCountBytes = IntUtils.encodeVarint(outputs.length); @@ -289,7 +281,7 @@ class BtcTransaction { data.add(wit.toBytes()); } } - if (mwebBytes != null && mwebBytes!.isNotEmpty) { + if (mwebBytes != null) { data.add(mwebBytes!); } data.add(locktime); @@ -302,25 +294,26 @@ class BtcTransaction { /// [script] The scriptCode (template) that corresponds to the segwit, transaction output type that we want to spend. /// [amount] The amount of the UTXO to spend is included in the signature for segwit (in satoshis). /// [sighash] The type of the signature hash to be created. - List getTransactionSegwitDigit({ - required int txInIndex, - required Script script, - int sighash = BitcoinOpCodeConst.sighashAll, - required BigInt amount, - CashToken? token, - }) { - final tx = copy(this); + List getTransactionSegwitDigit( + {required int txInIndex, + required Script script, + int sighash = BitcoinOpCodeConst.sighashAll, + required BigInt amount, + CashToken? token}) { + final tx = clone(this); List hashPrevouts = List.filled(32, 0); List hashSequence = List.filled(32, 0); List hashOutputs = List.filled(32, 0); final basicSigHashType = sighash & 0x1F; - final anyoneCanPay = (sighash & 0xF0) == BitcoinOpCodeConst.sighashAnyoneCanPay; + final anyoneCanPay = + (sighash & 0xF0) == BitcoinOpCodeConst.sighashAnyoneCanPay; final signAll = (basicSigHashType != BitcoinOpCodeConst.sighashSingle) && (basicSigHashType != BitcoinOpCodeConst.sighashNone); if (!anyoneCanPay) { hashPrevouts = []; for (final txin in tx.inputs) { - final txidBytes = List.from(BytesUtils.fromHexString(txin.txId).reversed.toList()); + final txidBytes = List.from( + BytesUtils.fromHexString(txin.txId).reversed.toList()); hashPrevouts = [ ...hashPrevouts, ...txidBytes, @@ -344,9 +337,11 @@ class BtcTransaction { } hashOutputs = QuickCrypto.sha256DoubleHash(hashOutputs); } - if (basicSigHashType == BitcoinOpCodeConst.sighashSingle && txInIndex < tx.outputs.length) { + if (basicSigHashType == BitcoinOpCodeConst.sighashSingle && + txInIndex < tx.outputs.length) { final out = tx.outputs[txInIndex]; - final packedAmount = BigintUtils.toBytes(out.amount, length: 8, order: Endian.little); + final packedAmount = + BigintUtils.toBytes(out.amount, length: 8, order: Endian.little); final scriptBytes = IntUtils.prependVarint(out.scriptPubKey.toBytes()); hashOutputs = [...packedAmount, ...scriptBytes]; hashOutputs = QuickCrypto.sha256DoubleHash(hashOutputs); @@ -359,21 +354,24 @@ class BtcTransaction { final txIn = inputs[txInIndex]; final txidBytes = BytesUtils.fromHexString(txIn.txId).reversed.toList(); - txForSigning.add( - [...txidBytes, ...IntUtils.toBytes(txIn.txIndex, length: 4, byteOrder: Endian.little)]); + txForSigning.add([ + ...txidBytes, + ...IntUtils.toBytes(txIn.txIndex, length: 4, byteOrder: Endian.little) + ]); if (token != null) { txForSigning.add(token.toBytes()); } final varintBytes = IntUtils.prependVarint(script.toBytes()); txForSigning.add(varintBytes); - final packedAmount = BigintUtils.toBytes(amount, length: 8, order: Endian.little); + final packedAmount = + BigintUtils.toBytes(amount, length: 8, order: Endian.little); txForSigning.add(packedAmount); txForSigning.add(txIn.sequence); txForSigning.add(hashOutputs); txForSigning.add(locktime); - txForSigning.add(IntUtils.toBytes(sighash, length: 4, byteOrder: Endian.little)); - + txForSigning + .add(IntUtils.toBytes(sighash, length: 4, byteOrder: Endian.little)); return QuickCrypto.sha256DoubleHash(txForSigning.toBytes()); } @@ -383,18 +381,18 @@ class BtcTransaction { /// [scriptPubKeys] he scriptPubkeys that correspond to all the inputs/UTXOs /// [amounts] The amounts that correspond to all the inputs/UTXOs /// [sighash] The type of the signature hash to be created - List getTransactionTaprootDigset({ - required int txIndex, - required List