From 0b0fabffda09166757f72d13fb628634227af0a3 Mon Sep 17 00:00:00 2001 From: Matthew Mitchell Date: Mon, 19 Dec 2022 14:16:14 +0000 Subject: [PATCH 1/5] Simplify P2WPKH and allow generation of P2SH-P2PKH address --- lib/src/address.dart | 4 +- lib/src/payments/p2sh.dart | 4 +- lib/src/payments/p2wpkh.dart | 96 ++----------- lib/src/transaction.dart | 15 +- lib/src/transaction_builder.dart | 27 ++-- test/fixtures/p2wpkh.json | 173 ------------------------ test/integration/addresses_test.dart | 19 ++- test/integration/transactions_test.dart | 15 +- test/payments/p2wpkh_test.dart | 127 ++++++----------- 9 files changed, 106 insertions(+), 374 deletions(-) delete mode 100644 test/fixtures/p2wpkh.json diff --git a/lib/src/address.dart b/lib/src/address.dart index 4f96070..bf0369d 100644 --- a/lib/src/address.dart +++ b/lib/src/address.dart @@ -64,9 +64,7 @@ class Address { final progLen = program.length; if (progLen == 20) { - P2WPKH p2wpkh = - P2WPKH(data: PaymentData(address: address), network: network); - return p2wpkh.data.output!; + return P2WPKH.fromPublicKeyHash(program).outputScript; } if (progLen == 32) { diff --git a/lib/src/payments/p2sh.dart b/lib/src/payments/p2sh.dart index ef4d4df..479b02d 100644 --- a/lib/src/payments/p2sh.dart +++ b/lib/src/payments/p2sh.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; +import 'package:coinslib/coinslib.dart'; import '../utils/script.dart' as bscript; import '../utils/constants/op.dart'; import "../crypto.dart" show hash160; import './multisig.dart'; -import '../models/networks.dart'; import 'package:bs58check/bs58check.dart' as bs58check; class P2SH { @@ -18,6 +18,8 @@ class P2SH { P2SH.fromScriptBytes(Uint8List bytes) : this.fromScriptHash(hash160(bytes)); P2SH.fromMultisig(MultisigScript script) : this.fromScriptBytes(script.scriptBytes); + P2SH.fromP2WPKH(P2WPKH p2wpkh) + : this.fromScriptBytes(p2wpkh.outputScript); /// Returns the outputScript (scriptPubKey) Uint8List get outputScript => diff --git a/lib/src/payments/p2wpkh.dart b/lib/src/payments/p2wpkh.dart index 419263a..d655ddd 100644 --- a/lib/src/payments/p2wpkh.dart +++ b/lib/src/payments/p2wpkh.dart @@ -1,97 +1,29 @@ import 'dart:typed_data'; -import '../utils/ecurve.dart' show isPoint; import 'package:coinslib/bech32/bech32.dart'; - import '../crypto.dart'; import '../models/networks.dart'; -import '../payments/index.dart' show PaymentData; import '../utils/script.dart' as bscript; import '../utils/constants/op.dart'; class P2WPKH { - final emptyScript = Uint8List.fromList([]); - - PaymentData data; - NetworkType network; - - P2WPKH({required this.data, NetworkType? network}) - : network = network ?? bitcoin { - if (data.address == null && - data.hash == null && - data.output == null && - data.pubkey == null && - data.witness.isEmpty) throw ArgumentError('Not enough data'); - - if (data.address != null) { - _getDataFromAddress(data.address!); - } - - if (data.hash != null) { - _getDataFromHash(); - } - - final output = data.output; - if (output != null) { - if (output.length != 22 || output[0] != ops['OP_0'] || output[1] != 20) { - throw ArgumentError('Output is invalid'); - } - data.hash ??= output.sublist(2); - _getDataFromHash(); - } + Uint8List pubKeyHash; - if (data.pubkey != null) { - data.hash = hash160(data.pubkey!); - _getDataFromHash(); - } - - final witness = data.witness; - if (witness.isNotEmpty) { - if (witness.length != 2) throw ArgumentError('Witness is invalid'); - if (!bscript.isCanonicalScriptSignature(witness[0])) { - throw ArgumentError('Witness has invalid signature'); - } - if (!isPoint(witness[1])) { - throw ArgumentError('Witness has invalid pubkey'); - } - _getDataFromWitness(witness); - } else if (data.pubkey != null && data.signature != null) { - data.witness = [data.signature!, data.pubkey!]; - data.input ??= emptyScript; + P2WPKH.fromPublicKeyHash(Uint8List hash) : pubKeyHash = hash { + if (hash.length != 20) { + throw ArgumentError('Invalid P2WPKH public key hash length'); } } - void _getDataFromWitness(List witness) { - data.input ??= emptyScript; - if (data.pubkey == null) { - data.pubkey = witness[1]; - data.hash ??= hash160(data.pubkey!); - _getDataFromHash(); - } - data.signature ??= witness[0]; - } + P2WPKH.fromPublicKey(Uint8List publicKey) + : this.fromPublicKeyHash(hash160(publicKey)); - void _getDataFromHash() { - data.address ??= segwit.encode(Segwit(network.bech32!, 0, data.hash!)); - data.output ??= bscript.compile([ops['OP_0'], data.hash]); - } + Uint8List get outputScript => bscript.compile( + [ops["OP_0"], pubKeyHash], + ); + + /// Returns the bech32 address for a given network + String address(NetworkType network) => + segwit.encode(Segwit(network.bech32!, 0, pubKeyHash)); - void _getDataFromAddress(String address) { - try { - Segwit segwitAddress = segwit.decode(address); - if (network.bech32 != segwitAddress.hrp) { - throw ArgumentError('Invalid prefix or Network mismatch'); - } - // Only support version 0 now; - if (segwitAddress.version != 0) { - throw ArgumentError('Invalid address version'); - } - data.hash = Uint8List.fromList(segwitAddress.program); - } on InvalidHrp { - throw ArgumentError('Invalid prefix or Network mismatch'); - } on InvalidProgramLength { - throw ArgumentError('Invalid address data'); - } on InvalidWitnessVersion { - throw ArgumentError('Invalid witness address version'); - } - } } + diff --git a/lib/src/transaction.dart b/lib/src/transaction.dart index ef827f7..0bdee57 100644 --- a/lib/src/transaction.dart +++ b/lib/src/transaction.dart @@ -494,13 +494,18 @@ class Input { } if (type == scriptTypes['P2WPKH']) { - P2WPKH p2wpkh = P2WPKH(data: PaymentData(witness: witness)); + + final signature = witness[0]; + final pubkey = witness[1]; + final outputScript = P2WPKH.fromPublicKey(pubkey).outputScript; + return Input( - prevOutScript: p2wpkh.data.output, + prevOutScript: outputScript, prevOutType: type, - pubkeys: [p2wpkh.data.pubkey!], - signatures: [InputSignature.decode(p2wpkh.data.signature!)], + pubkeys: [pubkey], + signatures: [InputSignature.decode(signature)], ); + } else if (type == scriptTypes['P2WSH']) { // Having witness data handled in a class would be nicer, but I'm // sticking reasonably close to the library interface as-is @@ -611,7 +616,7 @@ class Output { if (ourPubKey == null) return Output(); var type = classifyOutput(script); if (type == scriptTypes['P2WPKH']) { - Uint8List wpkh1 = P2WPKH(data: PaymentData(output: script)).data.hash!; + Uint8List wpkh1 = bscript.decompile(script)![1]; Uint8List wpkh2 = bcrypto.hash160(ourPubKey); if (wpkh1 != wpkh2) throw ArgumentError('Hash mismatch!'); return Output(pubkeys: [ourPubKey], signatures: [null]); diff --git a/lib/src/transaction_builder.dart b/lib/src/transaction_builder.dart index 0652dc6..3e17e5f 100644 --- a/lib/src/transaction_builder.dart +++ b/lib/src/transaction_builder.dart @@ -344,21 +344,24 @@ class TransactionBuilder { } else if (input.isComplete()) { // Build the following types of input only when complete - final paymentData = PaymentData( - pubkey: input.pubkeys![0], - signature: input.signatures.first.encode(), - ); + final pubkey = input.pubkeys![0]; + final signature = input.signatures.first.encode(); + + if (input.prevOutType == scriptTypes['P2WPKH']) { + tx.setInputScript(i, Uint8List(0)); + tx.setWitness(i, [signature, pubkey]); + } else if (input.prevOutType == scriptTypes['P2PKH']) { + + final paymentData = PaymentData( + pubkey: pubkey, + signature: signature, + ); - if (input.prevOutType == scriptTypes['P2PKH']) { P2PKH(data: paymentData, network: network); - } else if (input.prevOutType == scriptTypes['P2WPKH']) { - P2WPKH(data: paymentData, network: network); - } else { - continue; - } + tx.setInputScript(i, paymentData.input!); + tx.setWitness(i, []); - tx.setInputScript(i, paymentData.input!); - tx.setWitness(i, paymentData.witness); + } } } diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json deleted file mode 100644 index b424176..0000000 --- a/test/fixtures/p2wpkh.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "valid": [ - { - "description": "output from address", - "arguments": { - "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d" - }, - "options": {}, - "expected": { - "name": "p2wpkh", - "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", - "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", - "signature": null, - "input": null, - "witness": "" - } - }, - { - "description": "output from hash", - "arguments": { - "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727" - }, - "expected": { - "name": "p2wpkh", - "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", - "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", - "signature": null, - "input": null, - "witness": "" - } - }, - { - "description": "output from output", - "arguments": { - "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727" - }, - "expected": { - "name": "p2wpkh", - "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", - "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", - "signature": null, - "input": null, - "witness": "" - } - }, - { - "description": "output from pubkey", - "arguments": { - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" - }, - "expected": { - "name": "p2wpkh", - "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", - "signature": null, - "input": null, - "witness": "" - } - }, - { - "description": "witness/output from pubkey/signature", - "arguments": { - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", - "signature": "300602010002010001" - }, - "expected": { - "name": "p2wpkh", - "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", - "input": "", - "witness": ["300602010002010001", "030000000000000000000000000000000000000000000000000000000000000001"] - } - }, - { - "description": "witness/output from witness", - "arguments": { - "witness": ["300602010002010001", "030000000000000000000000000000000000000000000000000000000000000001"] - }, - "expected": { - "name": "p2wpkh", - "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", - "signature": "300602010002010001", - "input": "" - } - } - ], - "invalid": [ - { - "exception": "Not enough data", - "arguments": {} - }, - { - "exception": "Not enough data", - "arguments": { - "signature": "300602010002010001" - } - }, - { - "exception": "Output is invalid", - "description": "Unexpected OP", - "arguments": { - "output": "OP_RESERVED ea6d525c0c955d90d3dbd29a81ef8bfb79003727" - } - }, - { - "exception": "Invalid prefix or Network mismatch", - "arguments": { - "address": "foo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs30dvv" - } - }, - { - "exception": "Invalid address version", - "arguments": { - "address": "bc1pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5us4ke" - } - }, - { - "exception": "Invalid address data", - "arguments": { - "address": "bc1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmql8k8" - } - }, - { - "exception": "Witness is invalid", - "arguments": { - "witness": ["300602010002010001"] - } - }, - { - "exception": "Witness has invalid signature", - "arguments": { - "witness": ["ffffffffffffffffff", "030000000000000000000000000000000000000000000000000000000000000001"] - } - }, - { - "exception": "Witness has invalid pubkey", - "arguments": { - "witness": ["300602010002010001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"] - } - } - ], - "dynamic": { - "depends": { - "address": [ "address", "output", "hash", "pubkey", "witness" ], - "hash": [ "address", "output", "hash", "pubkey", "witness" ], - "output": [ "address", "output", "hash", "pubkey", "witness" ], - "pubkey": [ "witness" ], - "signature": [ "witness" ], - "input": [ "witness" ], - "witness": [ [ "pubkey", "signature" ] ] - }, - "details": [ - { - "description": "p2wpkh", - "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", - "signature": "300602010002010001", - "input": "", - "witness": [ - "300602010002010001", - "030000000000000000000000000000000000000000000000000000000000000001" - ] - } - ] - } -} diff --git a/test/integration/addresses_test.dart b/test/integration/addresses_test.dart index 4e45426..788c76f 100644 --- a/test/integration/addresses_test.dart +++ b/test/integration/addresses_test.dart @@ -79,18 +79,15 @@ main() { final keyPair = ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn'); final address = - P2WPKH(data: PaymentData(pubkey: keyPair.publicKey)).data.address; + P2WPKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'); }); test('can generate a SegWit testnet address', () { - final testnet = networks.testnet; final keyPair = ECPair.fromWIF('cPaJYBMDLjQp5gSUHnBfhX4Rgj95ekBS6oBttwQLw3qfsKKcDfuB'); final address = - P2WPKH(data: PaymentData(pubkey: keyPair.publicKey), network: testnet) - .data - .address; + P2WPKH.fromPublicKey(keyPair.publicKey!).address(networks.testnet); expect(address, 'tb1qgmp0h7lvexdxx9y05pmdukx09xcteu9sx2h4ya'); }); @@ -118,4 +115,16 @@ main() { "32QQmWZAbqBr837PE5dir6EgXcxFByojx1", ); }); + + test('can generate a P2SH-P2WPKH', () { + + final p2wpkh = P2WPKH.fromPublicKey(aliceKey.publicKey!); + final p2sh = P2SH.fromP2WPKH(p2wpkh); + + expect( + p2sh.address(networks.bitcoin), + "", + ); + + }); } diff --git a/test/integration/transactions_test.dart b/test/integration/transactions_test.dart index 8e0b9f1..5c3560b 100644 --- a/test/integration/transactions_test.dart +++ b/test/integration/transactions_test.dart @@ -80,18 +80,14 @@ main() { 'cUNfunNKXNNJDvUvsjxz5tznMR6ob1g5K6oa4WGbegoQD3eqf4am', network: networks.testnet, ); - final p2wpkh = P2WPKH( - data: PaymentData(pubkey: alice.publicKey), - network: networks.testnet, - ).data; - + final p2wpkh = P2WPKH.fromPublicKey(alice.publicKey!); final txb = TransactionBuilder(network: networks.testnet); txb.setVersion(1); txb.addInput( '53676626f5042d42e15313492ab7e708b87559dc0a8c74b7140057af51a2ed5b', 0, null, - p2wpkh.output, + p2wpkh.outputScript, ); // Alice's previous transaction output, has 200000 satoshis txb.addOutput( @@ -332,16 +328,13 @@ main() { // P2WPKH input (carol) 0.02 - final p2wpkh = P2WPKH( - data: PaymentData(pubkey: carolKey.publicKey), - network: networks.peercoinTestnet, - ).data; + final p2wpkh = P2WPKH.fromPublicKey(carolKey.publicKey!); txb.addInput( "20afc8511da97fc745f8efc5c2719b92002a608ce7a456b0187b75a2e21625e0", 1, null, - p2wpkh.output, + p2wpkh.outputScript, ); // P2WSH input (alice and bob) 0.031234 diff --git a/test/payments/p2wpkh_test.dart b/test/payments/p2wpkh_test.dart index 4449300..4100c3e 100644 --- a/test/payments/p2wpkh_test.dart +++ b/test/payments/p2wpkh_test.dart @@ -6,95 +6,58 @@ import 'dart:io'; import 'dart:convert'; import 'package:hex/hex.dart'; import 'dart:typed_data'; +import 'package:coinslib/src/models/networks.dart'; main() { - final fixtures = json.decode( - File("./test/fixtures/p2wpkh.json").readAsStringSync(encoding: utf8), - ); - group('(valid case)', () { - for (var f in (fixtures["valid"] as List)) { - test(f['description'] + ' as expected', () { - final arguments = _preformPaymentData(f['arguments']); - final p2wpkh = P2WPKH(data: arguments); - if (arguments.address == null) { - expect(p2wpkh.data.address, f['expected']['address']); - } - if (arguments.hash == null) { - expect(_toString(p2wpkh.data.hash), f['expected']['hash']); - } - if (arguments.pubkey == null) { - expect(_toString(p2wpkh.data.pubkey), f['expected']['pubkey']); - } - if (arguments.input == null) { - expect(_toString(p2wpkh.data.input), f['expected']['input']); - } - if (arguments.output == null) { - expect(_toString(p2wpkh.data.output), f['expected']['output']); - } - if (arguments.signature == null) { - expect(_toString(p2wpkh.data.signature), f['expected']['signature']); - } - if (arguments.witness.isEmpty) { - expect(_toString(p2wpkh.data.witness), f['expected']['witness']); - } - }); - } + test("P2WPKH.fromPublicKeyHash", () { + final p2wpkh = P2WPKH.fromPublicKeyHash( + HEX.decode("ea6d525c0c955d90d3dbd29a81ef8bfb79003727") as Uint8List, + ); + expect( + p2wpkh.address(bitcoin), + "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", + ); + expect( + p2wpkh.outputScript, + HEX.decode("0014ea6d525c0c955d90d3dbd29a81ef8bfb79003727") as Uint8List, + ); }); - group('(invalid case)', () { - for (final f in (fixtures["invalid"] as List)) { - test( - 'throws ${f['exception']}${f["description"] != null ? ('for ${f['description']}') : ''}', - () { - final arguments = _preformPaymentData(f['arguments']); - try { - expect(P2WPKH(data: arguments), isArgumentError); - } catch (err) { - expect((err as ArgumentError).message, f['exception']); - } - }); - } + test("P2WPKH.fromPublicKey", () { + final p2wpkh = P2WPKH.fromPublicKey( + HEX.decode( + "030000000000000000000000000000000000000000000000000000000000000001", + ) as Uint8List, + ); + expect( + p2wpkh.address(bitcoin), + "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", + ); + expect( + p2wpkh.pubKeyHash, + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28d1") as Uint8List, + ); + expect( + p2wpkh.outputScript, + HEX.decode("0014168b992bcfc44050310b3a94bd0771136d0b28d1") as Uint8List, + ); }); -} -PaymentData _preformPaymentData(dynamic x) { - final address = x['address']; - final hash = x['hash'] != null ? HEX.decode(x['hash']) : null; - final input = x['input'] != null ? bscript.fromASM(x['input']) : null; - final witness = x['witness'] != null - ? (x['witness'] as List) - .map((e) => HEX.decode(e.toString()) as Uint8List) - .toList() - : null; - final output = x['output'] != null - ? bscript.fromASM(x['output']) - : x['outputHex'] != null - ? HEX.decode(x['outputHex']) - : null; - final pubkey = x['pubkey'] != null ? HEX.decode(x['pubkey']) : null; - final signature = x['signature'] != null ? HEX.decode(x['signature']) : null; + test("invalid P2WPKH length", () { - return PaymentData( - address: address, - hash: hash as Uint8List?, - input: input, - output: output as Uint8List?, - pubkey: pubkey as Uint8List?, - signature: signature as Uint8List?, - witness: witness, - ); -} + expect( + () => P2WPKH.fromPublicKeyHash( + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28") as Uint8List, + ), + throwsA( + predicate( + (e) => e is ArgumentError && + e.message == 'Invalid P2WPKH public key hash length', + ), + ), + ); + + }); -String? _toString(dynamic x) { - if (x == null) { - return null; - } - if (x is Uint8List) { - return HEX.encode(x); - } - if (x is List) { - return bscript.toASM(x); - } - return ''; } From 2adc58b2e1ffe21abc372ff5c7a551d863328fc0 Mon Sep 17 00:00:00 2001 From: Matthew Mitchell Date: Mon, 19 Dec 2022 14:37:20 +0000 Subject: [PATCH 2/5] Update tests --- test/address_test.dart | 13 +++++++++++++ test/integration/addresses_test.dart | 2 +- test/payments/p2wpkh_test.dart | 4 ---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/test/address_test.dart b/test/address_test.dart index b2bd219..8a62b02 100644 --- a/test/address_test.dart +++ b/test/address_test.dart @@ -166,6 +166,19 @@ main() { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ); }); + + test('returns p2wpkh scripts', () { + expectP2WPKH(address, expectedHash) => + expectScript(address, "0014$expectedHash"); + expectP2WPKH( + "bc1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9e75rs", + "0000000000000000000000000000000000000000", + ); + expectP2WPKH( + "bc1qllllllllllllllllllllllllllllllllfglmy6", + "ffffffffffffffffffffffffffffffffffffffff", + ); + }); }); }); } diff --git a/test/integration/addresses_test.dart b/test/integration/addresses_test.dart index 788c76f..52cd5e7 100644 --- a/test/integration/addresses_test.dart +++ b/test/integration/addresses_test.dart @@ -123,7 +123,7 @@ main() { expect( p2sh.address(networks.bitcoin), - "", + "3GUySFjkixqDxfmNghuXU9h4TgTixp5E2p", ); }); diff --git a/test/payments/p2wpkh_test.dart b/test/payments/p2wpkh_test.dart index 4100c3e..f11fe12 100644 --- a/test/payments/p2wpkh_test.dart +++ b/test/payments/p2wpkh_test.dart @@ -1,9 +1,5 @@ -import 'package:coinslib/src/payments/index.dart' show PaymentData; import 'package:coinslib/src/payments/p2wpkh.dart'; import 'package:test/test.dart'; -import 'package:coinslib/src/utils/script.dart' as bscript; -import 'dart:io'; -import 'dart:convert'; import 'package:hex/hex.dart'; import 'dart:typed_data'; import 'package:coinslib/src/models/networks.dart'; From aab70b9e749755576fb0956b40dedfda39fe71e2 Mon Sep 17 00:00:00 2001 From: Matthew Mitchell Date: Mon, 19 Dec 2022 19:27:27 +0000 Subject: [PATCH 3/5] Simplify P2PKH --- lib/src/address.dart | 4 +- lib/src/coinslib_base.dart | 84 ++++++-------- lib/src/payments/p2pkh.dart | 125 ++++----------------- lib/src/transaction.dart | 35 +++--- lib/src/transaction_builder.dart | 24 +--- test/address_test.dart | 14 +++ test/fixtures/p2pkh.json | 157 --------------------------- test/integration/addresses_test.dart | 14 +-- test/integration/bip32_test.dart | 4 +- test/payments/p2pkh_test.dart | 127 +++++++++------------- test/payments/p2wpkh_test.dart | 2 +- test/transaction_builder_test.dart | 3 +- 12 files changed, 155 insertions(+), 438 deletions(-) delete mode 100644 test/fixtures/p2pkh.json diff --git a/lib/src/address.dart b/lib/src/address.dart index bf0369d..e6c77fd 100644 --- a/lib/src/address.dart +++ b/lib/src/address.dart @@ -35,9 +35,7 @@ class Address { final data = decodeBase58.sublist(1); if (prefix == network.pubKeyHash) { - P2PKH p2pkh = - P2PKH(data: PaymentData(address: address), network: network); - return p2pkh.data.output!; + return P2PKH.fromPublicKeyHash(data).outputScript; } if (prefix == network.scriptHash) { diff --git a/lib/src/coinslib_base.dart b/lib/src/coinslib_base.dart index a57c0a2..3e1a175 100644 --- a/lib/src/coinslib_base.dart +++ b/lib/src/coinslib_base.dart @@ -1,70 +1,65 @@ -// TODO: Put public facing types in this file. import 'dart:typed_data'; import 'package:coinslib/src/utils/magic_hash.dart'; import 'package:hex/hex.dart'; import 'bip32_base.dart' as bip32; import 'models/networks.dart'; -import 'payments/index.dart' show PaymentData; import 'payments/p2pkh.dart'; import 'ecpair.dart'; /// Checks if you are awesome. Spoiler: you are. class HDWallet { - bip32.BIP32? _bip32; - P2PKH? _p2pkh; + bip32.BIP32 _bip32; + P2PKH _p2pkh; String? seed; NetworkType network; String? get privKey { - if (_bip32 == null) return null; try { - return HEX.encode(_bip32!.privateKey!); + return HEX.encode(_bip32.privateKey!); } catch (_) { return null; } } - Uint8List? get pubKeyBytes => _bip32?.publicKey; - String? get pubKey => _bip32 != null ? HEX.encode(_bip32!.publicKey) : null; + Uint8List get pubKeyBytes => _bip32.publicKey; + String get pubKey => HEX.encode(_bip32.publicKey); String? get base58Priv { - if (_bip32 == null) return null; try { - return _bip32!.toBase58(); + return _bip32.toBase58(); } catch (_) { return null; } } - String? get base58 => _bip32 != null ? _bip32!.neutered().toBase58() : null; + String get base58 => _bip32.neutered().toBase58(); String? get wif { - if (_bip32 == null) return null; try { - return _bip32!.toWIF(); + return _bip32.toWIF(); } catch (_) { return null; } } - String? get address => _p2pkh != null ? _p2pkh!.data.address : null; + String get address => _p2pkh.address(network); - HDWallet({required bip32, required p2pkh, required this.network, this.seed}) { - _bip32 = bip32; - _p2pkh = p2pkh; - } + HDWallet({ + required bip32, + required p2pkh, + required this.network, + this.seed, + }) : _bip32 = bip32, _p2pkh = p2pkh; HDWallet derivePath(String path) { - final bip32 = _bip32!.derivePath(path); - final p2pkh = - P2PKH(data: PaymentData(pubkey: bip32.publicKey), network: network); + final bip32 = _bip32.derivePath(path); + final p2pkh = P2PKH.fromPublicKey(bip32.publicKey); return HDWallet(bip32: bip32, p2pkh: p2pkh, network: network); } HDWallet derive(int index) { - final bip32 = _bip32!.derive(index); - final p2pkh = - P2PKH(data: PaymentData(pubkey: bip32.publicKey), network: network); + final bip32 = _bip32.derive(index); + final p2pkh = P2PKH.fromPublicKey(bip32.publicKey); return HDWallet(bip32: bip32, p2pkh: p2pkh, network: network); } @@ -72,8 +67,7 @@ class HDWallet { network = network ?? bitcoin; final seedHex = HEX.encode(seed); final wallet = bip32.BIP32.fromSeed(seed, network); - final p2pkh = - P2PKH(data: PaymentData(pubkey: wallet.publicKey), network: network); + final p2pkh = P2PKH.fromPublicKey(wallet.publicKey); return HDWallet( bip32: wallet, p2pkh: p2pkh, @@ -85,62 +79,54 @@ class HDWallet { factory HDWallet.fromBase58(String xpub, {NetworkType? network}) { network = network ?? bitcoin; final wallet = bip32.BIP32.fromBase58(xpub, network); - final p2pkh = - P2PKH(data: PaymentData(pubkey: wallet.publicKey), network: network); + final p2pkh = P2PKH.fromPublicKey(wallet.publicKey); return HDWallet(bip32: wallet, p2pkh: p2pkh, network: network, seed: null); } Uint8List sign(String message) { Uint8List messageHash = magicHash(message, network); - return _bip32!.signRecoverable(messageHash); + return _bip32.signRecoverable(messageHash); } bool verify({required String message, required Uint8List signature}) { Uint8List messageHash = magicHash(message); - return _bip32!.verify(messageHash, signature); + return _bip32.verify(messageHash, signature); } } class Wallet { - ECPair? _keyPair; - P2PKH? _p2pkh; - - String? get privKey => - _keyPair != null ? HEX.encode(_keyPair!.privateKey!) : null; - - String? get pubKey => - _keyPair != null ? HEX.encode(_keyPair!.publicKey!) : null; - - String? get wif => _keyPair != null ? _keyPair!.toWIF() : null; - - String? get address => _p2pkh != null ? _p2pkh!.data.address : null; + ECPair _keyPair; + P2PKH _p2pkh; + NetworkType network; - NetworkType? network; + String get privKey => HEX.encode(_keyPair.privateKey!); + String get pubKey => HEX.encode(_keyPair.publicKey!); + String get wif => _keyPair.toWIF(); + String get address => _p2pkh.address(network); Wallet(this._keyPair, this._p2pkh, this.network); factory Wallet.random([NetworkType? network]) { + network ??= bitcoin; final keyPair = ECPair.makeRandom(network: network); - final p2pkh = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey), network: network); + final p2pkh = P2PKH.fromPublicKey(keyPair.publicKey!); return Wallet(keyPair, p2pkh, network); } factory Wallet.fromWIF(String wif, [NetworkType? network]) { network = network ?? bitcoin; final keyPair = ECPair.fromWIF(wif, network: network); - final p2pkh = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey), network: network); + final p2pkh = P2PKH.fromPublicKey(keyPair.publicKey!); return Wallet(keyPair, p2pkh, network); } Uint8List sign(String message) { Uint8List messageHash = magicHash(message, network); - return _keyPair!.signRecoverable(messageHash); + return _keyPair.signRecoverable(messageHash); } bool verify({required String message, required Uint8List signature}) { Uint8List messageHash = magicHash(message, network); - return _keyPair!.verify(messageHash, signature); + return _keyPair.verify(messageHash, signature); } } diff --git a/lib/src/payments/p2pkh.dart b/lib/src/payments/p2pkh.dart index 7a345f0..88974d3 100644 --- a/lib/src/payments/p2pkh.dart +++ b/lib/src/payments/p2pkh.dart @@ -1,118 +1,39 @@ import 'dart:typed_data'; -import '../utils/ecurve.dart' show isPoint; import 'package:bs58check/bs58check.dart' as bs58check; - import '../crypto.dart'; import '../models/networks.dart'; -import '../payments/index.dart' show PaymentData; import '../utils/script.dart' as bscript; import '../utils/constants/op.dart'; class P2PKH { - PaymentData data; - NetworkType network; - - P2PKH({required this.data, NetworkType? network}) - : network = network ?? bitcoin { - _init(); - } - - _init() { - if (data.address != null) { - _getDataFromAddress(data.address!); - _getDataFromHash(); - } else if (data.hash != null) { - _getDataFromHash(); - } else if (data.output != null) { - if (!isValidOutput(data.output!)) { - throw ArgumentError('Output is invalid'); - } - data.hash = data.output!.sublist(3, 23); - _getDataFromHash(); - } else if (data.pubkey != null) { - data.hash = hash160(data.pubkey!); - _getDataFromHash(); - _getDataFromChunk(); - } else if (data.input != null) { - List chunks = bscript.decompile(data.input)!; - _getDataFromChunk(chunks); - if (chunks.length != 2) throw ArgumentError('Input is invalid'); - if (!bscript.isCanonicalScriptSignature(chunks[0])) { - throw ArgumentError('Input has invalid signature'); - } - if (!isPoint(chunks[1])) { - throw ArgumentError('Input has invalid pubkey'); - } - } else { - throw ArgumentError('Not enough data'); - } - } - - void _getDataFromChunk([List? chunks]) { - if (data.pubkey == null && chunks != null) { - data.pubkey = - (chunks[1] is int) ? Uint8List.fromList([chunks[1]]) : chunks[1]; - data.hash = hash160(data.pubkey!); - _getDataFromHash(); - } - if (data.signature == null && chunks != null) { - data.signature = - (chunks[0] is int) ? Uint8List.fromList([chunks[0]]) : chunks[0]; - } - if (data.input == null && data.pubkey != null && data.signature != null) { - data.input = bscript.compile([data.signature!, data.pubkey!]); - } - } + Uint8List pubKeyHash; - void _getDataFromHash() { - if (data.address == null) { - final payload = Uint8List(21); - payload.buffer.asByteData().setUint8(0, network.pubKeyHash); - payload.setRange(1, payload.length, data.hash!); - data.address = bs58check.encode(payload); + P2PKH.fromPublicKeyHash(Uint8List hash) : pubKeyHash = hash { + if (hash.length != 20) { + throw ArgumentError('Invalid P2PKH public key hash length'); } - data.output ??= bscript.compile([ - ops['OP_DUP'], - ops['OP_HASH160'], - data.hash, - ops['OP_EQUALVERIFY'], - ops['OP_CHECKSIG'] - ]); } - void _getDataFromAddress(String address) { - Uint8List payload = bs58check.decode(address); - final version = payload.buffer.asByteData().getUint8(0); - if (version != network.pubKeyHash) { - throw ArgumentError('Invalid version or Network mismatch'); - } - data.hash = payload.sublist(1); - if (data.hash!.length != 20) throw ArgumentError('Invalid address'); + P2PKH.fromPublicKey(Uint8List publicKey) + : this.fromPublicKeyHash(hash160(publicKey)); + + Uint8List get outputScript => bscript.compile( + [ + ops["OP_DUP"], + ops["OP_HASH160"], + pubKeyHash, + ops["OP_EQUALVERIFY"], + ops["OP_CHECKSIG"] + ], + ); + + /// Returns the base58 address for a given network + String address(NetworkType network) { + final payload = Uint8List(21); + payload.buffer.asByteData().setUint8(0, network.pubKeyHash); + payload.setRange(1, payload.length, pubKeyHash); + return bs58check.encode(payload); } -} -isValidOutput(Uint8List data) { - return data.length == 25 && - data[0] == ops['OP_DUP'] && - data[1] == ops['OP_HASH160'] && - data[2] == 0x14 && - data[23] == ops['OP_EQUALVERIFY'] && - data[24] == ops['OP_CHECKSIG']; } -// Backward compatibility -@Deprecated( - "The 'P2PKHData' class is deprecated. Use the 'PaymentData' package instead.", -) -class P2PKHData extends PaymentData { - P2PKHData({address, hash, output, pubkey, input, signature, witness}) - : super( - address: address, - hash: hash, - output: output, - pubkey: pubkey, - input: input, - signature: signature, - witness: witness, - ); -} diff --git a/lib/src/transaction.dart b/lib/src/transaction.dart index 0bdee57..940e4ce 100644 --- a/lib/src/transaction.dart +++ b/lib/src/transaction.dart @@ -533,12 +533,16 @@ class Input { threshold: multisig.threshold, ); } else if (type == scriptTypes['P2PKH']) { - P2PKH p2pkh = P2PKH(data: PaymentData(input: scriptSig)); + final scriptChunks = bscript.decompile(scriptSig)!; + final signature = scriptChunks[0]; + final pubkey = scriptChunks[1]; + final p2pkh = P2PKH.fromPublicKey(pubkey); + return Input( - prevOutScript: p2pkh.data.output, + prevOutScript: p2pkh.outputScript, prevOutType: type, - pubkeys: [p2pkh.data.pubkey!], - signatures: [InputSignature.decode(p2pkh.data.signature!)], + pubkeys: [pubkey], + signatures: [InputSignature.decode(signature)], ); } else if (type == scriptTypes['P2PK']) { P2PK p2pk = P2PK(data: PaymentData(input: scriptSig)); @@ -614,19 +618,22 @@ class Output { factory Output.expandOutput(Uint8List script, [Uint8List? ourPubKey]) { if (ourPubKey == null) return Output(); - var type = classifyOutput(script); + final type = classifyOutput(script); + final chunks = bscript.decompile(script)!; + + Uint8List hash; + if (type == scriptTypes['P2WPKH']) { - Uint8List wpkh1 = bscript.decompile(script)![1]; - Uint8List wpkh2 = bcrypto.hash160(ourPubKey); - if (wpkh1 != wpkh2) throw ArgumentError('Hash mismatch!'); - return Output(pubkeys: [ourPubKey], signatures: [null]); + hash = chunks[1]; } else if (type == scriptTypes['P2PKH']) { - Uint8List pkh1 = P2PKH(data: PaymentData(output: script)).data.hash!; - Uint8List pkh2 = bcrypto.hash160(ourPubKey); - if (pkh1 != pkh2) throw ArgumentError('Hash mismatch!'); - return Output(pubkeys: [ourPubKey], signatures: [null]); + hash = chunks[2]; + } else { + throw UnsupportedError('type "$type"'); } - throw UnsupportedError('type "$type"'); + + if (hash != bcrypto.hash160(ourPubKey)) throw ArgumentError('Hash mismatch!'); + return Output(pubkeys: [ourPubKey], signatures: [null]); + } factory Output.clone(Output output) { diff --git a/lib/src/transaction_builder.dart b/lib/src/transaction_builder.dart index 3e17e5f..d796f3f 100644 --- a/lib/src/transaction_builder.dart +++ b/lib/src/transaction_builder.dart @@ -11,7 +11,6 @@ import 'transaction.dart'; import 'address.dart'; import 'payments/index.dart' show PaymentData; import 'payments/p2pkh.dart'; -import 'payments/p2wpkh.dart'; import 'classify.dart'; import 'input_signature.dart'; @@ -213,15 +212,11 @@ class TransactionBuilder { input.prevOutType = scriptTypes['P2WPKH']; input.witness = []; input.pubkeys = [ourPubKey]; - input.signScript = P2PKH( - data: PaymentData(pubkey: ourPubKey), - network: network, - ).data.output; + input.signScript = P2PKH.fromPublicKey(ourPubKey).outputScript; } else { - Uint8List prevOutScript = pubkeyToOutputScript(ourPubKey); input.prevOutType = scriptTypes['P2PKH']; input.pubkeys = [ourPubKey]; - input.signScript = prevOutScript; + input.signScript = P2PKH.fromPublicKey(ourPubKey).outputScript; } // Check outPubKey is in input.pubkeys or we cannot sign @@ -351,16 +346,8 @@ class TransactionBuilder { tx.setInputScript(i, Uint8List(0)); tx.setWitness(i, [signature, pubkey]); } else if (input.prevOutType == scriptTypes['P2PKH']) { - - final paymentData = PaymentData( - pubkey: pubkey, - signature: signature, - ); - - P2PKH(data: paymentData, network: network); - tx.setInputScript(i, paymentData.input!); + tx.setInputScript(i, bscript.compile([signature, pubkey])); tx.setWitness(i, []); - } } } @@ -471,8 +458,3 @@ class TransactionBuilder { Map get prevTxSet => _prevTxSet; } -Uint8List pubkeyToOutputScript(Uint8List pubkey, [NetworkType? nw]) { - NetworkType network = nw ?? bitcoin; - P2PKH p2pkh = P2PKH(data: PaymentData(pubkey: pubkey), network: network); - return p2pkh.data.output!; -} diff --git a/test/address_test.dart b/test/address_test.dart index 8a62b02..7ac45db 100644 --- a/test/address_test.dart +++ b/test/address_test.dart @@ -138,6 +138,20 @@ main() { expect(HEX.encode(actual), expectedScript); } + test('returns p2pkh scripts', () { + expectP2PKH(address, expectedHash) => + expectScript(address, "76a914${expectedHash}88ac"); + + expectP2PKH( + "1111111111111111111114oLvT2", + "0000000000000000000000000000000000000000", + ); + expectP2PKH( + "1QLbz7JHiBTspS962RLKV8GndWFwi5j6Qr", + "ffffffffffffffffffffffffffffffffffffffff", + ); + }); + test('returns p2sh scripts', () { expectP2SH(address, expectedHash) => expectScript(address, "a914${expectedHash}87"); diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json deleted file mode 100644 index 66f6c14..0000000 --- a/test/fixtures/p2pkh.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "valid": [ - { - "description": "output from address", - "arguments": { - "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh" - }, - "expected": { - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", - "signature": null, - "input": null - } - }, - { - "description": "output from hash", - "arguments": { - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1" - }, - "expected": { - "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", - "signature": null, - "input": null - } - }, - { - "description": "output from output", - "arguments": { - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG" - }, - "expected": { - "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "signature": null, - "input": null - } - }, - { - "description": "output from pubkey", - "arguments": { - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" - }, - "expected": { - "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", - "signature": null, - "input": null - } - }, - { - "description": "input/output from pubkey/signature", - "arguments": { - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", - "signature": "300602010002010001" - }, - "expected": { - "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", - "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" - } - }, - { - "description": "input/output from input", - "arguments": { - "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" - }, - "expected": { - "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", - "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", - "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", - "signature": "300602010002010001" - } - } - ], - "invalid": [ - { - "exception": "Not enough data", - "arguments": {} - }, - { - "exception": "Not enough data", - "arguments": { - "signature": "300602010002010001" - } - }, - { - "description": "Unexpected OP_RESERVED", - "exception": "Output is invalid", - "arguments": { - "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_RESERVED" - } - }, - { - "description": "Unexpected OP_DUP", - "exception": "Output is invalid", - "options": {}, - "arguments": { - "output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d137 OP_EQUALVERIFY" - } - }, - { - "description": "Hash too short (too many chunks)", - "exception": "Output is invalid", - "arguments": { - "output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_TRUE OP_EQUALVERIFY" - } - }, - { - "description": "Non-minimally encoded (non BIP62 compliant)", - "exception": "Output is invalid", - "arguments": { - "outputHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - } - }, - { - "exception": "Invalid version or Network mismatch", - "arguments": { - "address": "3LRW7jeCvQCRdPF8S3yUCfRAx4eqXFmdcr" - } - }, - { - "exception": "Invalid address", - "arguments": { - "address": "111111111111111111117K4nzc" - } - }, - { - "exception": "Invalid address", - "arguments": { - "address": "111111111111111111111111133izVn" - } - }, - { - "exception": "Input has invalid signature", - "arguments": { - "input": "ffffffffffffffffff 030000000000000000000000000000000000000000000000000000000000000001" - } - }, - { - "exception": "Input has invalid pubkey", - "arguments": { - "input": "300602010002010001 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - } - }, - { - "description": "Input has unexpected data", - "exception": "Input is invalid", - "arguments": { - "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001 ffff" - } - } - ] -} \ No newline at end of file diff --git a/test/integration/addresses_test.dart b/test/integration/addresses_test.dart index 52cd5e7..87ee9c7 100644 --- a/test/integration/addresses_test.dart +++ b/test/integration/addresses_test.dart @@ -31,7 +31,7 @@ main() { test('can generate a random address', () { final keyPair = ECPair.makeRandom(rng: rng); final address = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey)).data.address; + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64'); }); @@ -40,7 +40,7 @@ main() { .process(utf8.encode('correct horse battery staple') as Uint8List); final keyPair = ECPair.fromPrivateKey(hash); final address = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey)).data.address; + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8'); }); @@ -48,7 +48,7 @@ main() { final keyPair = ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct'); final address = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey)).data.address; + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31'); }); @@ -57,9 +57,7 @@ main() { final keyPair = ECPair.makeRandom(network: testnet, rng: rng); final wif = keyPair.toWIF(); final address = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey), network: testnet) - .data - .address; + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.testnet); expect(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L'); expect(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr'); }); @@ -68,9 +66,7 @@ main() { final keyPair = ECPair.makeRandom(network: litecoin, rng: rng); final wif = keyPair.toWIF(); final address = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey), network: litecoin) - .data - .address; + P2PKH.fromPublicKey(keyPair.publicKey!).address(litecoin); expect(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn'); expect(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS'); }); diff --git a/test/integration/bip32_test.dart b/test/integration/bip32_test.dart index 49beced..0586eaa 100644 --- a/test/integration/bip32_test.dart +++ b/test/integration/bip32_test.dart @@ -107,7 +107,5 @@ void main() { } String getAddress(node, [network]) { - return P2PKH(data: PaymentData(pubkey: node.publicKey), network: network) - .data - .address!; + return P2PKH.fromPublicKey(node.publicKey).address(network ?? bitcoin); } diff --git a/test/payments/p2pkh_test.dart b/test/payments/p2pkh_test.dart index 4efa613..f427859 100644 --- a/test/payments/p2pkh_test.dart +++ b/test/payments/p2pkh_test.dart @@ -1,89 +1,62 @@ -import 'package:coinslib/src/payments/index.dart' show PaymentData; import 'package:coinslib/src/payments/p2pkh.dart'; import 'package:test/test.dart'; -import 'package:coinslib/src/utils/script.dart' as bscript; -import 'dart:io'; -import 'dart:convert'; import 'package:hex/hex.dart'; import 'dart:typed_data'; +import 'package:coinslib/src/models/networks.dart'; main() { - final fixtures = json.decode( - File("./test/fixtures/p2pkh.json").readAsStringSync(encoding: utf8), - ); - group('(valid case)', () { - for (final f in (fixtures["valid"] as List)) { - test(f['description'] + ' as expected', () { - final arguments = _preformPaymentData(f['arguments']); - final p2pkh = P2PKH(data: arguments); - if (arguments.address == null) { - expect(p2pkh.data.address, f['expected']['address']); - } - if (arguments.hash == null) { - expect(_toString(p2pkh.data.hash), f['expected']['hash']); - } - if (arguments.pubkey == null) { - expect(_toString(p2pkh.data.pubkey), f['expected']['pubkey']); - } - if (arguments.input == null) { - expect(_toString(p2pkh.data.input), f['expected']['input']); - } - if (arguments.output == null) { - expect(_toString(p2pkh.data.output), f['expected']['output']); - } - if (arguments.signature == null) { - expect(_toString(p2pkh.data.signature), f['expected']['signature']); - } - }); - } + test("P2PKH.fromPublicKeyHash", () { + final p2pkh = P2PKH.fromPublicKeyHash( + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28d1") as Uint8List, + ); + expect( + p2pkh.address(bitcoin), + "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + ); + expect( + p2pkh.outputScript, + HEX.decode("76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac") as Uint8List, + ); }); - group('(invalid case)', () { - for (final f in (fixtures["invalid"] as List)) { - test( - 'throws ${f['exception']}${f['description'] != null ? ('for ${f['description']}') : ''}', - () { - final arguments = _preformPaymentData(f['arguments']); - try { - expect(P2PKH(data: arguments), isArgumentError); - } catch (err) { - expect((err as ArgumentError).message, f['exception']); - } - }); - } + + test("P2PKH.fromPublicKey", () { + + final p2pkh = P2PKH.fromPublicKey( + HEX.decode( + "030000000000000000000000000000000000000000000000000000000000000001", + ) as Uint8List, + ); + expect( + p2pkh.address(bitcoin), + "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + ); + expect( + p2pkh.pubKeyHash, + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28d1") as Uint8List, + ); + expect( + p2pkh.outputScript, + HEX.decode("76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac") as Uint8List, + ); + }); -} -PaymentData _preformPaymentData(dynamic x) { - final address = x['address']; - final hash = x['hash'] != null ? HEX.decode(x['hash']) : null; - final input = x['input'] != null ? bscript.fromASM(x['input']) : null; - final output = x['output'] != null - ? bscript.fromASM(x['output']) - : x['outputHex'] != null - ? HEX.decode(x['outputHex']) - : null; - final pubkey = x['pubkey'] != null ? HEX.decode(x['pubkey']) : null; - final signature = x['signature'] != null ? HEX.decode(x['signature']) : null; - return PaymentData( - address: address, - hash: hash as Uint8List?, - input: input, - output: output as Uint8List?, - pubkey: pubkey as Uint8List?, - signature: signature as Uint8List?, - ); -} + test("invalid P2PKH hash length", () { + + expect( + () => P2PKH.fromPublicKeyHash( + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28") as Uint8List, + ), + throwsA( + predicate( + (e) => e is ArgumentError && + e.message == 'Invalid P2PKH public key hash length', + ), + ), + ); + + }); -String? _toString(dynamic x) { - if (x == null) { - return null; - } - if (x is Uint8List) { - return HEX.encode(x); - } - if (x is List) { - return bscript.toASM(x); - } - return ''; } + diff --git a/test/payments/p2wpkh_test.dart b/test/payments/p2wpkh_test.dart index f11fe12..63e9654 100644 --- a/test/payments/p2wpkh_test.dart +++ b/test/payments/p2wpkh_test.dart @@ -40,7 +40,7 @@ main() { ); }); - test("invalid P2WPKH length", () { + test("invalid P2WPKH hash length", () { expect( () => P2WPKH.fromPublicKeyHash( diff --git a/test/transaction_builder_test.dart b/test/transaction_builder_test.dart index 4ae2a08..1e6c830 100644 --- a/test/transaction_builder_test.dart +++ b/test/transaction_builder_test.dart @@ -234,8 +234,7 @@ main() { }); test('accepts an address string and value', () { - final address = - P2PKH(data: PaymentData(pubkey: keyPair.publicKey)).data.address; + final address = P2PKH.fromPublicKey(keyPair.publicKey!).address(bitcoin); final vout = txb.addOutput(address, BigInt.from(1000)); expect(vout, 0); final txout = txb.tx.outs[0]; From 4b55ac855fe929163a48b04cd4ec47ee88dc27f5 Mon Sep 17 00:00:00 2001 From: Matthew Mitchell Date: Tue, 20 Dec 2022 16:14:55 +0000 Subject: [PATCH 4/5] Remove P2PK as it wasn't complete anyway --- lib/coinslib.dart | 4 +++- lib/src/address.dart | 1 - lib/src/payments/index.dart | 26 ---------------------- lib/src/payments/p2pk.dart | 29 ------------------------- lib/src/transaction.dart | 8 +++---- lib/src/transaction_builder.dart | 1 - test/integration/addresses_test.dart | 1 - test/integration/bip32_test.dart | 1 - test/integration/transactions_test.dart | 1 - test/transaction_builder_test.dart | 1 - 10 files changed, 6 insertions(+), 67 deletions(-) delete mode 100644 lib/src/payments/index.dart delete mode 100644 lib/src/payments/p2pk.dart diff --git a/lib/coinslib.dart b/lib/coinslib.dart index 2483b22..f696432 100644 --- a/lib/coinslib.dart +++ b/lib/coinslib.dart @@ -9,7 +9,9 @@ export 'src/transaction.dart'; export 'src/address.dart'; export 'src/transaction_builder.dart'; export 'src/ecpair.dart'; +export 'src/payments/multisig.dart'; export 'src/payments/p2pkh.dart'; export 'src/payments/p2wpkh.dart'; -export 'src/payments/index.dart'; +export 'src/payments/p2sh.dart'; +export 'src/payments/p2wsh.dart'; export 'src/bip32_base.dart'; diff --git a/lib/src/address.dart b/lib/src/address.dart index e6c77fd..8515170 100644 --- a/lib/src/address.dart +++ b/lib/src/address.dart @@ -6,7 +6,6 @@ import 'package:coinslib/src/payments/p2wsh.dart'; import 'models/networks.dart'; import 'package:bs58check/bs58check.dart' as bs58check; import 'package:coinslib/bech32/bech32.dart'; -import 'payments/index.dart' show PaymentData; import 'payments/p2pkh.dart'; import 'payments/p2wpkh.dart'; import 'payments/p2sh.dart'; diff --git a/lib/src/payments/index.dart b/lib/src/payments/index.dart deleted file mode 100644 index cca9583..0000000 --- a/lib/src/payments/index.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:typed_data'; - -class PaymentData { - String? address; - Uint8List? hash; - Uint8List? output; - Uint8List? signature; - Uint8List? pubkey; - Uint8List? input; - List witness; - - PaymentData({ - this.address, - this.hash, - this.output, - this.pubkey, - this.input, - this.signature, - List? witness, - }) : witness = witness ?? []; - - @override - String toString() { - return 'PaymentData{address: $address, hash: $hash, output: $output, signature: $signature, pubkey: $pubkey, input: $input, witness: $witness}'; - } -} diff --git a/lib/src/payments/p2pk.dart b/lib/src/payments/p2pk.dart deleted file mode 100644 index 64a3bd3..0000000 --- a/lib/src/payments/p2pk.dart +++ /dev/null @@ -1,29 +0,0 @@ -import '../utils/ecurve.dart' show isPoint; -import '../models/networks.dart'; -import '../payments/index.dart' show PaymentData; -import '../utils/constants/op.dart'; - -class P2PK { - PaymentData data; - NetworkType network; - - P2PK({required this.data, NetworkType? network}) - : network = network ?? bitcoin { - _init(); - } - - _init() { - final output = data.output; - if (output != null) { - if (output[output.length - 1] != ops['OP_CHECKSIG']) { - throw ArgumentError('Output is invalid'); - } - if (!isPoint(output.sublist(1, -1))) { - throw ArgumentError('Output pubkey is invalid'); - } - } - if (data.input != null) { - // TODO - } - } -} diff --git a/lib/src/transaction.dart b/lib/src/transaction.dart index 940e4ce..777ebb1 100644 --- a/lib/src/transaction.dart +++ b/lib/src/transaction.dart @@ -1,9 +1,7 @@ import 'dart:typed_data'; import 'package:coinslib/src/payments/multisig.dart'; import 'package:hex/hex.dart'; -import 'payments/index.dart' show PaymentData; import 'payments/p2pkh.dart' show P2PKH; -import 'payments/p2pk.dart' show P2PK; import 'payments/p2wpkh.dart' show P2WPKH; import 'crypto.dart' as bcrypto; import 'classify.dart'; @@ -425,7 +423,7 @@ class Transaction { } } -// TODO: In dire need of complete refactoring +// In dire need of complete refactoring class Input { Uint8List? hash; int? index; @@ -545,11 +543,11 @@ class Input { signatures: [InputSignature.decode(signature)], ); } else if (type == scriptTypes['P2PK']) { - P2PK p2pk = P2PK(data: PaymentData(input: scriptSig)); + final signature = bscript.decompile(scriptSig)![0]; return Input( prevOutType: type, pubkeys: [], - signatures: [InputSignature.decode(p2pk.data.signature!)], + signatures: [InputSignature.decode(signature)], ); } diff --git a/lib/src/transaction_builder.dart b/lib/src/transaction_builder.dart index d796f3f..08f2679 100644 --- a/lib/src/transaction_builder.dart +++ b/lib/src/transaction_builder.dart @@ -9,7 +9,6 @@ import 'ecpair.dart'; import 'models/networks.dart'; import 'transaction.dart'; import 'address.dart'; -import 'payments/index.dart' show PaymentData; import 'payments/p2pkh.dart'; import 'classify.dart'; import 'input_signature.dart'; diff --git a/test/integration/addresses_test.dart b/test/integration/addresses_test.dart index 87ee9c7..df8f596 100644 --- a/test/integration/addresses_test.dart +++ b/test/integration/addresses_test.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import 'package:coinslib/src/models/networks.dart' as networks; import 'package:coinslib/src/ecpair.dart' show ECPair; -import 'package:coinslib/src/payments/index.dart' show PaymentData; import 'package:coinslib/src/payments/p2pkh.dart' show P2PKH; import 'package:coinslib/src/payments/p2sh.dart'; import 'package:coinslib/src/payments/p2wpkh.dart' show P2WPKH; diff --git a/test/integration/bip32_test.dart b/test/integration/bip32_test.dart index 0586eaa..4b4dbfe 100644 --- a/test/integration/bip32_test.dart +++ b/test/integration/bip32_test.dart @@ -1,6 +1,5 @@ import 'dart:typed_data'; import 'package:coinslib/src/models/networks.dart'; -import 'package:coinslib/src/payments/index.dart' show PaymentData; import 'package:coinslib/src/payments/p2pkh.dart'; import 'package:test/test.dart'; import 'package:hex/hex.dart'; diff --git a/test/integration/transactions_test.dart b/test/integration/transactions_test.dart index 5c3560b..b40ff53 100644 --- a/test/integration/transactions_test.dart +++ b/test/integration/transactions_test.dart @@ -6,7 +6,6 @@ import 'package:coinslib/src/ecpair.dart'; import 'package:coinslib/src/transaction_builder.dart'; import 'package:coinslib/src/models/networks.dart' as networks; import 'package:coinslib/src/payments/p2wpkh.dart' show P2WPKH; -import 'package:coinslib/src/payments/index.dart' show PaymentData; import 'package:coinslib/src/payments/multisig.dart' show MultisigScript; import '../keys.dart'; diff --git a/test/transaction_builder_test.dart b/test/transaction_builder_test.dart index 1e6c830..f2cb4b1 100644 --- a/test/transaction_builder_test.dart +++ b/test/transaction_builder_test.dart @@ -10,7 +10,6 @@ import 'package:coinslib/src/transaction.dart'; import 'package:coinslib/src/address.dart'; import 'package:coinslib/src/transaction_builder.dart'; import 'package:coinslib/src/utils/script.dart' as bscript; -import 'package:coinslib/src/payments/index.dart' show PaymentData; import 'package:coinslib/src/payments/p2pkh.dart'; final networks = {'bitcoin': bitcoin, 'testnet': testnet}; From 5bb2f30f35452e3c528b71551d359157e3f658d7 Mon Sep 17 00:00:00 2001 From: Matthew Mitchell Date: Tue, 20 Dec 2022 20:40:18 +0000 Subject: [PATCH 5/5] Run linter and format --- lib/src/coinslib_base.dart | 3 ++- lib/src/payments/p2pkh.dart | 20 +++++++++----------- lib/src/payments/p2sh.dart | 4 +--- lib/src/payments/p2wpkh.dart | 8 +++----- lib/src/transaction.dart | 6 ++---- lib/src/transaction_builder.dart | 1 - test/integration/addresses_test.dart | 17 +++++++---------- test/payments/multisig_test.dart | 5 ++--- test/payments/p2pkh_test.dart | 20 ++++++++------------ test/payments/p2wpkh_test.dart | 11 ++++------- 10 files changed, 38 insertions(+), 57 deletions(-) diff --git a/lib/src/coinslib_base.dart b/lib/src/coinslib_base.dart index 3e1a175..1952597 100644 --- a/lib/src/coinslib_base.dart +++ b/lib/src/coinslib_base.dart @@ -49,7 +49,8 @@ class HDWallet { required p2pkh, required this.network, this.seed, - }) : _bip32 = bip32, _p2pkh = p2pkh; + }) : _bip32 = bip32, + _p2pkh = p2pkh; HDWallet derivePath(String path) { final bip32 = _bip32.derivePath(path); diff --git a/lib/src/payments/p2pkh.dart b/lib/src/payments/p2pkh.dart index 88974d3..9bd305a 100644 --- a/lib/src/payments/p2pkh.dart +++ b/lib/src/payments/p2pkh.dart @@ -15,17 +15,17 @@ class P2PKH { } P2PKH.fromPublicKey(Uint8List publicKey) - : this.fromPublicKeyHash(hash160(publicKey)); + : this.fromPublicKeyHash(hash160(publicKey)); Uint8List get outputScript => bscript.compile( - [ - ops["OP_DUP"], - ops["OP_HASH160"], - pubKeyHash, - ops["OP_EQUALVERIFY"], - ops["OP_CHECKSIG"] - ], - ); + [ + ops["OP_DUP"], + ops["OP_HASH160"], + pubKeyHash, + ops["OP_EQUALVERIFY"], + ops["OP_CHECKSIG"] + ], + ); /// Returns the base58 address for a given network String address(NetworkType network) { @@ -34,6 +34,4 @@ class P2PKH { payload.setRange(1, payload.length, pubKeyHash); return bs58check.encode(payload); } - } - diff --git a/lib/src/payments/p2sh.dart b/lib/src/payments/p2sh.dart index 479b02d..364f98f 100644 --- a/lib/src/payments/p2sh.dart +++ b/lib/src/payments/p2sh.dart @@ -3,7 +3,6 @@ import 'package:coinslib/coinslib.dart'; import '../utils/script.dart' as bscript; import '../utils/constants/op.dart'; import "../crypto.dart" show hash160; -import './multisig.dart'; import 'package:bs58check/bs58check.dart' as bs58check; class P2SH { @@ -18,8 +17,7 @@ class P2SH { P2SH.fromScriptBytes(Uint8List bytes) : this.fromScriptHash(hash160(bytes)); P2SH.fromMultisig(MultisigScript script) : this.fromScriptBytes(script.scriptBytes); - P2SH.fromP2WPKH(P2WPKH p2wpkh) - : this.fromScriptBytes(p2wpkh.outputScript); + P2SH.fromP2WPKH(P2WPKH p2wpkh) : this.fromScriptBytes(p2wpkh.outputScript); /// Returns the outputScript (scriptPubKey) Uint8List get outputScript => diff --git a/lib/src/payments/p2wpkh.dart b/lib/src/payments/p2wpkh.dart index d655ddd..b359fd0 100644 --- a/lib/src/payments/p2wpkh.dart +++ b/lib/src/payments/p2wpkh.dart @@ -15,15 +15,13 @@ class P2WPKH { } P2WPKH.fromPublicKey(Uint8List publicKey) - : this.fromPublicKeyHash(hash160(publicKey)); + : this.fromPublicKeyHash(hash160(publicKey)); Uint8List get outputScript => bscript.compile( - [ops["OP_0"], pubKeyHash], - ); + [ops["OP_0"], pubKeyHash], + ); /// Returns the bech32 address for a given network String address(NetworkType network) => segwit.encode(Segwit(network.bech32!, 0, pubKeyHash)); - } - diff --git a/lib/src/transaction.dart b/lib/src/transaction.dart index 777ebb1..56aea08 100644 --- a/lib/src/transaction.dart +++ b/lib/src/transaction.dart @@ -492,7 +492,6 @@ class Input { } if (type == scriptTypes['P2WPKH']) { - final signature = witness[0]; final pubkey = witness[1]; final outputScript = P2WPKH.fromPublicKey(pubkey).outputScript; @@ -503,7 +502,6 @@ class Input { pubkeys: [pubkey], signatures: [InputSignature.decode(signature)], ); - } else if (type == scriptTypes['P2WSH']) { // Having witness data handled in a class would be nicer, but I'm // sticking reasonably close to the library interface as-is @@ -629,9 +627,9 @@ class Output { throw UnsupportedError('type "$type"'); } - if (hash != bcrypto.hash160(ourPubKey)) throw ArgumentError('Hash mismatch!'); + if (hash != bcrypto.hash160(ourPubKey)) + throw ArgumentError('Hash mismatch!'); return Output(pubkeys: [ourPubKey], signatures: [null]); - } factory Output.clone(Output output) { diff --git a/lib/src/transaction_builder.dart b/lib/src/transaction_builder.dart index 08f2679..ea0e39d 100644 --- a/lib/src/transaction_builder.dart +++ b/lib/src/transaction_builder.dart @@ -456,4 +456,3 @@ class TransactionBuilder { Map get prevTxSet => _prevTxSet; } - diff --git a/test/integration/addresses_test.dart b/test/integration/addresses_test.dart index df8f596..b290544 100644 --- a/test/integration/addresses_test.dart +++ b/test/integration/addresses_test.dart @@ -30,7 +30,7 @@ main() { test('can generate a random address', () { final keyPair = ECPair.makeRandom(rng: rng); final address = - P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64'); }); @@ -39,7 +39,7 @@ main() { .process(utf8.encode('correct horse battery staple') as Uint8List); final keyPair = ECPair.fromPrivateKey(hash); final address = - P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8'); }); @@ -47,7 +47,7 @@ main() { final keyPair = ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct'); final address = - P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31'); }); @@ -56,7 +56,7 @@ main() { final keyPair = ECPair.makeRandom(network: testnet, rng: rng); final wif = keyPair.toWIF(); final address = - P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.testnet); + P2PKH.fromPublicKey(keyPair.publicKey!).address(networks.testnet); expect(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L'); expect(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr'); }); @@ -64,8 +64,7 @@ main() { test('can generate a Litecoin address', () { final keyPair = ECPair.makeRandom(network: litecoin, rng: rng); final wif = keyPair.toWIF(); - final address = - P2PKH.fromPublicKey(keyPair.publicKey!).address(litecoin); + final address = P2PKH.fromPublicKey(keyPair.publicKey!).address(litecoin); expect(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn'); expect(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS'); }); @@ -74,7 +73,7 @@ main() { final keyPair = ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn'); final address = - P2WPKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); + P2WPKH.fromPublicKey(keyPair.publicKey!).address(networks.bitcoin); expect(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'); }); @@ -82,7 +81,7 @@ main() { final keyPair = ECPair.fromWIF('cPaJYBMDLjQp5gSUHnBfhX4Rgj95ekBS6oBttwQLw3qfsKKcDfuB'); final address = - P2WPKH.fromPublicKey(keyPair.publicKey!).address(networks.testnet); + P2WPKH.fromPublicKey(keyPair.publicKey!).address(networks.testnet); expect(address, 'tb1qgmp0h7lvexdxx9y05pmdukx09xcteu9sx2h4ya'); }); @@ -112,7 +111,6 @@ main() { }); test('can generate a P2SH-P2WPKH', () { - final p2wpkh = P2WPKH.fromPublicKey(aliceKey.publicKey!); final p2sh = P2SH.fromP2WPKH(p2wpkh); @@ -120,6 +118,5 @@ main() { p2sh.address(networks.bitcoin), "3GUySFjkixqDxfmNghuXU9h4TgTixp5E2p", ); - }); } diff --git a/test/payments/multisig_test.dart b/test/payments/multisig_test.dart index 9a302d1..320ff83 100644 --- a/test/payments/multisig_test.dart +++ b/test/payments/multisig_test.dart @@ -2,7 +2,6 @@ import 'dart:typed_data'; import 'package:coinslib/coinslib.dart'; import 'package:test/test.dart'; -import 'package:coinslib/src/payments/multisig.dart'; import 'package:hex/hex.dart'; uint8ListFromHex(String hex) => Uint8List.fromList(HEX.decode(hex)); @@ -11,7 +10,7 @@ main() { final wallet = HDWallet.fromSeed(Uint8List(16)); const successThreshold = 17; // Generate 20 keys from HD Wallet - final pubkeys = List.generate(20, (i) => wallet.derive(i).pubKeyBytes!); + final pubkeys = List.generate(20, (i) => wallet.derive(i).pubKeyBytes); final pubkey = pubkeys[0]; final pksWithPush = @@ -100,7 +99,7 @@ main() { beforePKNum.toList() + // Add PK [0x21] + - wallet.derive(20).pubKeyBytes! + + wallet.derive(20).pubKeyBytes + // Add PK num and CHECKMULTISIG [0x01, 0x15, 0xae], ); diff --git a/test/payments/p2pkh_test.dart b/test/payments/p2pkh_test.dart index f427859..bf3630f 100644 --- a/test/payments/p2pkh_test.dart +++ b/test/payments/p2pkh_test.dart @@ -5,7 +5,6 @@ import 'dart:typed_data'; import 'package:coinslib/src/models/networks.dart'; main() { - test("P2PKH.fromPublicKeyHash", () { final p2pkh = P2PKH.fromPublicKeyHash( HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28d1") as Uint8List, @@ -16,12 +15,12 @@ main() { ); expect( p2pkh.outputScript, - HEX.decode("76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac") as Uint8List, + HEX.decode("76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac") + as Uint8List, ); }); test("P2PKH.fromPublicKey", () { - final p2pkh = P2PKH.fromPublicKey( HEX.decode( "030000000000000000000000000000000000000000000000000000000000000001", @@ -37,26 +36,23 @@ main() { ); expect( p2pkh.outputScript, - HEX.decode("76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac") as Uint8List, + HEX.decode("76a914168b992bcfc44050310b3a94bd0771136d0b28d188ac") + as Uint8List, ); - }); test("invalid P2PKH hash length", () { - expect( () => P2PKH.fromPublicKeyHash( - HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28") as Uint8List, + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28") as Uint8List, ), throwsA( predicate( - (e) => e is ArgumentError && - e.message == 'Invalid P2PKH public key hash length', + (e) => + e is ArgumentError && + e.message == 'Invalid P2PKH public key hash length', ), ), ); - }); - } - diff --git a/test/payments/p2wpkh_test.dart b/test/payments/p2wpkh_test.dart index 63e9654..56a8f6c 100644 --- a/test/payments/p2wpkh_test.dart +++ b/test/payments/p2wpkh_test.dart @@ -5,7 +5,6 @@ import 'dart:typed_data'; import 'package:coinslib/src/models/networks.dart'; main() { - test("P2WPKH.fromPublicKeyHash", () { final p2wpkh = P2WPKH.fromPublicKeyHash( HEX.decode("ea6d525c0c955d90d3dbd29a81ef8bfb79003727") as Uint8List, @@ -41,19 +40,17 @@ main() { }); test("invalid P2WPKH hash length", () { - expect( () => P2WPKH.fromPublicKeyHash( - HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28") as Uint8List, + HEX.decode("168b992bcfc44050310b3a94bd0771136d0b28") as Uint8List, ), throwsA( predicate( - (e) => e is ArgumentError && - e.message == 'Invalid P2WPKH public key hash length', + (e) => + e is ArgumentError && + e.message == 'Invalid P2WPKH public key hash length', ), ), ); - }); - }