Skip to content

Commit

Permalink
Merge pull request #3 from lemois-1337/2024-support-old-kaspa-derivat…
Browse files Browse the repository at this point in the history
…ion-path

Support for Karlsen and Kaspa derivation path wallets
  • Loading branch information
lemois-1337 authored Mar 15, 2024
2 parents b6a5681 + e867d20 commit 35b565a
Show file tree
Hide file tree
Showing 60 changed files with 317 additions and 135 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ dart pub global activate protoc_plugin 20.0.1
protoc --dart_out="grpc:lib/karlsen/grpc" -I./proto messages.proto p2p.proto rpc.proto --plugin ~/.pub-cache/bin/protoc-gen-dart
```

## Regenerate Freezed Code

If you need to regenerate the runtime code, please do the following:

```bash
flutter pub run build_runner build --delete-conflicting-outputs
```

## Translations

For some details regarding translations, have a look at
Expand Down
5 changes: 3 additions & 2 deletions lib/intro/intro_data_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ class IntroDataNotifier extends StateNotifier<IntroData> {

void generateNewMnemonic({int strength = 256}) {
final mnemonic = generateMnemonic(strength: strength);
setMnemonic(mnemonic, generated: true);
setMnemonic(mnemonic, generated: true, legacy: false);
}

void setMnemonic(String mnemonic, {bool generated = false}) {
void setMnemonic(String mnemonic, {bool generated = false, bool legacy = false}) {
final seed = compute(_computeSeed, mnemonic, debugLabel: 'ComputeSeed');
state = state.copyWith(
mnemonic: mnemonic,
generated: generated,
legacy: legacy,
seed: seed,
kpub: null,
);
Expand Down
5 changes: 3 additions & 2 deletions lib/intro/intro_import_seed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ final _showInvalidChecksumProvider =

class IntroImportSeed extends HookConsumerWidget {
final bool isLegacy;
const IntroImportSeed({Key? key, this.isLegacy = false}) : super(key: key);
final bool isLegacyDerivation;
const IntroImportSeed({Key? key, this.isLegacy = false, this.isLegacyDerivation = false}) : super(key: key);

int get mnemonicLength => isLegacy ? 12 : 24;

Expand Down Expand Up @@ -160,7 +161,7 @@ class IntroImportSeed extends HookConsumerWidget {
final intro = ref.read(introStateProvider.notifier);

if (isValidMnemonic(mnemonic, verifyChecksum: false)) {
intro.setMnemonic(mnemonic);
intro.setMnemonic(mnemonic, legacy: isLegacyDerivation);
}
}

Expand Down
10 changes: 10 additions & 0 deletions lib/intro/intro_import_select.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ class IntroImportSelect extends ConsumerWidget {
final styles = ref.watch(stylesProvider);
final l10n = l10nOf(context);

void importWalletLegacyDerivation() {
final notifier = ref.read(introStateProvider.notifier);
notifier.importWalletLegacyDerivation();
}

void importWallet() {
final notifier = ref.read(introStateProvider.notifier);
notifier.importWallet();
Expand Down Expand Up @@ -133,6 +138,11 @@ class IntroImportSelect extends ConsumerWidget {
description: l10n.importOption24WordsDescription,
onPressed: importWallet,
),
ImportWalletTypeCard(
title: l10n.importOption24WordsLegacyTitle,
description: l10n.importOption24WordsLegacyDescription,
onPressed: importWalletLegacyDerivation,
),
ImportWalletTypeCard(
title: l10n.importOption12WordsTitle,
description: l10n.importOption12WordsDescription,
Expand Down
8 changes: 6 additions & 2 deletions lib/intro/intro_state_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class IntroStateNotifier extends StateNotifier<IntroState> {
_goToPage(IntroPage.importSelect);
}

void importWalletLegacyDerivation() {
_goToPage(IntroPage.importSeedLegacyDerivation);
}

void importWallet() {
_goToPage(IntroPage.importSeed);
}
Expand Down Expand Up @@ -62,11 +66,11 @@ class IntroStateNotifier extends StateNotifier<IntroState> {
}
}

void setMnemonic(String mnemonic, {String? walletName}) {
void setMnemonic(String mnemonic, {String? walletName, bool legacy = false}) {
if (walletName != null) {
introData.setName(walletName);
}
introData.setMnemonic(mnemonic);
introData.setMnemonic(mnemonic, legacy: legacy);
_goToPage(IntroPage.walletName);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/intro/intro_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum IntroPage {
password,
passwordOnLaunch,
importSelect,
importSeedLegacyDerivation,
importSeed,
importLegacySeed,
importKpub,
Expand All @@ -31,6 +32,7 @@ class IntroData with _$IntroData {
String? name,
String? pin,
String? password,
@Default(false) bool legacy,
@Default(false) bool generated,
@Default(false) bool completed,
}) = _IntroData;
Expand Down
25 changes: 23 additions & 2 deletions lib/intro/intro_types.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ mixin _$IntroData {
String? get name => throw _privateConstructorUsedError;
String? get pin => throw _privateConstructorUsedError;
String? get password => throw _privateConstructorUsedError;
bool get legacy => throw _privateConstructorUsedError;
bool get generated => throw _privateConstructorUsedError;
bool get completed => throw _privateConstructorUsedError;

Expand All @@ -464,6 +465,7 @@ abstract class $IntroDataCopyWith<$Res> {
String? name,
String? pin,
String? password,
bool legacy,
bool generated,
bool completed});
}
Expand All @@ -487,6 +489,7 @@ class _$IntroDataCopyWithImpl<$Res, $Val extends IntroData>
Object? name = freezed,
Object? pin = freezed,
Object? password = freezed,
Object? legacy = null,
Object? generated = null,
Object? completed = null,
}) {
Expand Down Expand Up @@ -515,6 +518,10 @@ class _$IntroDataCopyWithImpl<$Res, $Val extends IntroData>
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String?,
legacy: null == legacy
? _value.legacy
: legacy // ignore: cast_nullable_to_non_nullable
as bool,
generated: null == generated
? _value.generated
: generated // ignore: cast_nullable_to_non_nullable
Expand All @@ -541,6 +548,7 @@ abstract class _$$_IntroDataCopyWith<$Res> implements $IntroDataCopyWith<$Res> {
String? name,
String? pin,
String? password,
bool legacy,
bool generated,
bool completed});
}
Expand All @@ -562,6 +570,7 @@ class __$$_IntroDataCopyWithImpl<$Res>
Object? name = freezed,
Object? pin = freezed,
Object? password = freezed,
Object? legacy = null,
Object? generated = null,
Object? completed = null,
}) {
Expand Down Expand Up @@ -590,6 +599,10 @@ class __$$_IntroDataCopyWithImpl<$Res>
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String?,
legacy: null == legacy
? _value.legacy
: legacy // ignore: cast_nullable_to_non_nullable
as bool,
generated: null == generated
? _value.generated
: generated // ignore: cast_nullable_to_non_nullable
Expand All @@ -612,6 +625,7 @@ class _$_IntroData implements _IntroData {
this.name,
this.pin,
this.password,
this.legacy = false,
this.generated = false,
this.completed = false});

Expand All @@ -629,14 +643,17 @@ class _$_IntroData implements _IntroData {
final String? password;
@override
@JsonKey()
final bool legacy;
@override
@JsonKey()
final bool generated;
@override
@JsonKey()
final bool completed;

@override
String toString() {
return 'IntroData(mnemonic: $mnemonic, kpub: $kpub, seed: $seed, name: $name, pin: $pin, password: $password, generated: $generated, completed: $completed)';
return 'IntroData(mnemonic: $mnemonic, kpub: $kpub, seed: $seed, name: $name, pin: $pin, password: $password, legacy: $legacy, generated: $generated, completed: $completed)';
}

@override
Expand All @@ -652,6 +669,7 @@ class _$_IntroData implements _IntroData {
(identical(other.pin, pin) || other.pin == pin) &&
(identical(other.password, password) ||
other.password == password) &&
(identical(other.legacy, legacy) || other.legacy == legacy) &&
(identical(other.generated, generated) ||
other.generated == generated) &&
(identical(other.completed, completed) ||
Expand All @@ -660,7 +678,7 @@ class _$_IntroData implements _IntroData {

@override
int get hashCode => Object.hash(runtimeType, mnemonic, kpub, seed, name, pin,
password, generated, completed);
password, legacy, generated, completed);

@JsonKey(ignore: true)
@override
Expand All @@ -677,6 +695,7 @@ abstract class _IntroData implements IntroData {
final String? name,
final String? pin,
final String? password,
final bool legacy,
final bool generated,
final bool completed}) = _$_IntroData;

Expand All @@ -693,6 +712,8 @@ abstract class _IntroData implements IntroData {
@override
String? get password;
@override
bool get legacy;
@override
bool get generated;
@override
bool get completed;
Expand Down
30 changes: 15 additions & 15 deletions lib/karlsen/bip32_kdx.dart → lib/karlsen/bip32_desktop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import 'package:coinslib/src/models/networks.dart' as networks;
import 'package:coinslib/src/utils/crypto.dart';
import 'package:coinslib/src/utils/ecurve.dart' as ecc;

class BIP32Kdx extends BIP32 {
BIP32Kdx(super._d, super._Q, super.chainCode, super.network);
class BIP32Desktop extends BIP32 {
BIP32Desktop(super._d, super._Q, super.chainCode, super.network);

@override
BIP32 derive(int index) {
Expand All @@ -33,23 +33,23 @@ class BIP32Kdx extends BIP32 {
if (!ecc.isPrivate(il)) {
return derive(index + 1);
}
BIP32Kdx hd;
BIP32Desktop hd;
if (!isNeutered()) {
final ki = ecc.privateAdd(privateKey!, il);
if (ki == null) return derive(index + 1);
hd = BIP32Kdx.fromPrivateKey(ki, ir, network);
hd = BIP32Desktop.fromPrivateKey(ki, ir, network);
} else {
final ki = ecc.pointAddScalar(publicKey, il, true);
if (ki == null) return derive(index + 1);
hd = BIP32Kdx.fromPublicKey(ki, ir, network);
hd = BIP32Desktop.fromPublicKey(ki, ir, network);
}
hd.depth = depth + 1;
hd.index = index;
hd.parentFingerprint = fingerprint.buffer.asByteData().getUint32(0);
return hd;
}

factory BIP32Kdx.fromBase58(String string, [NetworkType? nw]) {
factory BIP32Desktop.fromBase58(String string, [NetworkType? nw]) {
Uint8List buffer = bs58check.decode(string);
if (buffer.length != 78) throw ArgumentError("Invalid buffer length");
NetworkType network = nw ?? networks.bitcoin;
Expand Down Expand Up @@ -77,27 +77,27 @@ class BIP32Kdx extends BIP32 {

// 32 bytes: the chain code
Uint8List chainCode = buffer.sublist(13, 45);
BIP32Kdx hd;
BIP32Desktop hd;

// 33 bytes: private key data (0x00 + k)
if (version == network.bip32.private) {
if (bytes.getUint8(45) != 0x00) {
throw ArgumentError("Invalid private key");
}
Uint8List k = buffer.sublist(46, 78);
hd = BIP32Kdx.fromPrivateKey(k, chainCode, network);
hd = BIP32Desktop.fromPrivateKey(k, chainCode, network);
} else {
// 33 bytes: public key data (0x02 + X or 0x03 + X)
Uint8List X = buffer.sublist(45, 78);
hd = BIP32Kdx.fromPublicKey(X, chainCode, network);
hd = BIP32Desktop.fromPublicKey(X, chainCode, network);
}
hd.depth = depth;
hd.index = index;
hd.parentFingerprint = parentFingerprint;
return hd;
}

factory BIP32Kdx.fromPublicKey(
factory BIP32Desktop.fromPublicKey(
Uint8List publicKey,
Uint8List chainCode, [
NetworkType? nw,
Expand All @@ -106,10 +106,10 @@ class BIP32Kdx extends BIP32 {
if (!ecc.isPoint(publicKey)) {
throw ArgumentError("Point is not on the curve");
}
return BIP32Kdx(null, publicKey, chainCode, network);
return BIP32Desktop(null, publicKey, chainCode, network);
}

factory BIP32Kdx.fromPrivateKey(
factory BIP32Desktop.fromPrivateKey(
Uint8List privateKey,
Uint8List chainCode, [
NetworkType? nw,
Expand All @@ -123,10 +123,10 @@ class BIP32Kdx extends BIP32 {
if (!ecc.isPrivate(privateKey)) {
throw ArgumentError("Private key not in range [1, n]");
}
return BIP32Kdx(privateKey, null, chainCode, network);
return BIP32Desktop(privateKey, null, chainCode, network);
}

factory BIP32Kdx.fromSeed(Uint8List seed, [NetworkType? nw]) {
factory BIP32Desktop.fromSeed(Uint8List seed, [NetworkType? nw]) {
if (seed.length < 16) {
throw ArgumentError("Seed should be at least 128 bits");
}
Expand All @@ -137,6 +137,6 @@ class BIP32Kdx extends BIP32 {
final i = hmacSHA512(utf8.encode("Bitcoin seed") as Uint8List, seed);
final il = i.sublist(0, 32);
final ir = i.sublist(32);
return BIP32Kdx.fromPrivateKey(il, ir, network);
return BIP32Desktop.fromPrivateKey(il, ir, network);
}
}
8 changes: 4 additions & 4 deletions lib/karlsen/transaction/types.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ abstract class $ScriptPublicKeyCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(fromJson: hexToBytes, toJson: bytesToHex)
Uint8List scriptPublicKey,
Uint8List scriptPublicKey,
int version});
}

Expand Down Expand Up @@ -623,7 +623,7 @@ abstract class _$$_ScriptPublicKeyCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(fromJson: hexToBytes, toJson: bytesToHex)
Uint8List scriptPublicKey,
Uint8List scriptPublicKey,
int version});
}

Expand Down Expand Up @@ -659,7 +659,7 @@ class __$$_ScriptPublicKeyCopyWithImpl<$Res>
class _$_ScriptPublicKey extends _ScriptPublicKey {
const _$_ScriptPublicKey(
{@JsonKey(fromJson: hexToBytes, toJson: bytesToHex)
required this.scriptPublicKey,
required this.scriptPublicKey,
required this.version})
: super._();

Expand Down Expand Up @@ -710,7 +710,7 @@ class _$_ScriptPublicKey extends _ScriptPublicKey {
abstract class _ScriptPublicKey extends ScriptPublicKey {
const factory _ScriptPublicKey(
{@JsonKey(fromJson: hexToBytes, toJson: bytesToHex)
required final Uint8List scriptPublicKey,
required final Uint8List scriptPublicKey,
required final int version}) = _$_ScriptPublicKey;
const _ScriptPublicKey._() : super._();

Expand Down
Loading

0 comments on commit 35b565a

Please sign in to comment.