diff --git a/lib/src/global.dart b/lib/src/global.dart index 6e4d7a7..0e4fbcc 100644 --- a/lib/src/global.dart +++ b/lib/src/global.dart @@ -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 { diff --git a/lib/src/utils/block.dart b/lib/src/utils/block.dart index 78ff0ad..a313b3b 100644 --- a/lib/src/utils/block.dart +++ b/lib/src/utils/block.dart @@ -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 { @@ -25,6 +25,10 @@ class BlockUtils { } static Hash getTransactionHash(AccountBlockTemplate transaction) { + return Hash.digest(getTransactionBytes(transaction)); + } + + static List getTransactionBytes(AccountBlockTemplate transaction) { var versionBytes = BytesUtils.longToBytes(transaction.version); var chainIdentifierBytes = BytesUtils.longToBytes(transaction.chainIdentifier); @@ -62,12 +66,7 @@ class BlockUtils { nonceBytes ]); - return Hash.digest(source); - } - - static Future> _getTransactionSignature( - KeyPair keyPair, AccountBlockTemplate transaction) { - return keyPair.sign(transaction.hash.getBytes()!); + return source; } static Hash _getPoWData(AccountBlockTemplate transaction) { @@ -98,11 +97,11 @@ class BlockUtils { } static Future _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); @@ -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; @@ -163,16 +162,14 @@ class BlockUtils { } static Future _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 send( - AccountBlockTemplate transaction, KeyPair currentKeyPair, + AccountBlockTemplate transaction, WalletAccount currentKeyPair, {void Function(PowStatus)? generatingPowCallback, waitForRequiredPlasma = false}) async { var z = Zenon(); @@ -189,10 +186,10 @@ class BlockUtils { } static Future 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, diff --git a/lib/src/wallet/interfaces.dart b/lib/src/wallet/interfaces.dart new file mode 100644 index 0000000..8dbe53b --- /dev/null +++ b/lib/src/wallet/interfaces.dart @@ -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> getWalletDefinitions(); + + // Gets a wallet by wallet definition. + Future 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 supportsWallet(WalletDefinition walletDefinition); +} + +// Represents a wallet. +abstract class Wallet { + // Gets a wallet account by index. + Future 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> getPublicKey(); + + // Gets the address of the wallet account. + Future
getAddress(); + + // Signs an arbitrary message and returns the signature as an array of bytes. + Future> sign(List message); + + // Signs a transaction and returns the signature as an array of bytes. + Future> signTx(AccountBlockTemplate tx); +} diff --git a/lib/src/wallet/keypair.dart b/lib/src/wallet/keypair.dart index 33d95a0..a3fb68b 100644 --- a/lib/src/wallet/keypair.dart +++ b/lib/src/wallet/keypair.dart @@ -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? privateKey; List? publicKey; Address? _address; @@ -24,17 +26,25 @@ class KeyPair { } Future get address async { + return await getAddress(); + } + + Future
getAddress() async { if (_address == null) { publicKey = await getPublicKey(); _address = Address.fromPublicKey(publicKey!); } - return _address; + return _address!; } Future> sign(List message) async { return Crypto.sign(message, privateKey, (await getPublicKey())); } + Future> signTx(AccountBlockTemplate tx) async { + return Crypto.sign(tx.hash.getBytes()!, privateKey, (await getPublicKey())); + } + Future verify(List signature, List message) async { return Crypto.verify(signature, message, (await getPublicKey())); } diff --git a/lib/src/wallet/keystore.dart b/lib/src/wallet/keystore.dart index 80a5ada..209d177 100644 --- a/lib/src/wallet/keystore.dart +++ b/lib/src/wallet/keystore.dart @@ -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; @@ -48,6 +69,10 @@ class KeyStore { setMnemonic(bip39.entropyToMnemonic(entropy)); } + Future getAccount([int index = 0]) async { + return getKeyPair(index); + } + KeyPair getKeyPair([int index = 0]) { return KeyPair( Crypto.deriveKey(Derivation.getDerivationAccount(index), seed!)); diff --git a/lib/src/wallet/manager.dart b/lib/src/wallet/manager.dart index 9cba0f2..98bff68 100644 --- a/lib/src/wallet/manager.dart +++ b/lib/src/wallet/manager.dart @@ -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; @@ -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 saveKeyStore(KeyStore store, String password, + Future saveKeyStore(KeyStore store, String password, {String? name}) async { name ??= (await store.getKeyPair(0).address).toString(); @@ -41,15 +47,15 @@ class KeyStoreManager { onError: port.sendPort, onExit: port.sendPort); StreamSubscription? sub; - var completer = Completer(); + var completer = Completer(); 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; } @@ -84,11 +90,11 @@ class KeyStoreManager { return KeyFile.fromJson(json.decode(content)).decrypt(password); } - Future findKeyStore(String name) async { - for (var file in walletPath!.listSync()) { + Future 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)'); } @@ -97,26 +103,52 @@ class KeyStoreManager { return null; } - Future> listAllKeyStores() async { - var keyStoreList = []; - for (var keyStore in walletPath!.listSync()) { + Future> listAllKeyStores() async { + var keyStoreList = []; + for (var keyStore in walletPath.listSync()) { if (keyStore is File) { - keyStoreList.add(keyStore); + keyStoreList.add(KeyStoreDefinition(file: keyStore)); } } return keyStoreList; } - Future createNew(String passphrase, String? name) async { + Future createNew(String passphrase, String? name) async { var store = await KeyStore.newRandom(); var keyStore = await saveKeyStore(store, passphrase, name: name); return keyStore; } - Future createFromMnemonic( + Future 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> getWalletDefinitions() async { + return listAllKeyStores(); + } + + Future 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 supportsWallet(WalletDefinition walletDefinition) async { + if (walletDefinition is KeyStoreDefinition) { + for (var definition in await getWalletDefinitions()) { + if (definition.walletId == walletDefinition.walletId) return true; + } + } + return false; } } diff --git a/lib/src/wallet/wallet.dart b/lib/src/wallet/wallet.dart index 13c89e8..ac79745 100644 --- a/lib/src/wallet/wallet.dart +++ b/lib/src/wallet/wallet.dart @@ -5,3 +5,4 @@ export 'keypair.dart'; export 'keystore.dart'; export 'manager.dart'; export 'mnemonic.dart'; +export 'interfaces.dart'; \ No newline at end of file diff --git a/lib/src/zenon.dart b/lib/src/zenon.dart index 5f157e5..c5501c8 100644 --- a/lib/src/zenon.dart +++ b/lib/src/zenon.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:znn_sdk_dart/src/api/api.dart' as api; import 'package:znn_sdk_dart/src/client/client.dart'; @@ -9,17 +8,17 @@ 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/wallet.dart'; -var noKeyPairSelectedException = ZnnSdkException('No default keyPair selected'); +var noKeyPairSelectedException = ZnnSdkException('No default wallet account selected'); class Zenon { static final Zenon _singleton = Zenon._internal(); - KeyPair? defaultKeyPair; - KeyStore? defaultKeyStore; - File? defaultKeyStorePath; + WalletAccount? defaultKeyPair; + Wallet? defaultKeyStore; + WalletDefinition? defaultKeyStorePath; late WsClient wsClient; - late KeyStoreManager keyStoreManager; + late WalletManager keyStoreManager; late api.LedgerApi ledger; late api.StatsApi stats; @@ -46,13 +45,13 @@ class Zenon { } Future send(AccountBlockTemplate transaction, - {KeyPair? currentKeyPair, void Function(PowStatus)? generatingPowCallback, waitForRequiredPlasma = false}) async { + {WalletAccount? currentKeyPair, void Function(PowStatus)? generatingPowCallback, waitForRequiredPlasma = false}) async { currentKeyPair ??= defaultKeyPair; if (currentKeyPair == null) throw noKeyPairSelectedException; return BlockUtils.send(transaction, currentKeyPair, generatingPowCallback: generatingPowCallback, waitForRequiredPlasma: waitForRequiredPlasma); } - Future requiresPoW(AccountBlockTemplate transaction, {KeyPair? blockSigningKey}) async { + Future requiresPoW(AccountBlockTemplate transaction, {WalletAccount? blockSigningKey}) async { blockSigningKey ??= defaultKeyPair; return BlockUtils.requiresPoW(transaction, blockSigningKey: blockSigningKey); } diff --git a/pubspec.yaml b/pubspec.yaml index 63190f2..221a629 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: znn_sdk_dart description: Zenon SDK for Dart and Flutter -version: 0.0.5 +version: 0.0.6 environment: sdk: '>=2.14.0 <3.0.0'