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

null safety upgrade #14

Merged
merged 1 commit into from
May 27, 2021
Merged
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
18 changes: 12 additions & 6 deletions example/bip32_example.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'dart:typed_data';
import '../lib/bip32.dart' as bip32;
import 'package:hex/hex.dart';

main() {
bip32.BIP32 node = bip32.BIP32.fromBase58('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi');
bip32.BIP32 node = bip32.BIP32.fromBase58(
'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi');

print(HEX.encode(node.privateKey));
print(HEX.encode(node.privateKey!));
// => e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35

bip32.BIP32 nodeNeutered = node.neutered();
Expand All @@ -21,7 +23,7 @@ main() {
print(child.toBase58());
// => xprv9ww7sMFLzJMzur2oEQDB642fbsMS4q6JRraMVTrM9bTWBq7NDS8ZpmsKVB4YF3mZecqax1fjnsPF19xnsJNfRp4RSyexacULXMKowSACTRc

print(HEX.encode(child.privateKey));
print(HEX.encode(child.privateKey!));
// => f26cf12f89ab91aeeb8d7324a22e8ba080829db15c9245414b073a8c342322aa

bip32.BIP32 childNeutered = child.neutered();
Expand All @@ -34,15 +36,19 @@ main() {
print(childNeutered.toBase58());
// => xpub6AvUGrnEpfvJ8L7GLRkBTByQ9uBvUHp9o5VxHrFxhvzV4dSWkySpNaBoLR9FpbnwRmTa69yLHF3QfcaxbWT7gWdwws5k4dpmJvqpEuMWwnj

bip32.BIP32 nodeFromSeed = bip32.BIP32.fromSeed(HEX.decode("000102030405060708090a0b0c0d0e0f"));
bip32.BIP32 nodeFromSeed = bip32.BIP32
.fromSeed(HEX.decode("000102030405060708090a0b0c0d0e0f") as Uint8List);
print(nodeFromSeed.toBase58());
// => xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi

bip32.BIP32 nodeFromPub = bip32.BIP32.fromBase58("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
bip32.BIP32 nodeFromPub = bip32.BIP32.fromBase58(
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
print(nodeFromPub.toBase58());
// => xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8

var message = HEX.decode("0202020202020202020202020202020202020202020202020202020202020202");
var message = HEX.decode(
"0202020202020202020202020202020202020202020202020202020202020202")
as Uint8List;
var signature = nodeFromSeed.sign(message);
print(signature);
// => [63, 219, 20, 114, 95, 184, 192, 55, 216, 206, 126, 121, 17, 71, 64, 70, 163, 82, 247, 73, 243, 95, 30, 137, 177, 155, 100, 225, 177, 203, 217, 147, 122, 64, 208, 129, 54, 133, 113, 41, 216, 160, 191, 15, 136, 98, 235, 25, 219, 178, 70, 222, 127, 151, 135, 242, 25, 192, 161, 187, 187, 84, 81, 215]
Expand Down
79 changes: 42 additions & 37 deletions lib/src/bip32_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,25 @@ import 'dart:convert';
class Bip32Type {
int public;
int private;
Bip32Type({this.public, this.private});

Bip32Type({required this.public, required this.private});
}

class NetworkType {
int wif;
Bip32Type bip32;
NetworkType({this.wif, this.bip32});
NetworkType({required this.wif, required this.bip32});
}

final _BITCOIN = new NetworkType(
wif: 0x80,
bip32: new Bip32Type(
public: 0x0488b21e,
private: 0x0488ade4
)
);
wif: 0x80, bip32: new Bip32Type(public: 0x0488b21e, private: 0x0488ade4));
const HIGHEST_BIT = 0x80000000;
const UINT31_MAX = 2147483647; // 2^31 - 1
const UINT32_MAX = 4294967295; // 2^32 - 1

/// Checks if you are awesome. Spoiler: you are.
class BIP32 {
Uint8List _d;
Uint8List _Q;
Uint8List? _d;
Uint8List? _Q;
Uint8List chainCode;
int depth = 0;
int index = 0;
Expand All @@ -40,11 +36,11 @@ class BIP32 {
BIP32(this._d, this._Q, this.chainCode, this.network);

Uint8List get publicKey {
if (_Q == null) _Q = ecc.pointFromScalar(_d, true);
return _Q;
if (_Q == null) _Q = ecc.pointFromScalar(_d!, true)!;
return _Q!;
}

Uint8List get privateKey => _d;
Uint8List? get privateKey => _d;
Uint8List get identifier => hash160(publicKey);
Uint8List get fingerprint => identifier.sublist(0, 4);

Expand All @@ -53,15 +49,17 @@ class BIP32 {
}

BIP32 neutered() {
final neutered = BIP32.fromPublicKey(this.publicKey, this.chainCode, this.network);
final neutered =
BIP32.fromPublicKey(this.publicKey, this.chainCode, this.network);
neutered.depth = this.depth;
neutered.index = this.index;
neutered.parentFingerprint = this.parentFingerprint;
return neutered;
}

String toBase58() {
final version = (!isNeutered()) ? network.bip32.private : network.bip32.public;
final version =
(!isNeutered()) ? network.bip32.private : network.bip32.public;
Uint8List buffer = new Uint8List(78);
ByteData bytes = buffer.buffer.asByteData();
bytes.setUint32(0, version);
Expand All @@ -71,7 +69,7 @@ class BIP32 {
buffer.setRange(13, 45, chainCode);
if (!isNeutered()) {
bytes.setUint8(45, 0);
buffer.setRange(46, 78, privateKey);
buffer.setRange(46, 78, privateKey!);
} else {
buffer.setRange(45, 78, publicKey);
}
Expand All @@ -83,22 +81,20 @@ class BIP32 {
throw new ArgumentError("Missing private key");
}
return wif.encode(new wif.WIF(
version: network.wif,
privateKey: privateKey,
compressed: true
));
version: network.wif, privateKey: privateKey!, compressed: true));
}

BIP32 derive(int index) {
if (index > UINT32_MAX || index < 0) throw new ArgumentError("Expected UInt32");
if (index > UINT32_MAX || index < 0)
throw new ArgumentError("Expected UInt32");
final isHardened = index >= HIGHEST_BIT;
Uint8List data = new Uint8List(37);
if (isHardened) {
if (isNeutered()) {
throw new ArgumentError("Missing private key for hardened child key");
}
data[0] = 0x00;
data.setRange(1, 33, privateKey);
data.setRange(1, 33, privateKey!);
data.buffer.asByteData().setUint32(33, index);
} else {
data.setRange(0, 33, publicKey);
Expand All @@ -112,7 +108,7 @@ class BIP32 {
}
BIP32 hd;
if (!isNeutered()) {
final ki = ecc.privateAdd(privateKey, IL);
final ki = ecc.privateAdd(privateKey!, IL);
if (ki == null) return derive(index + 1);
hd = BIP32.fromPrivateKey(ki, IR, network);
} else {
Expand All @@ -127,7 +123,8 @@ class BIP32 {
}

BIP32 deriveHardened(int index) {
if (index > UINT31_MAX || index < 0) throw new ArgumentError("Expected UInt31");
if (index > UINT31_MAX || index < 0)
throw new ArgumentError("Expected UInt31");
return this.derive(index + HIGHEST_BIT);
}

Expand All @@ -136,10 +133,11 @@ class BIP32 {
if (!regex.hasMatch(path)) throw new ArgumentError("Expected BIP32 Path");
List<String> splitPath = path.split("/");
if (splitPath[0] == "m") {
if (parentFingerprint != 0) throw new ArgumentError("Expected master, got child");
if (parentFingerprint != 0)
throw new ArgumentError("Expected master, got child");
splitPath = splitPath.sublist(1);
}
return splitPath.fold(this, (BIP32 prevHd,String indexStr) {
return splitPath.fold(this, (BIP32 prevHd, String indexStr) {
int index;
if (indexStr.substring(indexStr.length - 1) == "'") {
index = int.parse(indexStr.substring(0, indexStr.length - 1));
Expand All @@ -152,14 +150,14 @@ class BIP32 {
}

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

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

factory BIP32.fromBase58(String string, [NetworkType nw]) {
factory BIP32.fromBase58(String string, [NetworkType? nw]) {
Uint8List buffer = bs58check.decode(string);
if (buffer.length != 78) throw new ArgumentError("Invalid buffer length");
NetworkType network = nw ?? _BITCOIN;
Expand All @@ -175,7 +173,8 @@ class BIP32 {
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
var parentFingerprint = bytes.getUint32(5);
if (depth == 0) {
if (parentFingerprint != 0x00000000) throw new ArgumentError("Invalid parent fingerprint");
if (parentFingerprint != 0x00000000)
throw new ArgumentError("Invalid parent fingerprint");
}

// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
Expand All @@ -189,7 +188,8 @@ class BIP32 {

// 33 bytes: private key data (0x00 + k)
if (version == network.bip32.private) {
if (bytes.getUint8(45) != 0x00) throw new ArgumentError("Invalid private key");
if (bytes.getUint8(45) != 0x00)
throw new ArgumentError("Invalid private key");
Uint8List k = buffer.sublist(46, 78);
hd = BIP32.fromPrivateKey(k, chainCode, network);
} else {
Expand All @@ -203,30 +203,35 @@ class BIP32 {
return hd;
}

factory BIP32.fromPublicKey(Uint8List publicKey, Uint8List chainCode, [NetworkType nw]) {
factory BIP32.fromPublicKey(Uint8List publicKey, Uint8List chainCode,
[NetworkType? nw]) {
NetworkType network = nw ?? _BITCOIN;
if (!ecc.isPoint(publicKey)) {
throw new ArgumentError("Point is not on the curve");
}
return new BIP32(null, publicKey, chainCode, network);
}

factory BIP32.fromPrivateKey(Uint8List privateKey, Uint8List chainCode, [NetworkType nw]) {
factory BIP32.fromPrivateKey(Uint8List privateKey, Uint8List chainCode,
[NetworkType? nw]) {
NetworkType network = nw ?? _BITCOIN;
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]");
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 BIP32(privateKey, null, chainCode, network);
}

factory BIP32.fromSeed(Uint8List seed, [NetworkType nw]) {
factory BIP32.fromSeed(Uint8List seed, [NetworkType? nw]) {
if (seed.length < 16) {
throw new ArgumentError("Seed should be at least 128 bits");
}
if (seed.length > 64) {
throw new ArgumentError("Seed should be at most 512 bits");
}
NetworkType network = nw ?? _BITCOIN;
final I = hmacSHA512(utf8.encode("Bitcoin seed"), seed);
final I = hmacSHA512(utf8.encode("Bitcoin seed") as Uint8List, seed);
final IL = I.sublist(0, 32);
final IR = I.sublist(32);
return BIP32.fromPrivateKey(IL, IR, network);
Expand Down
Loading