Skip to content

Commit

Permalink
Merge pull request #13 from hypercore-one/feature/wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
KingGorrin authored Dec 12, 2023
2 parents d960c0b + a2bcf81 commit 5967f38
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 52 deletions.
2 changes: 1 addition & 1 deletion lib/src/global.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:path/path.dart' as path;

export 'package:logging/logging.dart' show Level;

const znnSdkVersion = '0.0.5';
const znnSdkVersion = '0.0.6';
const znnRootDirectory = 'znn';

class ZnnPaths {
Expand Down
37 changes: 17 additions & 20 deletions lib/src/utils/block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:znn_sdk_dart/src/model/primitives/hash.dart';
import 'package:znn_sdk_dart/src/model/primitives/hash_height.dart';
import 'package:znn_sdk_dart/src/pow/pow.dart';
import 'package:znn_sdk_dart/src/utils/utils.dart';
import 'package:znn_sdk_dart/src/wallet/keypair.dart';
import 'package:znn_sdk_dart/src/wallet/interfaces.dart';
import 'package:znn_sdk_dart/src/zenon.dart';

class BlockUtils {
Expand All @@ -25,6 +25,10 @@ class BlockUtils {
}

static Hash getTransactionHash(AccountBlockTemplate transaction) {
return Hash.digest(getTransactionBytes(transaction));
}

static List<int> getTransactionBytes(AccountBlockTemplate transaction) {
var versionBytes = BytesUtils.longToBytes(transaction.version);
var chainIdentifierBytes =
BytesUtils.longToBytes(transaction.chainIdentifier);
Expand Down Expand Up @@ -62,12 +66,7 @@ class BlockUtils {
nonceBytes
]);

return Hash.digest(source);
}

static Future<List<int>> _getTransactionSignature(
KeyPair keyPair, AccountBlockTemplate transaction) {
return keyPair.sign(transaction.hash.getBytes()!);
return source;
}

static Hash _getPoWData(AccountBlockTemplate transaction) {
Expand Down Expand Up @@ -98,11 +97,11 @@ class BlockUtils {
}

static Future<bool> _checkAndSetFields(
AccountBlockTemplate transaction, KeyPair currentKeyPair) async {
AccountBlockTemplate transaction, WalletAccount currentKeyPair) async {
var z = Zenon();

transaction.address = (await currentKeyPair.address)!;
transaction.publicKey = (await currentKeyPair.getPublicKey());
transaction.address = await currentKeyPair.getAddress();
transaction.publicKey = await currentKeyPair.getPublicKey();

await _autofillTransactionParameters(transaction);

Expand Down Expand Up @@ -149,10 +148,10 @@ class BlockUtils {
transaction.fusedPlasma = response.availablePlasma;
transaction.difficulty = response.requiredDifficulty.toInt();
logger.info(
'Generating Plasma for block: hash=${BlockUtils._getPoWData(transaction)}');
'Generating Plasma for block: hash=${_getPoWData(transaction)}');
generatingPowCallback?.call(PowStatus.generating);
transaction.nonce = await generatePoW(
BlockUtils._getPoWData(transaction), transaction.difficulty);
_getPoWData(transaction), transaction.difficulty);
generatingPowCallback?.call(PowStatus.done);
} else {
transaction.fusedPlasma = response.basePlasma;
Expand All @@ -163,16 +162,14 @@ class BlockUtils {
}

static Future<bool> _setHashAndSignature(
AccountBlockTemplate transaction, KeyPair currentKeyPair) async {
transaction.hash = BlockUtils.getTransactionHash(transaction);
var transSig =
await BlockUtils._getTransactionSignature(currentKeyPair, transaction);
transaction.signature = transSig;
AccountBlockTemplate transaction, WalletAccount currentKeyPair) async {
transaction.hash = getTransactionHash(transaction);
transaction.signature = await currentKeyPair.signTx(transaction);
return true;
}

static Future<AccountBlockTemplate> send(
AccountBlockTemplate transaction, KeyPair currentKeyPair,
AccountBlockTemplate transaction, WalletAccount currentKeyPair,
{void Function(PowStatus)? generatingPowCallback,
waitForRequiredPlasma = false}) async {
var z = Zenon();
Expand All @@ -189,10 +186,10 @@ class BlockUtils {
}

static Future<bool> requiresPoW(AccountBlockTemplate transaction,
{KeyPair? blockSigningKey}) async {
{WalletAccount? blockSigningKey}) async {
var z = Zenon();

transaction.address = (await blockSigningKey!.address)!;
transaction.address = await blockSigningKey!.getAddress();
var powParam = GetRequiredParam(
address: transaction.address,
blockType: transaction.blockType,
Expand Down
49 changes: 49 additions & 0 deletions lib/src/wallet/interfaces.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:znn_sdk_dart/src/model/primitives.dart';
import 'package:znn_sdk_dart/src/model/nom/account_block_template.dart';

// Represents the definition of a wallet.
abstract class WalletDefinition {
// Gets the id or path of the wallet.
String get walletId;

// Gets the name of the wallet.
String get walletName;
}

// Represents the options for retrieving a wallet.
abstract class WalletOptions {}

// Represents the wallet manager for interacting with wallets.
abstract class WalletManager {
// Gets the definition of wallets
Future<Iterable<WalletDefinition>> getWalletDefinitions();

// Gets a wallet by wallet definition.
Future<Wallet> getWallet(WalletDefinition walletDefinition,
[WalletOptions? options]);

// Determines whether or not the manager supports the given wallet definition.
// Returns true if the wallet is supported; otherwise false.
Future<bool> supportsWallet(WalletDefinition walletDefinition);
}

// Represents a wallet.
abstract class Wallet {
// Gets a wallet account by index.
Future<WalletAccount> getAccount([int index = 0]);
}

// Represents the account of a wallet.
abstract class WalletAccount {
// Gets the public key of the wallet account as an array of bytes.
Future<List<int>> getPublicKey();

// Gets the address of the wallet account.
Future<Address> getAddress();

// Signs an arbitrary message and returns the signature as an array of bytes.
Future<List<int>> sign(List<int> message);

// Signs a transaction and returns the signature as an array of bytes.
Future<List<int>> signTx(AccountBlockTemplate tx);
}
16 changes: 13 additions & 3 deletions lib/src/wallet/keypair.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import 'dart:async';

import 'package:znn_sdk_dart/src/crypto/crypto.dart';
import 'package:znn_sdk_dart/src/model/primitives.dart';

class KeyPair {
import 'package:znn_sdk_dart/src/wallet/interfaces.dart';
import 'package:znn_sdk_dart/src/model/nom/account_block_template.dart';
import 'package:znn_sdk_dart/src/utils/block.dart';
class KeyPair implements WalletAccount {
List<int>? privateKey;
List<int>? publicKey;
Address? _address;
Expand All @@ -24,17 +26,25 @@ class KeyPair {
}

Future<Address?> get address async {
return await getAddress();
}

Future<Address> getAddress() async {
if (_address == null) {
publicKey = await getPublicKey();
_address = Address.fromPublicKey(publicKey!);
}
return _address;
return _address!;
}

Future<List<int>> sign(List<int> message) async {
return Crypto.sign(message, privateKey, (await getPublicKey()));
}

Future<List<int>> signTx(AccountBlockTemplate tx) async {
return Crypto.sign(tx.hash.getBytes()!, privateKey, (await getPublicKey()));
}

Future<bool> verify(List<int> signature, List<int> message) async {
return Crypto.verify(signature, message, (await getPublicKey()));
}
Expand Down
27 changes: 26 additions & 1 deletion lib/src/wallet/keystore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,33 @@ import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:cryptography/cryptography.dart' as cryptography;
import 'package:hex/hex.dart';
import 'package:path/path.dart' as path;
import 'package:znn_sdk_dart/src/crypto/crypto.dart';
import 'package:znn_sdk_dart/src/model/primitives.dart';
import 'package:znn_sdk_dart/src/wallet/derivation.dart';
import 'package:znn_sdk_dart/src/wallet/keypair.dart';
import 'package:znn_sdk_dart/src/wallet/exceptions.dart';
import 'package:znn_sdk_dart/src/wallet/interfaces.dart';

class KeyStore {
class KeyStoreDefinition implements WalletDefinition {
final File file;

KeyStoreDefinition({required this.file}) {
if (!file.existsSync()) {
throw InvalidKeyStorePath('Given keyStore does not exist ($file)');
}
}

String get walletId {
return file.path;
}

String get walletName {
return path.basename(file.path);
}
}

class KeyStore implements Wallet {
String? mnemonic;
late String entropy;
String? seed;
Expand Down Expand Up @@ -48,6 +69,10 @@ class KeyStore {
setMnemonic(bip39.entropyToMnemonic(entropy));
}

Future<WalletAccount> getAccount([int index = 0]) async {
return getKeyPair(index);
}

KeyPair getKeyPair([int index = 0]) {
return KeyPair(
Crypto.deriveKey(Derivation.getDerivationAccount(index), seed!));
Expand Down
68 changes: 50 additions & 18 deletions lib/src/wallet/manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import 'package:znn_sdk_dart/src/global.dart';
import 'package:znn_sdk_dart/src/wallet/exceptions.dart';
import 'package:znn_sdk_dart/src/wallet/keyfile.dart';
import 'package:znn_sdk_dart/src/wallet/keystore.dart';
import 'package:znn_sdk_dart/src/wallet/interfaces.dart';

class KeyStoreOptions implements WalletOptions {
final String decryptionPassword;
KeyStoreOptions(this.decryptionPassword);
}

class SaveKeyStoreArguments {
final KeyStore store;
Expand All @@ -23,13 +29,13 @@ void saveKeyStoreFunction(SaveKeyStoreArguments args) async {
args.port.send(json.encode(encrypted));
}

class KeyStoreManager {
Directory? walletPath;
class KeyStoreManager implements WalletManager {
final Directory walletPath;
KeyStore? keyStoreInUse;

KeyStoreManager({this.walletPath});
KeyStoreManager({required this.walletPath});

Future<File> saveKeyStore(KeyStore store, String password,
Future<KeyStoreDefinition> saveKeyStore(KeyStore store, String password,
{String? name}) async {
name ??= (await store.getKeyPair(0).address).toString();

Expand All @@ -41,15 +47,15 @@ class KeyStoreManager {
onError: port.sendPort, onExit: port.sendPort);

StreamSubscription? sub;
var completer = Completer<File>();
var completer = Completer<KeyStoreDefinition>();

sub = port.listen((data) async {
if (data != null) {
if (data is String) {
logger.info(data);
var location = File(path.join(walletPath!.path, name));
var location = File(path.join(walletPath.path, name));
await location.writeAsString(data);
completer.complete(location);
completer.complete(KeyStoreDefinition(file: location));
} else {
throw data;
}
Expand Down Expand Up @@ -84,11 +90,11 @@ class KeyStoreManager {
return KeyFile.fromJson(json.decode(content)).decrypt(password);
}

Future<File?> findKeyStore(String name) async {
for (var file in walletPath!.listSync()) {
Future<KeyStoreDefinition?> findKeyStore(String name) async {
for (var file in walletPath.listSync()) {
if (path.basename(file.path) == name) {
if (file is File) {
return file;
return KeyStoreDefinition(file: file);
} else {
throw InvalidKeyStorePath('Given keyStore is not a file ($name)');
}
Expand All @@ -97,26 +103,52 @@ class KeyStoreManager {
return null;
}

Future<List<File>> listAllKeyStores() async {
var keyStoreList = <File>[];
for (var keyStore in walletPath!.listSync()) {
Future<List<KeyStoreDefinition>> listAllKeyStores() async {
var keyStoreList = <KeyStoreDefinition>[];
for (var keyStore in walletPath.listSync()) {
if (keyStore is File) {
keyStoreList.add(keyStore);
keyStoreList.add(KeyStoreDefinition(file: keyStore));
}
}
return keyStoreList;
}

Future<File> createNew(String passphrase, String? name) async {
Future<KeyStoreDefinition> createNew(String passphrase, String? name) async {
var store = await KeyStore.newRandom();
var keyStore = await saveKeyStore(store, passphrase, name: name);
return keyStore;
}

Future<File> createFromMnemonic(
Future<KeyStoreDefinition> createFromMnemonic(
String mnemonic, String passphrase, String? name) async {
var store = KeyStore.fromMnemonic(mnemonic);
var keyStore = await saveKeyStore(store, passphrase, name: name);
return keyStore;
return await saveKeyStore(store, passphrase, name: name);
}

Future<Iterable<WalletDefinition>> getWalletDefinitions() async {
return listAllKeyStores();
}

Future<Wallet> getWallet(WalletDefinition walletDefinition,
[WalletOptions? walletOptions]) async {
if (!(walletDefinition is KeyStoreDefinition)) {
throw Exception(
"Unsupported wallet definition ${walletDefinition.runtimeType}.");
}
if (!(walletOptions is KeyStoreOptions)) {
throw Exception(
"Unsupported wallet options ${walletOptions.runtimeType}.");
}
return await readKeyStore(
walletOptions.decryptionPassword, walletDefinition.file);
}

Future<bool> supportsWallet(WalletDefinition walletDefinition) async {
if (walletDefinition is KeyStoreDefinition) {
for (var definition in await getWalletDefinitions()) {
if (definition.walletId == walletDefinition.walletId) return true;
}
}
return false;
}
}
1 change: 1 addition & 0 deletions lib/src/wallet/wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export 'keypair.dart';
export 'keystore.dart';
export 'manager.dart';
export 'mnemonic.dart';
export 'interfaces.dart';
Loading

0 comments on commit 5967f38

Please sign in to comment.