Skip to content

Nullsafety #61

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

Open
wants to merge 3 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
14 changes: 14 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "bitcoin_flutter",
"request": "launch",
"type": "dart",
"program": "example/bitcoin_flutter_example.dart",
}
]
}
3 changes: 1 addition & 2 deletions lib/bitcoin_flutter.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/// Support for doing something awesome.
///
/// More dartdocs go here.
// @dart=2.12
library bitcoin_flutter;

export 'src/bitcoin_flutter_base.dart';
export 'src/models/networks.dart';
export 'src/transaction.dart';
export 'src/address.dart';
export 'src/transaction_builder.dart';
export 'src/ecpair.dart';
export 'src/payments/p2pkh.dart';
export 'src/payments/p2wpkh.dart';
Expand Down
26 changes: 11 additions & 15 deletions lib/src/address.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'payments/p2pkh.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 @@ -16,33 +16,29 @@ class Address {
}
}

static Uint8List addressToOutputScript(String address, [NetworkType nw]) {
static Uint8List addressToOutputScript(String address, [NetworkType? nw]) {
NetworkType network = nw ?? bitcoin;
var decodeBase58;
var decodeBech32;
try {
decodeBase58 = bs58check.decode(address);
} catch (err) {}
if (decodeBase58 != null) {
if (decodeBase58[0] != network.pubKeyHash)
throw new ArgumentError('Invalid version or Network mismatch');
P2PKH p2pkh =
new P2PKH(data: new PaymentData(address: address), network: network);
return p2pkh.data.output;
if (decodeBase58[0] != network.pubKeyHash) throw ArgumentError('Invalid version or Network mismatch');
P2PKH p2pkh = P2PKH(data: PaymentData(address: address), network: network);
return p2pkh.data.output!;
} else {
try {
decodeBech32 = segwit.decode(address);
} catch (err) {}
if (decodeBech32 != null) {
if (network.bech32 != decodeBech32.hrp)
throw new ArgumentError('Invalid prefix or Network mismatch');
if (decodeBech32.version != 0)
throw new ArgumentError('Invalid address version');
P2WPKH p2wpkh = new P2WPKH(
data: new PaymentData(address: address), network: network);
return p2wpkh.data.output;
if (network.bech32 != decodeBech32.hrp) throw ArgumentError('Invalid prefix or Network mismatch');
if (decodeBech32.version != 0) throw ArgumentError('Invalid address version');
P2WPKH p2wpkh = P2WPKH(data: PaymentData(address: address), network: network);
if (p2wpkh.data.output == null) throw ArgumentError('p2wpkh.data.output is null');
return p2wpkh.data.output!;
}
}
throw new ArgumentError(address + ' has no matching Script');
throw ArgumentError(address + ' has no matching Script');
}
}
104 changes: 44 additions & 60 deletions lib/src/bitcoin_flutter_base.dart
Original file line number Diff line number Diff line change
@@ -1,143 +1,127 @@
// TODO: Put public facing types in this file.
import 'dart:typed_data';

import 'package:bip32/bip32.dart' as bip32;
import 'package:bitcoin_flutter/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';
import 'dart:convert';

/// Checks if you are awesome. Spoiler: you are.
class HDWallet {
bip32.BIP32 _bip32;
P2PKH _p2pkh;
String seed;
bip32.BIP32? _bip32;
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 => _bip32?.publicKey != null ? HEX.encode(_bip32!.publicKey) : null;

String get base58Priv {
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 != null ? _bip32!.neutered().toBase58() : null;

String get wif {
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 != null ? _p2pkh!.data.address : null;

HDWallet(
{@required bip32, @required p2pkh, @required this.network, this.seed}) {
HDWallet({required bip32, required p2pkh, required this.network, this.seed}) {
this._bip32 = bip32;
this._p2pkh = p2pkh;
}

HDWallet derivePath(String path) {
final bip32 = _bip32.derivePath(path);
final p2pkh = new P2PKH(
data: new PaymentData(pubkey: bip32.publicKey), network: network);
final bip32 = _bip32!.derivePath(path);
final p2pkh = P2PKH(data: PaymentData(pubkey: bip32.publicKey), network: network);
return HDWallet(bip32: bip32, p2pkh: p2pkh, network: network);
}

HDWallet derive(int index) {
final bip32 = _bip32.derive(index);
final p2pkh = new P2PKH(
data: new PaymentData(pubkey: bip32.publicKey), network: network);
HDWallet? derive(int index) {
if (_bip32 == null) return null;
final bip32 = _bip32!.derive(index);
final p2pkh = P2PKH(data: PaymentData(pubkey: bip32.publicKey), network: network);
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(
seed,
bip32.NetworkType(
bip32: bip32.Bip32Type(
public: network.bip32.public, private: network.bip32.private),
wif: network.wif));
final p2pkh = new P2PKH(
data: new PaymentData(pubkey: wallet.publicKey), network: network);
return HDWallet(
bip32: wallet, p2pkh: p2pkh, network: network, seed: seedHex);
seed, bip32.NetworkType(bip32: bip32.Bip32Type(public: network.bip32.public, private: network.bip32.private), wif: network.wif));
final p2pkh = P2PKH(data: PaymentData(pubkey: wallet.publicKey), network: network);
return 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,
bip32.NetworkType(
bip32: bip32.Bip32Type(
public: network.bip32.public, private: network.bip32.private),
wif: network.wif));
final p2pkh = new P2PKH(
data: new PaymentData(pubkey: wallet.publicKey), network: network);
xpub, bip32.NetworkType(bip32: bip32.Bip32Type(public: network.bip32.public, private: network.bip32.private), wif: network.wif));
final p2pkh = P2PKH(data: PaymentData(pubkey: wallet.publicKey), network: network);
return HDWallet(bip32: wallet, p2pkh: p2pkh, network: network, seed: null);
}

Uint8List sign(String message) {
Uint8List messageHash = magicHash(message, network);
return _bip32.sign(messageHash);
return _bip32!.sign(messageHash);
}

bool verify({String message, Uint8List signature}) {
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;
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 != null ? _p2pkh!.data.address : null;

NetworkType network;

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

factory Wallet.random([NetworkType network]) {
factory Wallet.random(NetworkType network) {
final _keyPair = ECPair.makeRandom(network: network);
final _p2pkh = new P2PKH(
data: new PaymentData(pubkey: _keyPair.publicKey), 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 = new P2PKH(
data: new PaymentData(pubkey: _keyPair.publicKey), network: network);
final _p2pkh = P2PKH(data: PaymentData(pubkey: _keyPair.publicKey), network: network);
return Wallet(_keyPair, _p2pkh, network);
}

Expand All @@ -146,7 +130,7 @@ class Wallet {
return _keyPair.sign(messageHash);
}

bool verify({String message, Uint8List signature}) {
bool verify({required String message, required Uint8List signature}) {
Uint8List messageHash = magicHash(message, network);
return _keyPair.verify(messageHash, signature);
}
Expand Down
12 changes: 6 additions & 6 deletions lib/src/classify.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const SCRIPT_TYPES = {
'WITNESS_COMMITMENT': 'witnesscommitment'
};

String classifyOutput(Uint8List script) {
String? classifyOutput(Uint8List script) {
if (witnessPubKeyHash.outputCheck(script)) return SCRIPT_TYPES['P2WPKH'];
if (pubkeyhash.outputCheck(script)) return SCRIPT_TYPES['P2PKH'];
final chunks = bscript.decompile(script);
Expand All @@ -27,14 +27,14 @@ String classifyOutput(Uint8List script) {
String classifyInput(Uint8List script) {
final chunks = bscript.decompile(script);
if (chunks == null) throw new ArgumentError('Invalid script');
if (pubkeyhash.inputCheck(chunks)) return SCRIPT_TYPES['P2PKH'];
if (pubkey.inputCheck(chunks)) return SCRIPT_TYPES['P2PK'];
return SCRIPT_TYPES['NONSTANDARD'];
if (pubkeyhash.inputCheck(chunks)) return SCRIPT_TYPES['P2PKH']!;
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 new ArgumentError('Invalid script');
if (witnessPubKeyHash.inputCheck(chunks)) return SCRIPT_TYPES['P2WPKH'];
return SCRIPT_TYPES['NONSTANDARD'];
if (witnessPubKeyHash.inputCheck(chunks)) return SCRIPT_TYPES['P2WPKH']!;
return SCRIPT_TYPES['NONSTANDARD']!;
}
50 changes: 20 additions & 30 deletions lib/src/ecpair.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,38 @@ 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}) {
late Uint8List? _d;
late Uint8List? _Q;
late NetworkType network;
late bool compressed;
ECPair(Uint8List? _d, Uint8List? _Q, {network, compressed}) {
this._d = _d;
this._Q = _Q;
this.network = network ?? bitcoin;
this.compressed = compressed ?? true;
}
Uint8List get publicKey {
if (_Q == null) _Q = ecc.pointFromScalar(_d, compressed);
return _Q;
if (_Q == null) _Q = ecc.pointFromScalar(_d!, compressed);
return _Q!;
}

Uint8List get privateKey => _d;
Uint8List? get privateKey => _d;
String toWIF() {
if (privateKey == null) {
throw new ArgumentError('Missing private key');
}
return wif.encode(new wif.WIF(
version: network.wif, privateKey: privateKey, compressed: compressed));
return wif.encode(new wif.WIF(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}) {
wif.WIF decoded = wif.decode(w);
final version = decoded.version;
// TODO support multi networks
Expand All @@ -54,29 +53,20 @@ class ECPair {
throw new ArgumentError('Unknown network version');
}
}
return ECPair.fromPrivateKey(decoded.privateKey,
compressed: decoded.compressed, network: nw);
return ECPair.fromPrivateKey(decoded.privateKey, compressed: decoded.compressed, network: nw);
}
factory ECPair.fromPublicKey(Uint8List publicKey,
{NetworkType network, bool compressed}) {
factory ECPair.fromPublicKey(Uint8List publicKey, {NetworkType? network, bool? compressed}) {
if (!ecc.isPoint(publicKey)) {
throw new ArgumentError('Point is not on the curve');
throw ArgumentError('Point is not on the curve');
}
return new ECPair(null, publicKey,
network: network, compressed: compressed);
return ECPair(null, publicKey, network: network, compressed: compressed);
}
factory ECPair.fromPrivateKey(Uint8List privateKey,
{NetworkType network, bool compressed}) {
if (privateKey.length != 32)
throw new ArgumentError(
'Expected property privateKey of type Buffer(Length: 32)');
if (!ecc.isPrivate(privateKey))
throw new ArgumentError('Private key not in range [1, n)');
return new ECPair(privateKey, null,
network: network, compressed: compressed);
factory ECPair.fromPrivateKey(Uint8List privateKey, {NetworkType? network, bool? compressed}) {
if (privateKey.length != 32) throw new ArgumentError('Expected property privateKey of type Buffer(Length: 32)');
if (!ecc.isPrivate(privateKey)) throw new ArgumentError('Private key not in range [1, n)');
return new ECPair(privateKey, null, network: network, compressed: compressed);
}
factory ECPair.makeRandom(
{NetworkType network, bool compressed, Function rng}) {
factory ECPair.makeRandom({NetworkType? network, bool? compressed, Function? rng}) {
final rfunc = rng ?? _randomBytes;
Uint8List d;
// int beginTime = DateTime.now().millisecondsSinceEpoch;
Expand Down
Loading