Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge updates from dart-bitcoin/bitcoin_flutter #2

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pubspec.lock
# Conventional directory for build outputs
build/

# Conventional directory for test coverage outputs
coverage/

# Directory created by dartdoc
doc/api/

Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
## 2.1.0
- Release bitcoindart package

## 2.0.2
- Add support for optional 'noStrict' parameter in Transaction.fromBuffer

## 2.0.1
- Add payments/index.dart to lib exports

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import 'package:bip39/bip39.dart' as bip39;

main() {
var seed = bip39.mnemonicToSeed("praise you muffin lion enable neck grocery crumble super myself license ghost");
var hdWallet = new HDWallet(seed);
var hdWallet = new HDWallet.fromSeed(seed);
print(hdWallet.address);
// => 12eUJoaWBENQ3tNZE52ZQaHqr3v4tTX4os
print(hdWallet.pubKey);
Expand Down
11 changes: 6 additions & 5 deletions lib/bitcoindart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
/// More dartdocs go here.
library bitcoindart;

export 'src/bitcoindart_base.dart';
export 'src/models/networks.dart';
export 'src/transaction.dart';
export 'src/address.dart';
export 'src/transaction_builder.dart';
export 'src/bitcoindart_base.dart';
export 'src/ecpair.dart';
export 'src/models/networks.dart';
export 'src/payments/index.dart';
export 'src/payments/p2pkh.dart';
export 'src/payments/p2sh.dart';
export 'src/payments/p2wpkh.dart';
export 'src/payments/index.dart';
export 'src/transaction.dart';
export 'src/transaction_builder.dart';
// TODO: Export any libraries intended for clients of this package.
16 changes: 9 additions & 7 deletions lib/src/address.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import 'dart:typed_data';
import 'models/networks.dart';
import 'package:bs58check/bs58check.dart' as bs58check;

import 'package:bech32/bech32.dart';
import 'package:bs58check/bs58check.dart' as bs58check;

import 'models/networks.dart';
import 'payments/index.dart' show PaymentData;
import 'payments/p2pkh.dart';
import 'payments/p2sh.dart';
import 'payments/p2wpkh.dart';

class Address {
static bool validateAddress(String address, [NetworkType nw]) {
static bool validateAddress(String address, [NetworkType? nw]) {
try {
addressToOutputScript(address, nw);
return true;
Expand All @@ -17,7 +19,7 @@ class Address {
}
}

static Uint8List addressToOutputScript(String address, [NetworkType nw]) {
static Uint8List addressToOutputScript(String address, [NetworkType? nw]) {
var network = nw ?? bitcoin;
var decodeBase58;
var decodeBech32;
Expand All @@ -30,12 +32,12 @@ class Address {
if (decodeBase58[0] == network.pubKeyHash) {
return P2PKH(data: PaymentData(address: address), network: network)
.data
.output;
.output!;
}
if (decodeBase58[0] == network.scriptHash) {
return P2SH(data: PaymentData(address: address), network: network)
.data
.output;
.output!;
}
throw ArgumentError('Invalid version or Network mismatch');
} else {
Expand All @@ -53,7 +55,7 @@ class Address {
}
var p2wpkh =
P2WPKH(data: PaymentData(address: address), network: network);
return p2wpkh.data.output;
return p2wpkh.data.output!;
}
}
throw ArgumentError(address + ' has no matching Script');
Expand Down
63 changes: 33 additions & 30 deletions lib/src/bitcoindart_base.dart
Original file line number Diff line number Diff line change
@@ -1,56 +1,54 @@
// TODO: Put public facing types in this file.
import 'dart:typed_data';

import 'package:bip32/bip32.dart' as bip32;
import 'package:bitcoindart/src/utils/magic_hash.dart';
import 'package:hex/hex.dart';
import 'package:bip32/bip32.dart' as bip32;

import 'ecpair.dart';
import 'models/networks.dart';
import 'payments/index.dart' show PaymentData;
import 'payments/p2pkh.dart';
import 'ecpair.dart';
import 'package:meta/meta.dart';

/// Checks if you are awesome. Spoiler: you are.
class HDWallet {
bip32.BIP32 _bip32;
P2PKH _p2pkh;
String seed;
late bip32.BIP32 _bip32;
late P2PKH _p2pkh;
String? seed;
NetworkType network;

String get privKey {
if (_bip32 == null) return null;
String? get privKey {
if (_bip32.privateKey == null) return null;
try {
return HEX.encode(_bip32.privateKey);
return HEX.encode(_bip32.privateKey!);
} catch (_) {
return null;
}
}

String get pubKey => _bip32 != null ? HEX.encode(_bip32.publicKey) : null;
String get pubKey => HEX.encode(_bip32.publicKey);

String get base58Priv {
if (_bip32 == null) return null;
String? get base58Priv {
try {
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;
String? get wif {
try {
return _bip32.toWIF();
} catch (_) {
return null;
}
}

String get address => _p2pkh != null ? _p2pkh.data.address : null;
String? get address => _p2pkh.data.address;

HDWallet(
{@required bip32, @required p2pkh, @required this.network, this.seed}) {
HDWallet({required bip32, required p2pkh, required this.network, this.seed}) {
_bip32 = bip32;
_p2pkh = p2pkh;
}
Expand All @@ -69,7 +67,7 @@ class HDWallet {
return HDWallet(bip32: bip32, p2pkh: p2pkh, network: network);
}

factory HDWallet.fromSeed(Uint8List seed, {NetworkType network}) {
factory HDWallet.fromSeed(Uint8List seed, {NetworkType? network}) {
network = network ?? bitcoin;
final seedHex = HEX.encode(seed);
final wallet = bip32.BIP32.fromSeed(
Expand All @@ -84,7 +82,7 @@ class HDWallet {
bip32: wallet, p2pkh: p2pkh, network: network, seed: seedHex);
}

factory HDWallet.fromBase58(String xpub, {NetworkType network}) {
factory HDWallet.fromBase58(String xpub, {NetworkType? network}) {
network = network ?? bitcoin;
final wallet = bip32.BIP32.fromBase58(
xpub,
Expand All @@ -102,8 +100,12 @@ class HDWallet {
return _bip32.sign(messageHash);
}

bool verify({String message, Uint8List signature}) {
var messageHash = magicHash(message);
bool verify(
{required String message,
required Uint8List signature,
NetworkType? network}) {
network = network ?? bitcoin;
var messageHash = magicHash(message, network);
return _bip32.verify(messageHash, signature);
}
}
Expand All @@ -112,27 +114,28 @@ class Wallet {
ECPair _keyPair;
P2PKH _p2pkh;

String get privKey =>
_keyPair != null ? HEX.encode(_keyPair.privateKey) : null;
String? get privKey =>
_keyPair.privateKey != null ? HEX.encode(_keyPair.privateKey!) : null;

String get pubKey => _keyPair != null ? HEX.encode(_keyPair.publicKey) : null;
String get pubKey => HEX.encode(_keyPair.publicKey);

String get wif => _keyPair != null ? _keyPair.toWIF() : null;
String get wif => _keyPair.toWIF();

String get address => _p2pkh != null ? _p2pkh.data.address : null;
String? get address => _p2pkh.data.address;

NetworkType network;

Wallet(this._keyPair, this._p2pkh, this.network);

factory Wallet.random([NetworkType network]) {
factory Wallet.random([NetworkType? network]) {
network = network ?? bitcoin;
final _keyPair = ECPair.makeRandom(network: network);
final _p2pkh =
P2PKH(data: PaymentData(pubkey: _keyPair.publicKey), network: network);
return Wallet(_keyPair, _p2pkh, network);
}

factory Wallet.fromWIF(String wif, [NetworkType network]) {
factory Wallet.fromWIF(String wif, [NetworkType? network]) {
network = network ?? bitcoin;
final _keyPair = ECPair.fromWIF(wif, network: network);
final _p2pkh =
Expand All @@ -145,7 +148,7 @@ class Wallet {
return _keyPair.sign(messageHash);
}

bool verify({String message, Uint8List signature}) {
bool verify({required String message, required Uint8List signature}) {
var messageHash = magicHash(message, network);
return _keyPair.verify(messageHash, signature);
}
Expand Down
23 changes: 12 additions & 11 deletions lib/src/classify.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'dart:typed_data';

import '../src/utils/script.dart' as bscript;
import 'templates/pubkey.dart' as pubkey;
import 'templates/pubkeyhash.dart' as pubkeyhash;
import 'templates/scriptHash.dart' as scripthash;
import 'templates/pubkey.dart' as pubkey;
import 'templates/witnesspubkeyhash.dart' as witness_pubkey_hash;

const SCRIPT_TYPES = {
Expand All @@ -18,28 +19,28 @@ const SCRIPT_TYPES = {
};

String classifyOutput(Uint8List script) {
if (witness_pubkey_hash.outputCheck(script)) return SCRIPT_TYPES['P2WPKH'];
if (pubkeyhash.outputCheck(script)) return SCRIPT_TYPES['P2PKH'];
if (scripthash.outputCheck(script)) return SCRIPT_TYPES['P2SH'];
if (witness_pubkey_hash.outputCheck(script)) return SCRIPT_TYPES['P2WPKH']!;
if (pubkeyhash.outputCheck(script)) return SCRIPT_TYPES['P2PKH']!;
if (scripthash.outputCheck(script)) return SCRIPT_TYPES['P2SH']!;
final chunks = bscript.decompile(script);
if (chunks == null) throw ArgumentError('Invalid script');
return SCRIPT_TYPES['NONSTANDARD'];
return SCRIPT_TYPES['NONSTANDARD']!;
}

String classifyInput(Uint8List script, bool allowIncomplete) {
final chunks = bscript.decompile(script);
if (chunks == null) throw ArgumentError('Invalid script');
if (pubkeyhash.inputCheck(chunks)) return SCRIPT_TYPES['P2PKH'];
if (pubkeyhash.inputCheck(chunks)) return SCRIPT_TYPES['P2PKH']!;
if (scripthash.inputCheck(chunks, allowIncomplete)) {
return SCRIPT_TYPES['P2SH'];
return SCRIPT_TYPES['P2SH']!;
}
if (pubkey.inputCheck(chunks)) return SCRIPT_TYPES['P2PK'];
return SCRIPT_TYPES['NONSTANDARD'];
if (pubkey.inputCheck(chunks)) return SCRIPT_TYPES['P2PK']!;
return SCRIPT_TYPES['NONSTANDARD']!;
}

String classifyWitness(List<Uint8List> script) {
final chunks = bscript.decompile(script);
if (chunks == null) throw ArgumentError('Invalid script');
if (witness_pubkey_hash.inputCheck(chunks)) return SCRIPT_TYPES['P2WPKH'];
return SCRIPT_TYPES['NONSTANDARD'];
if (witness_pubkey_hash.inputCheck(chunks)) return SCRIPT_TYPES['P2WPKH']!;
return SCRIPT_TYPES['NONSTANDARD']!;
}
35 changes: 20 additions & 15 deletions lib/src/ecpair.dart
Original file line number Diff line number Diff line change
@@ -1,43 +1,47 @@
import 'dart:typed_data';
import 'dart:math';
import 'dart:typed_data';

import 'package:bip32/src/utils/ecurve.dart' as ecc;
import 'package:bip32/src/utils/wif.dart' as wif;

import 'models/networks.dart';

class ECPair {
Uint8List _d;
Uint8List _Q;
NetworkType network;
bool compressed;
ECPair(Uint8List _d, Uint8List _Q, {network, compressed}) {
Uint8List? _d;
Uint8List? _Q;
late NetworkType network;
late bool compressed;
ECPair(Uint8List? _d, Uint8List? _Q,
{NetworkType? network, bool? compressed}) {
this._d = _d;
this._Q = _Q;
this.network = network ?? bitcoin;
this.compressed = compressed ?? true;
}
Uint8List get publicKey {
_Q ??= ecc.pointFromScalar(_d, compressed);
return _Q;
_Q ??= ecc.pointFromScalar(_d!, compressed);
return _Q!;
}

Uint8List get privateKey => _d;
Uint8List? get privateKey => _d;

String toWIF() {
if (privateKey == null) {
throw ArgumentError('Missing private key');
}
return wif.encode(wif.WIF(
version: network.wif, privateKey: privateKey, compressed: compressed));
version: network.wif, privateKey: privateKey!, compressed: compressed));
}

Uint8List sign(Uint8List hash) {
return ecc.sign(hash, privateKey);
return ecc.sign(hash, privateKey!);
}

bool verify(Uint8List hash, Uint8List signature) {
return ecc.verify(hash, publicKey, signature);
}

factory ECPair.fromWIF(String w, {NetworkType network}) {
factory ECPair.fromWIF(String w, {NetworkType? network}) {
var decoded = wif.decode(w);
final version = decoded.version;
// TODO support multi networks
Expand All @@ -58,14 +62,14 @@ class ECPair {
compressed: decoded.compressed, network: nw);
}
factory ECPair.fromPublicKey(Uint8List publicKey,
{NetworkType network, bool compressed}) {
{NetworkType? network, bool? compressed}) {
if (!ecc.isPoint(publicKey)) {
throw ArgumentError('Point is not on the curve');
}
return ECPair(null, publicKey, network: network, compressed: compressed);
}
factory ECPair.fromPrivateKey(Uint8List privateKey,
{NetworkType network, bool compressed}) {
{NetworkType? network, bool? compressed}) {
if (privateKey.length != 32) {
throw ArgumentError(
'Expected property privateKey of type Buffer(Length: 32)');
Expand All @@ -76,7 +80,8 @@ class ECPair {
return ECPair(privateKey, null, network: network, compressed: compressed);
}
factory ECPair.makeRandom(
{NetworkType network, bool compressed, Function rng}) {
{NetworkType? network, bool? compressed, Function? rng}) {
network = network ?? bitcoin;
final rfunc = rng ?? _randomBytes;
Uint8List d;
// int beginTime = DateTime.now().millisecondsSinceEpoch;
Expand Down
Loading