diff --git a/lib/wallets/crypto_currency/coins/tezos.dart b/lib/wallets/crypto_currency/coins/tezos.dart index 587cba407..52c49deac 100644 --- a/lib/wallets/crypto_currency/coins/tezos.dart +++ b/lib/wallets/crypto_currency/coins/tezos.dart @@ -1,3 +1,4 @@ +import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/models/node_model.dart'; import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -14,6 +15,16 @@ class Tezos extends Bip39Currency { } } + DerivationPath get standardDerivationPath => + DerivationPath()..value = "m/44'/1729'/0'/0'"; + + List get possibleDerivationPaths => [ + standardDerivationPath, + DerivationPath()..value = "", + DerivationPath()..value = "m/44'/1729'/0'/0'/0'", + DerivationPath()..value = "m/44'/1729'/0'/0/0", + ]; + @override String get genesisHash => throw UnimplementedError( "Not used in tezos at the moment", diff --git a/lib/wallets/wallet/supporting/tezos_utils.dart b/lib/wallets/wallet/supporting/tezos_utils.dart new file mode 100644 index 000000000..b52882507 --- /dev/null +++ b/lib/wallets/wallet/supporting/tezos_utils.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:bip39/bip39.dart' as bip39; +import 'package:coinlib_flutter/coinlib_flutter.dart'; +import 'package:tezart/src/crypto/crypto.dart'; +import 'package:tezart/tezart.dart'; + +class _Node { + final Uint8List privateKey; + final Uint8List chainCode; + + _Node(this.privateKey, this.chainCode); +} + +_Node _deriveRootNode(Uint8List seed) { + return _deriveNode(seed, Uint8List.fromList(utf8.encode("ed25519 seed"))); +} + +_Node _deriveNode(Uint8List msg, Uint8List key) { + final hMac = hmacSha512(key, msg); + final privateKey = hMac.sublist(0, 32); + final chainCode = hMac.sublist(32); + return _Node(privateKey, chainCode); +} + +_Node _deriveChildNode(_Node node, int index) { + Uint8List indexBuf = Uint8List(4); + ByteData.view(indexBuf.buffer).setUint32(0, index, Endian.big); + + Uint8List message = Uint8List.fromList([ + Uint8List(1)[0], + ...node.privateKey, + ...indexBuf, + ]); + + return _deriveNode(message, Uint8List.fromList(node.chainCode)); +} + +List _derivationPathToArray(String derivationPath) { + if (derivationPath.isEmpty) { + return []; + } + + derivationPath = derivationPath.replaceAll('m/', '').replaceAll("'", 'h'); + + return derivationPath.split('/').map((level) { + if (level.endsWith("h")) { + level = level.substring(0, level.length - 1); + } + final int levelNumber = int.parse(level); + if (levelNumber >= 0x80000000) { + throw ArgumentError('Invalid derivation path. Out of bound.'); + } + return levelNumber + 0x80000000; + }).toList(); +} + +Keystore mnemonicToKeyStore({ + required String mnemonic, + String mnemonicPassphrase = "", + String derivationPath = "", +}) { + if (derivationPath.isEmpty) { + return Keystore.fromMnemonic(mnemonic, password: mnemonicPassphrase); + } + + final pathArray = _derivationPathToArray(derivationPath); + final seed = bip39.mnemonicToSeed(mnemonic, passphrase: mnemonicPassphrase); + _Node node = _deriveRootNode(seed); + for (final index in pathArray) { + node = _deriveChildNode(node, index); + } + + final encoded = encodeWithPrefix( + prefix: Prefixes.edsk2, + bytes: node.privateKey, + ); + + return Keystore.fromSeed(encoded); +}