mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
feat: zano hf + bip39 (#3359)
* feat: bip39 zano hf * fix: correct monero_c hash * lockfile [skip ci] * fix: zano: proper bip32 fix: zano respect 24 word setting fix: rename / delete * fix: nonBip39WalletTypes * fix: zano wallet rename * fix: passphrase store zano --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
@@ -26,7 +26,7 @@ dependencies:
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
ref: cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
ledger_flutter_plus: ^1.4.1
|
||||
|
||||
@@ -24,7 +24,7 @@ dependencies:
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
ref: cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
|
||||
|
||||
@@ -66,8 +66,13 @@ class Transfer {
|
||||
? []
|
||||
: (json['remote_aliases'] as List<dynamic>).cast<String>(),
|
||||
showSender: json['show_sender'] as bool? ?? false,
|
||||
subtransfers: (json['subtransfers'] as List<dynamic>? ?? [])
|
||||
.map((e) => Subtransfer.fromJson(e as Map<String, dynamic>))
|
||||
subtransfers: (json['subtransfers_by_pid'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.expand(
|
||||
(group) => (group['subtransfers'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(Subtransfer.fromJson),
|
||||
)
|
||||
.toList(),
|
||||
timestamp: json['timestamp'] as int? ?? 0,
|
||||
transferInternalIndex: json['transfer_internal_index'] == null
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bip32/bip32.dart' as bip32;
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:bip39/src/wordlists/english.dart' as bip39_english;
|
||||
|
||||
bool isBip39Seed(String mnemonic) => bip39.validateMnemonic(mnemonic);
|
||||
|
||||
List<String> get bip39EnglishWords => List<String>.from(bip39_english.WORDLIST);
|
||||
|
||||
String generateBip39Mnemonic({int strength = 128}) =>
|
||||
bip39.generateMnemonic(strength: strength);
|
||||
|
||||
String getSecretDerivationFromBip39(String mnemonic, {String passphrase = ''}) {
|
||||
final seed = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase);
|
||||
|
||||
final bip32KeyPair = bip32.BIP32.fromSeed(seed).derivePath("m/44'/128'/0'/0/0");
|
||||
|
||||
return _reduceECKey(bip32KeyPair.privateKey!).toHexString();
|
||||
}
|
||||
|
||||
const _ed25519CurveOrder = "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED";
|
||||
|
||||
Uint8List _reduceECKey(Uint8List buffer) {
|
||||
final curveOrder = BigInt.parse(_ed25519CurveOrder, radix: 16);
|
||||
final bigNumber = _readBytes(buffer);
|
||||
|
||||
var result = bigNumber % curveOrder;
|
||||
|
||||
final resultBuffer = Uint8List(32);
|
||||
for (var i = 0; i < 32; i++) {
|
||||
resultBuffer[i] = (result & BigInt.from(0xff)).toInt();
|
||||
result = result >> 8;
|
||||
}
|
||||
|
||||
return resultBuffer;
|
||||
}
|
||||
|
||||
/// Read BigInt from a little-endian Uint8List
|
||||
/// From https://github.com/dart-lang/sdk/issues/32803#issuecomment-387405784
|
||||
BigInt _readBytes(Uint8List bytes) {
|
||||
BigInt read(int start, int end) {
|
||||
if (end - start <= 4) {
|
||||
var result = 0;
|
||||
for (int i = end - 1; i >= start; i--) {
|
||||
result = result * 256 + bytes[i];
|
||||
}
|
||||
return BigInt.from(result);
|
||||
}
|
||||
final mid = start + ((end - start) >> 1);
|
||||
return read(start, mid) + read(mid, end) * (BigInt.one << ((mid - start) * 8));
|
||||
}
|
||||
|
||||
return read(0, bytes.length);
|
||||
}
|
||||
|
||||
extension on Uint8List {
|
||||
String toHexString() =>
|
||||
map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
NODE=http://37.27.100.59:10500/json_rpc
|
||||
HEIGHT=$(curl -s -X POST $NODE -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['height'])")
|
||||
echo "_Checkpoint($(curl -s -X POST $NODE -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"0","method":"getblockheaderbyheight","params":{"height":1}}' | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['block_header']['timestamp'])"), 1),"
|
||||
for i in $(seq 1 $((HEIGHT/10000))); do echo "_Checkpoint($(curl -s -X POST $NODE -H 'Content-Type: application/json' -d "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getblockheaderbyheight\",\"params\":{\"height\":${i}0000}}" | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['block_header']['timestamp'])"), ${i}0000),"; done
|
||||
@@ -0,0 +1,467 @@
|
||||
class ZanoHeight {
|
||||
static final List<_Checkpoint> _points = [
|
||||
_Checkpoint(1557342384, 1),
|
||||
_Checkpoint(1557906733, 10000),
|
||||
_Checkpoint(1558516097, 20000),
|
||||
_Checkpoint(1559119125, 30000),
|
||||
_Checkpoint(1559741805, 40000),
|
||||
_Checkpoint(1560341175, 50000),
|
||||
_Checkpoint(1560948480, 60000),
|
||||
_Checkpoint(1561555965, 70000),
|
||||
_Checkpoint(1562142135, 80000),
|
||||
_Checkpoint(1562749400, 90000),
|
||||
_Checkpoint(1563354315, 100000),
|
||||
_Checkpoint(1563958673, 110000),
|
||||
_Checkpoint(1564559835, 120000),
|
||||
_Checkpoint(1565159325, 130000),
|
||||
_Checkpoint(1565772195, 140000),
|
||||
_Checkpoint(1566389235, 150000),
|
||||
_Checkpoint(1566992793, 160000),
|
||||
_Checkpoint(1567597678, 170000),
|
||||
_Checkpoint(1568204955, 180000),
|
||||
_Checkpoint(1568812530, 190000),
|
||||
_Checkpoint(1569418329, 200000),
|
||||
_Checkpoint(1570035660, 210000),
|
||||
_Checkpoint(1570641964, 220000),
|
||||
_Checkpoint(1571242140, 230000),
|
||||
_Checkpoint(1571841282, 240000),
|
||||
_Checkpoint(1572443911, 250000),
|
||||
_Checkpoint(1573049280, 260000),
|
||||
_Checkpoint(1573653255, 270000),
|
||||
_Checkpoint(1574251680, 280000),
|
||||
_Checkpoint(1574854984, 290000),
|
||||
_Checkpoint(1575459060, 300000),
|
||||
_Checkpoint(1576064640, 310000),
|
||||
_Checkpoint(1576665225, 320000),
|
||||
_Checkpoint(1577271045, 330000),
|
||||
_Checkpoint(1577867835, 340000),
|
||||
_Checkpoint(1578470370, 350000),
|
||||
_Checkpoint(1579078170, 360000),
|
||||
_Checkpoint(1579674765, 370000),
|
||||
_Checkpoint(1580277860, 380000),
|
||||
_Checkpoint(1580878155, 390000),
|
||||
_Checkpoint(1581482220, 400000),
|
||||
_Checkpoint(1582087577, 410000),
|
||||
_Checkpoint(1582689270, 420000),
|
||||
_Checkpoint(1583284005, 430000),
|
||||
_Checkpoint(1583880062, 440000),
|
||||
_Checkpoint(1584480692, 450000),
|
||||
_Checkpoint(1585085520, 460000),
|
||||
_Checkpoint(1585694261, 470000),
|
||||
_Checkpoint(1586292210, 480000),
|
||||
_Checkpoint(1586893822, 490000),
|
||||
_Checkpoint(1587498656, 500000),
|
||||
_Checkpoint(1588099364, 510000),
|
||||
_Checkpoint(1588696710, 520000),
|
||||
_Checkpoint(1589300843, 530000),
|
||||
_Checkpoint(1589904930, 540000),
|
||||
_Checkpoint(1590499185, 550000),
|
||||
_Checkpoint(1591111133, 560000),
|
||||
_Checkpoint(1591715587, 570000),
|
||||
_Checkpoint(1592316375, 580000),
|
||||
_Checkpoint(1592918012, 590000),
|
||||
_Checkpoint(1593521050, 600000),
|
||||
_Checkpoint(1594123886, 610000),
|
||||
_Checkpoint(1594725660, 620000),
|
||||
_Checkpoint(1595326710, 630000),
|
||||
_Checkpoint(1595929088, 640000),
|
||||
_Checkpoint(1596535020, 650000),
|
||||
_Checkpoint(1597132395, 660000),
|
||||
_Checkpoint(1597740870, 670000),
|
||||
_Checkpoint(1598339827, 680000),
|
||||
_Checkpoint(1598945010, 690000),
|
||||
_Checkpoint(1599547530, 700000),
|
||||
_Checkpoint(1600153050, 710000),
|
||||
_Checkpoint(1600754010, 720000),
|
||||
_Checkpoint(1601355810, 730000),
|
||||
_Checkpoint(1601956635, 740000),
|
||||
_Checkpoint(1602558615, 750000),
|
||||
_Checkpoint(1603155648, 760000),
|
||||
_Checkpoint(1603759399, 770000),
|
||||
_Checkpoint(1604365586, 780000),
|
||||
_Checkpoint(1604966602, 790000),
|
||||
_Checkpoint(1605564777, 800000),
|
||||
_Checkpoint(1606168023, 810000),
|
||||
_Checkpoint(1606772409, 820000),
|
||||
_Checkpoint(1607377110, 830000),
|
||||
_Checkpoint(1607988084, 840000),
|
||||
_Checkpoint(1608593385, 850000),
|
||||
_Checkpoint(1609196310, 860000),
|
||||
_Checkpoint(1609793426, 870000),
|
||||
_Checkpoint(1610398975, 880000),
|
||||
_Checkpoint(1610999055, 890000),
|
||||
_Checkpoint(1611601290, 900000),
|
||||
_Checkpoint(1612208050, 910000),
|
||||
_Checkpoint(1612805250, 920000),
|
||||
_Checkpoint(1613409645, 930000),
|
||||
_Checkpoint(1614003255, 940000),
|
||||
_Checkpoint(1614600803, 950000),
|
||||
_Checkpoint(1615202091, 960000),
|
||||
_Checkpoint(1615802123, 970000),
|
||||
_Checkpoint(1616414192, 980000),
|
||||
_Checkpoint(1617015705, 990000),
|
||||
_Checkpoint(1617613785, 1000000),
|
||||
_Checkpoint(1618218467, 1010000),
|
||||
_Checkpoint(1618821464, 1020000),
|
||||
_Checkpoint(1619424052, 1030000),
|
||||
_Checkpoint(1620027510, 1040000),
|
||||
_Checkpoint(1620630585, 1050000),
|
||||
_Checkpoint(1621232355, 1060000),
|
||||
_Checkpoint(1621832220, 1070000),
|
||||
_Checkpoint(1622426032, 1080000),
|
||||
_Checkpoint(1623041250, 1090000),
|
||||
_Checkpoint(1623644732, 1100000),
|
||||
_Checkpoint(1624244801, 1110000),
|
||||
_Checkpoint(1624848450, 1120000),
|
||||
_Checkpoint(1625449290, 1130000),
|
||||
_Checkpoint(1626049974, 1140000),
|
||||
_Checkpoint(1626656033, 1150000),
|
||||
_Checkpoint(1627255830, 1160000),
|
||||
_Checkpoint(1627855505, 1170000),
|
||||
_Checkpoint(1628464065, 1180000),
|
||||
_Checkpoint(1629064455, 1190000),
|
||||
_Checkpoint(1629665715, 1200000),
|
||||
_Checkpoint(1630268475, 1210000),
|
||||
_Checkpoint(1630870911, 1220000),
|
||||
_Checkpoint(1631475471, 1230000),
|
||||
_Checkpoint(1632071441, 1240000),
|
||||
_Checkpoint(1632677576, 1250000),
|
||||
_Checkpoint(1633286445, 1260000),
|
||||
_Checkpoint(1633884645, 1270000),
|
||||
_Checkpoint(1634480355, 1280000),
|
||||
_Checkpoint(1635077039, 1290000),
|
||||
_Checkpoint(1635679740, 1300000),
|
||||
_Checkpoint(1636284345, 1310000),
|
||||
_Checkpoint(1636882865, 1320000),
|
||||
_Checkpoint(1637482276, 1330000),
|
||||
_Checkpoint(1638082296, 1340000),
|
||||
_Checkpoint(1638682601, 1350000),
|
||||
_Checkpoint(1639287225, 1360000),
|
||||
_Checkpoint(1639884465, 1370000),
|
||||
_Checkpoint(1640484903, 1380000),
|
||||
_Checkpoint(1641084033, 1390000),
|
||||
_Checkpoint(1641688500, 1400000),
|
||||
_Checkpoint(1642286685, 1410000),
|
||||
_Checkpoint(1642886842, 1420000),
|
||||
_Checkpoint(1643493960, 1430000),
|
||||
_Checkpoint(1644093045, 1440000),
|
||||
_Checkpoint(1644691833, 1450000),
|
||||
_Checkpoint(1645291245, 1460000),
|
||||
_Checkpoint(1645892190, 1470000),
|
||||
_Checkpoint(1646489895, 1480000),
|
||||
_Checkpoint(1647089648, 1490000),
|
||||
_Checkpoint(1647694665, 1500000),
|
||||
_Checkpoint(1648293585, 1510000),
|
||||
_Checkpoint(1648893525, 1520000),
|
||||
_Checkpoint(1649497306, 1530000),
|
||||
_Checkpoint(1650098656, 1540000),
|
||||
_Checkpoint(1650694980, 1550000),
|
||||
_Checkpoint(1651303650, 1560000),
|
||||
_Checkpoint(1651899285, 1570000),
|
||||
_Checkpoint(1652501596, 1580000),
|
||||
_Checkpoint(1653101220, 1590000),
|
||||
_Checkpoint(1653700166, 1600000),
|
||||
_Checkpoint(1654302094, 1610000),
|
||||
_Checkpoint(1654906575, 1620000),
|
||||
_Checkpoint(1655510644, 1630000),
|
||||
_Checkpoint(1656108615, 1640000),
|
||||
_Checkpoint(1656712545, 1650000),
|
||||
_Checkpoint(1657313460, 1660000),
|
||||
_Checkpoint(1657911515, 1670000),
|
||||
_Checkpoint(1658515717, 1680000),
|
||||
_Checkpoint(1659121230, 1690000),
|
||||
_Checkpoint(1659722213, 1700000),
|
||||
_Checkpoint(1660318875, 1710000),
|
||||
_Checkpoint(1660921522, 1720000),
|
||||
_Checkpoint(1661518436, 1730000),
|
||||
_Checkpoint(1662118050, 1740000),
|
||||
_Checkpoint(1662717982, 1750000),
|
||||
_Checkpoint(1663306425, 1760000),
|
||||
_Checkpoint(1663904520, 1770000),
|
||||
_Checkpoint(1664511195, 1780000),
|
||||
_Checkpoint(1665113460, 1790000),
|
||||
_Checkpoint(1665705148, 1800000),
|
||||
_Checkpoint(1666306807, 1810000),
|
||||
_Checkpoint(1666909050, 1820000),
|
||||
_Checkpoint(1667510577, 1830000),
|
||||
_Checkpoint(1668111697, 1840000),
|
||||
_Checkpoint(1668711150, 1850000),
|
||||
_Checkpoint(1669309785, 1860000),
|
||||
_Checkpoint(1669910715, 1870000),
|
||||
_Checkpoint(1670511885, 1880000),
|
||||
_Checkpoint(1671111596, 1890000),
|
||||
_Checkpoint(1671713348, 1900000),
|
||||
_Checkpoint(1672312933, 1910000),
|
||||
_Checkpoint(1672913610, 1920000),
|
||||
_Checkpoint(1673517614, 1930000),
|
||||
_Checkpoint(1674116655, 1940000),
|
||||
_Checkpoint(1674717105, 1950000),
|
||||
_Checkpoint(1675316550, 1960000),
|
||||
_Checkpoint(1675914240, 1970000),
|
||||
_Checkpoint(1676519608, 1980000),
|
||||
_Checkpoint(1677113500, 1990000),
|
||||
_Checkpoint(1677715095, 2000000),
|
||||
_Checkpoint(1678316835, 2010000),
|
||||
_Checkpoint(1678912224, 2020000),
|
||||
_Checkpoint(1679513175, 2030000),
|
||||
_Checkpoint(1680112070, 2040000),
|
||||
_Checkpoint(1680711218, 2050000),
|
||||
_Checkpoint(1681313478, 2060000),
|
||||
_Checkpoint(1681909155, 2070000),
|
||||
_Checkpoint(1682507671, 2080000),
|
||||
_Checkpoint(1683107382, 2090000),
|
||||
_Checkpoint(1683712020, 2100000),
|
||||
_Checkpoint(1684311975, 2110000),
|
||||
_Checkpoint(1684915377, 2120000),
|
||||
_Checkpoint(1685514352, 2130000),
|
||||
_Checkpoint(1686112770, 2140000),
|
||||
_Checkpoint(1686710430, 2150000),
|
||||
_Checkpoint(1687309584, 2160000),
|
||||
_Checkpoint(1687916053, 2170000),
|
||||
_Checkpoint(1688521450, 2180000),
|
||||
_Checkpoint(1689118965, 2190000),
|
||||
_Checkpoint(1689719584, 2200000),
|
||||
_Checkpoint(1690321515, 2210000),
|
||||
_Checkpoint(1690920915, 2220000),
|
||||
_Checkpoint(1691524920, 2230000),
|
||||
_Checkpoint(1692120135, 2240000),
|
||||
_Checkpoint(1692715440, 2250000),
|
||||
_Checkpoint(1693313578, 2260000),
|
||||
_Checkpoint(1693915410, 2270000),
|
||||
_Checkpoint(1694516824, 2280000),
|
||||
_Checkpoint(1695114075, 2290000),
|
||||
_Checkpoint(1695712485, 2300000),
|
||||
_Checkpoint(1696314266, 2310000),
|
||||
_Checkpoint(1696913190, 2320000),
|
||||
_Checkpoint(1697513447, 2330000),
|
||||
_Checkpoint(1698115590, 2340000),
|
||||
_Checkpoint(1698715860, 2350000),
|
||||
_Checkpoint(1699318665, 2360000),
|
||||
_Checkpoint(1699918515, 2370000),
|
||||
_Checkpoint(1700510340, 2380000),
|
||||
_Checkpoint(1701107490, 2390000),
|
||||
_Checkpoint(1701715953, 2400000),
|
||||
_Checkpoint(1702319655, 2410000),
|
||||
_Checkpoint(1702921425, 2420000),
|
||||
_Checkpoint(1703518740, 2430000),
|
||||
_Checkpoint(1704117450, 2440000),
|
||||
_Checkpoint(1704715350, 2450000),
|
||||
_Checkpoint(1705314150, 2460000),
|
||||
_Checkpoint(1705911585, 2470000),
|
||||
_Checkpoint(1706513190, 2480000),
|
||||
_Checkpoint(1707111871, 2490000),
|
||||
_Checkpoint(1707712515, 2500000),
|
||||
_Checkpoint(1708316785, 2510000),
|
||||
_Checkpoint(1708917275, 2520000),
|
||||
_Checkpoint(1709515140, 2530000),
|
||||
_Checkpoint(1710115320, 2540000),
|
||||
_Checkpoint(1710718811, 2550000),
|
||||
_Checkpoint(1711340233, 2560000),
|
||||
_Checkpoint(1711937827, 2570000),
|
||||
_Checkpoint(1712536797, 2580000),
|
||||
_Checkpoint(1713132840, 2590000),
|
||||
_Checkpoint(1713732780, 2600000),
|
||||
_Checkpoint(1714330320, 2610000),
|
||||
_Checkpoint(1714933845, 2620000),
|
||||
_Checkpoint(1715536185, 2630000),
|
||||
_Checkpoint(1716146082, 2640000),
|
||||
_Checkpoint(1716742155, 2650000),
|
||||
_Checkpoint(1717343205, 2660000),
|
||||
_Checkpoint(1717937699, 2670000),
|
||||
_Checkpoint(1718535495, 2680000),
|
||||
_Checkpoint(1719141615, 2690000),
|
||||
_Checkpoint(1719744075, 2700000),
|
||||
_Checkpoint(1720336724, 2710000),
|
||||
_Checkpoint(1720941803, 2720000),
|
||||
_Checkpoint(1721539733, 2730000),
|
||||
_Checkpoint(1722139174, 2740000),
|
||||
_Checkpoint(1722737769, 2750000),
|
||||
_Checkpoint(1723336755, 2760000),
|
||||
_Checkpoint(1723937074, 2770000),
|
||||
_Checkpoint(1724533860, 2780000),
|
||||
_Checkpoint(1725126437, 2790000),
|
||||
_Checkpoint(1725728340, 2800000),
|
||||
_Checkpoint(1726330290, 2810000),
|
||||
_Checkpoint(1726931550, 2820000),
|
||||
_Checkpoint(1727531335, 2830000),
|
||||
_Checkpoint(1728132893, 2840000),
|
||||
_Checkpoint(1728731641, 2850000),
|
||||
_Checkpoint(1729328707, 2860000),
|
||||
_Checkpoint(1729927125, 2870000),
|
||||
_Checkpoint(1730525159, 2880000),
|
||||
_Checkpoint(1731124123, 2890000),
|
||||
_Checkpoint(1731728397, 2900000),
|
||||
_Checkpoint(1732328405, 2910000),
|
||||
_Checkpoint(1732929444, 2920000),
|
||||
_Checkpoint(1733527918, 2930000),
|
||||
_Checkpoint(1734125775, 2940000),
|
||||
_Checkpoint(1734721755, 2950000),
|
||||
_Checkpoint(1735326599, 2960000),
|
||||
_Checkpoint(1735919505, 2970000),
|
||||
_Checkpoint(1736520114, 2980000),
|
||||
_Checkpoint(1737119579, 2990000),
|
||||
_Checkpoint(1737724515, 3000000),
|
||||
_Checkpoint(1738328220, 3010000),
|
||||
_Checkpoint(1738929119, 3020000),
|
||||
_Checkpoint(1739527564, 3030000),
|
||||
_Checkpoint(1740125219, 3040000),
|
||||
_Checkpoint(1740726039, 3050000),
|
||||
_Checkpoint(1741327110, 3060000),
|
||||
_Checkpoint(1741927857, 3070000),
|
||||
_Checkpoint(1742530920, 3080000),
|
||||
_Checkpoint(1743149987, 3090000),
|
||||
_Checkpoint(1743747268, 3100000),
|
||||
_Checkpoint(1744349415, 3110000),
|
||||
_Checkpoint(1744944112, 3120000),
|
||||
_Checkpoint(1745543580, 3130000),
|
||||
_Checkpoint(1746142732, 3140000),
|
||||
_Checkpoint(1746742705, 3150000),
|
||||
_Checkpoint(1747344191, 3160000),
|
||||
_Checkpoint(1747946610, 3170000),
|
||||
_Checkpoint(1748545387, 3180000),
|
||||
_Checkpoint(1749144165, 3190000),
|
||||
_Checkpoint(1749742950, 3200000),
|
||||
_Checkpoint(1750343363, 3210000),
|
||||
_Checkpoint(1750946445, 3220000),
|
||||
_Checkpoint(1751545747, 3230000),
|
||||
_Checkpoint(1752144620, 3240000),
|
||||
_Checkpoint(1752746280, 3250000),
|
||||
_Checkpoint(1753343664, 3260000),
|
||||
_Checkpoint(1753948349, 3270000),
|
||||
_Checkpoint(1754547975, 3280000),
|
||||
_Checkpoint(1755145610, 3290000),
|
||||
_Checkpoint(1755744210, 3300000),
|
||||
_Checkpoint(1756341045, 3310000),
|
||||
_Checkpoint(1756940460, 3320000),
|
||||
_Checkpoint(1757537565, 3330000),
|
||||
_Checkpoint(1758141330, 3340000),
|
||||
_Checkpoint(1758742563, 3350000),
|
||||
_Checkpoint(1759340190, 3360000),
|
||||
_Checkpoint(1759941840, 3370000),
|
||||
_Checkpoint(1760542420, 3380000),
|
||||
_Checkpoint(1761144358, 3390000),
|
||||
_Checkpoint(1761745435, 3400000),
|
||||
_Checkpoint(1762345095, 3410000),
|
||||
_Checkpoint(1762942865, 3420000),
|
||||
_Checkpoint(1763540460, 3430000),
|
||||
_Checkpoint(1764144321, 3440000),
|
||||
_Checkpoint(1764745065, 3450000),
|
||||
_Checkpoint(1765347169, 3460000),
|
||||
_Checkpoint(1765944370, 3470000),
|
||||
_Checkpoint(1766548020, 3480000),
|
||||
_Checkpoint(1767144555, 3490000),
|
||||
_Checkpoint(1767748830, 3500000),
|
||||
_Checkpoint(1768350722, 3510000),
|
||||
_Checkpoint(1768945425, 3520000),
|
||||
_Checkpoint(1769545406, 3530000),
|
||||
_Checkpoint(1770144700, 3540000),
|
||||
_Checkpoint(1770745380, 3550000),
|
||||
_Checkpoint(1771345176, 3560000),
|
||||
_Checkpoint(1771943526, 3570000),
|
||||
_Checkpoint(1772545646, 3580000),
|
||||
_Checkpoint(1773146830, 3590000),
|
||||
_Checkpoint(1773749868, 3600000),
|
||||
_Checkpoint(1774347077, 3610000),
|
||||
_Checkpoint(1774950120, 3620000),
|
||||
_Checkpoint(1775548635, 3630000),
|
||||
_Checkpoint(1776148320, 3640000),
|
||||
_Checkpoint(1776750266, 3650000),
|
||||
_Checkpoint(1777351110, 3660000),
|
||||
_Checkpoint(1777953826, 3670000),
|
||||
_Checkpoint(1778551170, 3680000),
|
||||
_Checkpoint(1779153960, 3690000),
|
||||
_Checkpoint(1779758055, 3700000),
|
||||
_Checkpoint(1780362465, 3710000),
|
||||
_Checkpoint(1780966500, 3720000),
|
||||
_Checkpoint(1781563275, 3730000),
|
||||
_Checkpoint(1782174166, 3740000),
|
||||
_Checkpoint(1782775815, 3750000),
|
||||
_Checkpoint(1783378725, 3760000),
|
||||
_Checkpoint(1783971435, 3770000),
|
||||
_Checkpoint(1784575305, 3780000),
|
||||
_Checkpoint(1785177870, 3790000),
|
||||
_Checkpoint(1785774750, 3800000),
|
||||
_Checkpoint(1786377455, 3810000),
|
||||
_Checkpoint(1786978902, 3820000),
|
||||
];
|
||||
|
||||
static int getBlockHeightByTime(DateTime time) {
|
||||
final t = time.toUtc().millisecondsSinceEpoch;
|
||||
|
||||
if (_points.isEmpty) return 0;
|
||||
|
||||
if (t <= _points.first.timeMs) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _points.length - 1; i++) {
|
||||
final p0 = _points[i];
|
||||
final p1 = _points[i + 1];
|
||||
|
||||
if (t >= p0.timeMs && t < p1.timeMs) {
|
||||
return _interpolate(
|
||||
p0.height,
|
||||
p1.height,
|
||||
p0.timeMs,
|
||||
p1.timeMs,
|
||||
t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final last = _points[_points.length - 1];
|
||||
final prev = _points[_points.length - 2];
|
||||
|
||||
final avgBlockTimeMs = (last.timeMs - prev.timeMs) / (last.height - prev.height);
|
||||
|
||||
final blocksSinceLast = ((t - last.timeMs) / avgBlockTimeMs).floor();
|
||||
|
||||
return last.height + blocksSinceLast;
|
||||
}
|
||||
|
||||
static DateTime getTimeByBlockHeight(int height) {
|
||||
if (_points.isEmpty) return DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
|
||||
if (height <= 1) {
|
||||
return DateTime.fromMillisecondsSinceEpoch(_points.first.timeMs, isUtc: true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _points.length - 1; i++) {
|
||||
final p0 = _points[i];
|
||||
final p1 = _points[i + 1];
|
||||
if (height >= p0.height && height < p1.height) {
|
||||
final t = _interpolate(
|
||||
p0.timeMs,
|
||||
p1.timeMs,
|
||||
p0.height,
|
||||
p1.height,
|
||||
height,
|
||||
);
|
||||
return DateTime.fromMillisecondsSinceEpoch(t, isUtc: true);
|
||||
}
|
||||
}
|
||||
|
||||
final last = _points[_points.length - 1];
|
||||
final prev = _points[_points.length - 2];
|
||||
final avgBlockTimeMs = (last.timeMs - prev.timeMs) / (last.height - prev.height);
|
||||
final msSinceLast = ((height - last.height) * avgBlockTimeMs).floor();
|
||||
return DateTime.fromMillisecondsSinceEpoch(last.timeMs + msSinceLast, isUtc: true);
|
||||
}
|
||||
|
||||
static int _interpolate(
|
||||
int h0,
|
||||
int h1,
|
||||
int t0,
|
||||
int t1,
|
||||
int t,
|
||||
) {
|
||||
final ratio = (t - t0) / (t1 - t0);
|
||||
return h0 + ((h1 - h0) * ratio).floor();
|
||||
}
|
||||
}
|
||||
|
||||
class _Checkpoint {
|
||||
final int timeMs;
|
||||
final int height;
|
||||
|
||||
_Checkpoint(int timeSec, this.height) : timeMs = timeSec * 1000;
|
||||
}
|
||||
@@ -1,9 +1,18 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
import 'package:cw_zano/api/model/get_address_info_result.dart';
|
||||
import 'package:cw_zano/get_height_by_date.dart';
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
|
||||
class ZanoUtils {
|
||||
static int heightByDate(DateTime date) =>
|
||||
ZanoHeight.getBlockHeightByTime(date.toUtc().subtract(const Duration(days: 1)));
|
||||
|
||||
static int creationTimestampFromHeight(int height) {
|
||||
if (height <= 0) return 0;
|
||||
return ZanoHeight.getTimeByBlockHeight(height).millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
static bool validateAddress(String address) {
|
||||
try {
|
||||
final result = GetAddressInfoResult.fromJson(
|
||||
|
||||
+274
-44
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
@@ -6,6 +7,7 @@ import 'dart:math';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
@@ -15,6 +17,7 @@ import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_keys_file.dart';
|
||||
import 'package:cw_core/zano_asset.dart';
|
||||
import 'package:cw_zano/api/model/create_wallet_result.dart';
|
||||
import 'package:cw_zano/api/model/destination.dart';
|
||||
@@ -32,7 +35,10 @@ import 'package:cw_zano/zano_wallet_addresses.dart';
|
||||
import 'package:cw_zano/zano_wallet_api.dart';
|
||||
import 'package:cw_zano/zano_wallet_exceptions.dart';
|
||||
import 'package:cw_zano/zano_wallet_service.dart';
|
||||
import 'package:cw_zano/zano_utils.dart';
|
||||
import 'package:cw_zano/api/model/balance.dart';
|
||||
import 'package:cw_zano/bip39_seed.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
@@ -42,10 +48,12 @@ class ZanoWallet = ZanoWalletBase with _$ZanoWallet;
|
||||
|
||||
abstract class ZanoWalletBase
|
||||
extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo>
|
||||
with Store, ZanoWalletApi {
|
||||
with Store, ZanoWalletApi, WalletKeysFile {
|
||||
static const int _autoSaveIntervalSeconds = 30;
|
||||
static const int _pollIntervalMilliseconds = 5000;
|
||||
static const int _maxLoadAssetsRetries = 5;
|
||||
static const int _walletStateReady = 2;
|
||||
static const String _cakeKeysFileSuffix = '.cw.keys';
|
||||
|
||||
@override
|
||||
void setPassword(String password) {
|
||||
@@ -54,10 +62,23 @@ abstract class ZanoWalletBase
|
||||
}
|
||||
|
||||
String _password;
|
||||
final EncryptionFileUtils _encryptionFileUtils;
|
||||
bool _didSyncSecrets = false;
|
||||
|
||||
@override
|
||||
String get password => _password;
|
||||
|
||||
@override
|
||||
Future<String> makeKeysFilePath() async => "${await makePath()}$_cakeKeysFileSuffix";
|
||||
|
||||
@override
|
||||
WalletKeysData get walletKeysData => WalletKeysData(
|
||||
mnemonic: _hasSeed ? seed : null,
|
||||
passphrase: (passphrase ?? '').isEmpty ? null : passphrase,
|
||||
);
|
||||
|
||||
bool get _hasSeed => seed.trim().isNotEmpty;
|
||||
|
||||
@override
|
||||
Future<String> signMessage(String message, {String? address = null}) =>
|
||||
super.signMessage(message, address: address);
|
||||
@@ -105,7 +126,8 @@ abstract class ZanoWalletBase
|
||||
/// number of transactions in each request
|
||||
static final int _txChunkSize = (pow(2, 32) - 1).toInt();
|
||||
|
||||
ZanoWalletBase(WalletInfo walletInfo, DerivationInfo derivationInfo, String password)
|
||||
ZanoWalletBase(WalletInfo walletInfo, DerivationInfo derivationInfo, String password,
|
||||
this._encryptionFileUtils)
|
||||
: balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance.empty(CryptoCurrency.zano)}),
|
||||
_isTransactionUpdating = false,
|
||||
_hasSyncAfterStartup = false,
|
||||
@@ -126,74 +148,138 @@ abstract class ZanoWalletBase
|
||||
@override
|
||||
Future<void> changePassword(String password) async {
|
||||
setPassword(password);
|
||||
if (_hasSeed) {
|
||||
await saveKeysFile(_password, _encryptionFileUtils);
|
||||
saveKeysFile(_password, _encryptionFileUtils, true);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<ZanoWallet> create({required WalletCredentials credentials}) async {
|
||||
static Future<ZanoWallet> create({
|
||||
required WalletCredentials credentials,
|
||||
required EncryptionFileUtils encryptionFileUtils,
|
||||
}) async {
|
||||
final wallet = ZanoWallet(credentials.walletInfo!,
|
||||
await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
|
||||
await credentials.walletInfo!.getDerivationInfo(), credentials.password!, encryptionFileUtils);
|
||||
await wallet.initWallet();
|
||||
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
|
||||
final createWalletResult = await wallet.createWallet(path, credentials.password!);
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
final providedMnemonic =
|
||||
credentials is ZanoNewWalletCredentials ? credentials.mnemonic : null;
|
||||
final mnemonic = providedMnemonic ?? generateBip39Mnemonic(strength: strength);
|
||||
final passphrase = credentials.passphrase ?? '';
|
||||
final secretDerivation = getSecretDerivationFromBip39(mnemonic, passphrase: passphrase);
|
||||
final creationTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
final createWalletResult = await wallet.restoreWalletFromDerivations(
|
||||
path,
|
||||
credentials.password!,
|
||||
secretDerivation,
|
||||
creationTimestamp: creationTimestamp,
|
||||
);
|
||||
await wallet.initWallet();
|
||||
wallet.seed = mnemonic;
|
||||
wallet.passphrase = passphrase;
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
if (credentials.passphrase != null) {
|
||||
await wallet.setPassphrase(credentials.passphrase!);
|
||||
wallet.seed = await createWalletResult.seed(wallet);
|
||||
wallet.passphrase = await wallet.getPassphrase();
|
||||
}
|
||||
await wallet._syncSecretsAndDerivation();
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
static Future<ZanoWallet> restore(
|
||||
{required ZanoRestoreWalletFromSeedCredentials credentials}) async {
|
||||
static Future<ZanoWallet> restore({
|
||||
required ZanoRestoreWalletFromSeedCredentials credentials,
|
||||
required EncryptionFileUtils encryptionFileUtils,
|
||||
}) async {
|
||||
final wallet = ZanoWallet(credentials.walletInfo!,
|
||||
await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
|
||||
await credentials.walletInfo!.getDerivationInfo(), credentials.password!, encryptionFileUtils);
|
||||
await wallet.initWallet();
|
||||
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
|
||||
final createWalletResult = await wallet.restoreWalletFromSeed(
|
||||
path, credentials.password!, credentials.mnemonic, credentials.passphrase);
|
||||
await wallet.initWallet();
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
if (credentials.passphrase != null) {
|
||||
await wallet.setPassphrase(credentials.passphrase!);
|
||||
wallet.seed = await createWalletResult.seed(wallet);
|
||||
wallet.passphrase = await wallet.getPassphrase();
|
||||
final passphrase = credentials.passphrase ?? '';
|
||||
final CreateWalletResult createWalletResult;
|
||||
final isBip39 = isBip39Seed(credentials.mnemonic);
|
||||
final creationTimestamp = ZanoUtils.creationTimestampFromHeight(credentials.height ?? 0);
|
||||
if (isBip39) {
|
||||
final secretDerivation =
|
||||
getSecretDerivationFromBip39(credentials.mnemonic, passphrase: passphrase);
|
||||
createWalletResult = await wallet.restoreWalletFromDerivations(
|
||||
path,
|
||||
credentials.password!,
|
||||
secretDerivation,
|
||||
creationTimestamp: creationTimestamp,
|
||||
);
|
||||
} else {
|
||||
createWalletResult = await wallet.restoreWalletFromSeed(
|
||||
path,
|
||||
credentials.password!,
|
||||
credentials.mnemonic,
|
||||
credentials.passphrase,
|
||||
creationTimestamp: creationTimestamp,
|
||||
);
|
||||
}
|
||||
await wallet.initWallet();
|
||||
wallet.seed = credentials.mnemonic;
|
||||
wallet.passphrase = passphrase;
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
if (!isBip39) {
|
||||
try {
|
||||
if (passphrase.isNotEmpty) {
|
||||
await wallet.setPassphrase(passphrase);
|
||||
}
|
||||
final nativeSeed = await createWalletResult.seed(wallet);
|
||||
if (nativeSeed.isNotEmpty) {
|
||||
wallet.seed = nativeSeed;
|
||||
}
|
||||
} catch (e) {
|
||||
printV('native seed not available yet: $e');
|
||||
}
|
||||
}
|
||||
await wallet._syncSecretsAndDerivation();
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
static Future<ZanoWallet> open(
|
||||
{required String name, required String password, required WalletInfo walletInfo}) async {
|
||||
{required String name,
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required EncryptionFileUtils encryptionFileUtils}) async {
|
||||
final path = await pathForWallet(name: name, type: walletInfo.type);
|
||||
final wallet =
|
||||
ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password, encryptionFileUtils);
|
||||
|
||||
await wallet._loadKeysFileIfPresent();
|
||||
|
||||
late final CreateWalletResult createWalletResult;
|
||||
if (ZanoWalletApi.openWalletCache[path] != null) {
|
||||
final wallet = ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password);
|
||||
await wallet.parseCreateWalletResult(ZanoWalletApi.openWalletCache[path]!).then((_) {
|
||||
unawaited(wallet.init(ZanoWalletApi.openWalletCache[path]!.wi.address));
|
||||
});
|
||||
return wallet;
|
||||
createWalletResult = ZanoWalletApi.openWalletCache[path]!;
|
||||
} else {
|
||||
final wallet = ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password);
|
||||
await wallet.initWallet();
|
||||
final createWalletResult = await wallet.loadWallet(path, password);
|
||||
await wallet.parseCreateWalletResult(createWalletResult).then((_) {
|
||||
unawaited(wallet.init(createWalletResult.wi.address));
|
||||
});
|
||||
return wallet;
|
||||
createWalletResult = await wallet.loadWallet(path, password);
|
||||
}
|
||||
await wallet.parseCreateWalletResult(createWalletResult);
|
||||
if (!wallet._hasSeed) {
|
||||
await wallet._loadSeedIfAvailable();
|
||||
}
|
||||
await wallet._syncSecretsAndDerivation();
|
||||
unawaited(wallet.init(createWalletResult.wi.address));
|
||||
return wallet;
|
||||
}
|
||||
|
||||
Future<void> parseCreateWalletResult(CreateWalletResult result) async {
|
||||
hWallet = result.walletId;
|
||||
seed = await result.seed(this);
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: result.privateSpendKey,
|
||||
privateViewKey: result.privateViewKey,
|
||||
publicSpendKey: result.publicSpendKey,
|
||||
publicViewKey: result.publicViewKey,
|
||||
);
|
||||
passphrase = await getPassphrase();
|
||||
if ((passphrase ?? '').isEmpty) {
|
||||
try {
|
||||
passphrase = await getPassphrase();
|
||||
} catch (e) {
|
||||
printV('passphrase not available yet: $e');
|
||||
}
|
||||
}
|
||||
|
||||
printV('setting hWallet = ${result.walletId}');
|
||||
walletAddresses.address = result.wi.address;
|
||||
@@ -214,6 +300,126 @@ abstract class ZanoWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
bool _isWalletReady(GetWalletStatusResult walletStatus) =>
|
||||
!walletStatus.isInLongRefresh && walletStatus.walletState == _walletStateReady;
|
||||
|
||||
Future<bool> _hasKeysFile() async {
|
||||
try {
|
||||
final path = await makeKeysFilePath();
|
||||
return File(path).existsSync() || File('$path.backup').existsSync();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadKeysFileIfPresent() async {
|
||||
if (!await _hasKeysFile()) {
|
||||
return;
|
||||
}
|
||||
final path = await makeKeysFilePath();
|
||||
Future<WalletKeysData> read(String readPath) async {
|
||||
final jsonSource = await _encryptionFileUtils.read(path: readPath, password: _password);
|
||||
return WalletKeysData.fromJSON(json.decode(jsonSource) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
try {
|
||||
WalletKeysData keysData;
|
||||
if (File(path).existsSync()) {
|
||||
keysData = await read(path);
|
||||
} else {
|
||||
keysData = await read('$path.backup');
|
||||
await saveKeysFile(_password, _encryptionFileUtils);
|
||||
}
|
||||
if (keysData.mnemonic != null && keysData.mnemonic!.trim().isNotEmpty) {
|
||||
seed = keysData.mnemonic!;
|
||||
}
|
||||
if (keysData.passphrase != null && keysData.passphrase!.isNotEmpty) {
|
||||
passphrase = keysData.passphrase;
|
||||
}
|
||||
} catch (e) {
|
||||
printV('error reading zano keys file $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveKeysFileIfNeeded() async {
|
||||
if (!_hasSeed || await _hasKeysFile()) {
|
||||
return;
|
||||
}
|
||||
await saveKeysFile(_password, _encryptionFileUtils);
|
||||
saveKeysFile(_password, _encryptionFileUtils, true);
|
||||
}
|
||||
|
||||
Future<void> _loadSeedIfAvailable() async {
|
||||
if (!isBip39Seed(seed)) {
|
||||
try {
|
||||
final loaded = await getSeed();
|
||||
if (loaded.isNotEmpty) {
|
||||
seed = loaded;
|
||||
}
|
||||
} catch (e) {
|
||||
printV('seed not available yet: $e');
|
||||
}
|
||||
}
|
||||
if ((passphrase ?? '').isEmpty) {
|
||||
try {
|
||||
passphrase = await getPassphrase() ?? passphrase;
|
||||
} catch (e) {
|
||||
printV('passphrase not available yet: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _syncSecretsAndDerivation() async {
|
||||
if (!_hasSeed || _didSyncSecrets) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await _persistSecretsIfNeeded();
|
||||
await _setDerivationForSeed();
|
||||
await _saveKeysFileIfNeeded();
|
||||
_didSyncSecrets = true;
|
||||
} catch (e) {
|
||||
printV('error persisting zano secrets $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _persistSecretsIfNeeded() async {
|
||||
if (isBip39Seed(seed) && await getBip39Mnemonic() == null) {
|
||||
await setBip39Secrets(
|
||||
mnemonic: seed,
|
||||
creationTimestamp: walletInfo.restoreHeight > 0
|
||||
? ZanoUtils.creationTimestampFromHeight(walletInfo.restoreHeight)
|
||||
: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
);
|
||||
}
|
||||
if ((passphrase ?? '').isNotEmpty && await getPassphrase() == null) {
|
||||
await setPassphrase(passphrase!);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setDerivationForSeed() async {
|
||||
if (!_hasSeed) {
|
||||
return;
|
||||
}
|
||||
final di = await walletInfo.getDerivationInfo();
|
||||
if (isBip39Seed(seed)) {
|
||||
if (di.derivationType == DerivationType.bip39 &&
|
||||
(di.derivationPath ?? '').isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
di.derivationType = DerivationType.bip39;
|
||||
di.derivationPath = "m/44'/128'/0'/0/0";
|
||||
await di.save();
|
||||
return;
|
||||
}
|
||||
if (di.derivationType != DerivationType.bip39 && di.derivationPath == 'legacy') {
|
||||
return;
|
||||
}
|
||||
di.derivationType = DerivationType.unknown;
|
||||
di.derivationPath = 'legacy';
|
||||
await di.save();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close({bool shouldCleanup = true}) async {
|
||||
closeWallet(null);
|
||||
@@ -310,12 +516,13 @@ abstract class ZanoWalletBase
|
||||
Future<Map<String, ZanoTransactionInfo>> fetchTransactions() async {
|
||||
try {
|
||||
final transfers = <Transfer>[];
|
||||
var offset = 0;
|
||||
late GetRecentTxsAndInfoResult result;
|
||||
do {
|
||||
result = await getRecentTxsAndInfo(offset: 0, count: _txChunkSize);
|
||||
// _lastTxIndex += result.transfers.length;
|
||||
result = await getRecentTxsAndInfo(offset: offset, count: _txChunkSize);
|
||||
transfers.addAll(result.transfers);
|
||||
} while (result.lastItemIndex + 1 < result.totalTransfers);
|
||||
offset = result.lastItemIndex + 1;
|
||||
} while (offset < result.totalTransfers);
|
||||
return Transfer.makeMap(transfers, zanoAssets, currentDaemonHeight);
|
||||
} catch (e) {
|
||||
printV((e.toString()));
|
||||
@@ -334,12 +541,18 @@ abstract class ZanoWalletBase
|
||||
|
||||
@override
|
||||
Future<void> renameWalletFiles(String newWalletName) async {
|
||||
final currentWalletPath = await pathForWallet(name: name, type: type);
|
||||
final currentWalletDirPath = await pathForWalletDir(name: name, type: type);
|
||||
final currentWalletPath = '$currentWalletDirPath/$name';
|
||||
final currentCacheFile = File(currentWalletPath);
|
||||
final currentKeysFile = File('$currentWalletPath.keys');
|
||||
final currentAddressListFile = File('$currentWalletPath.address.txt');
|
||||
final currentPendingKiFile = File('$currentWalletPath.outkey2ki');
|
||||
final currentSecretsFile = File(p.join(currentWalletDirPath, 'zano-secrets.json.bin'));
|
||||
final currentCakeKeysFile = File('$currentWalletPath$_cakeKeysFileSuffix');
|
||||
final currentCakeKeysBackupFile = File('$currentWalletPath$_cakeKeysFileSuffix.backup');
|
||||
|
||||
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
|
||||
final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type);
|
||||
final newWalletPath = '$newWalletDirPath/$newWalletName';
|
||||
|
||||
// Copies current wallet files into new wallet name's dir and files
|
||||
if (currentCacheFile.existsSync()) {
|
||||
@@ -351,9 +564,22 @@ abstract class ZanoWalletBase
|
||||
if (currentAddressListFile.existsSync()) {
|
||||
await currentAddressListFile.copy('$newWalletPath.address.txt');
|
||||
}
|
||||
if (currentPendingKiFile.existsSync()) {
|
||||
await currentPendingKiFile.copy('$newWalletPath.outkey2ki');
|
||||
}
|
||||
if (currentSecretsFile.existsSync()) {
|
||||
await currentSecretsFile.copy(p.join(newWalletDirPath, 'zano-secrets.json.bin'));
|
||||
}
|
||||
if (currentCakeKeysFile.existsSync()) {
|
||||
await currentCakeKeysFile.copy('$newWalletPath$_cakeKeysFileSuffix');
|
||||
}
|
||||
if (currentCakeKeysBackupFile.existsSync()) {
|
||||
await currentCakeKeysBackupFile.copy('$newWalletPath$_cakeKeysFileSuffix.backup');
|
||||
}
|
||||
|
||||
// Delete old name's dir and files
|
||||
await Directory(currentWalletPath).delete(recursive: true);
|
||||
// Delete the old wallet *directory*. pathForWallet() is the wallet file
|
||||
// (.../<name>/<name>); Directory(filePath).delete leaves seed sidecars.
|
||||
await Directory(currentWalletDirPath).delete(recursive: true);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -362,6 +588,7 @@ abstract class ZanoWalletBase
|
||||
@override
|
||||
Future<void> save() async {
|
||||
try {
|
||||
await _saveKeysFileIfNeeded();
|
||||
await store();
|
||||
await walletAddresses.updateAddressesInBox();
|
||||
} catch (e) {
|
||||
@@ -544,9 +771,12 @@ abstract class ZanoWalletBase
|
||||
_updateSyncProgress(walletStatus);
|
||||
|
||||
// we can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready)
|
||||
if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) {
|
||||
if (_isWalletReady(walletStatus)) {
|
||||
if (!_hasSeed) {
|
||||
await _loadSeedIfAvailable();
|
||||
}
|
||||
await _syncSecretsAndDerivation();
|
||||
final walletInfo = await getWalletInfo();
|
||||
seed = await walletInfo.wiExtended.seed(this);
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: walletInfo.wiExtended.spendPrivateKey,
|
||||
privateViewKey: walletInfo.wiExtended.viewPrivateKey,
|
||||
|
||||
@@ -46,12 +46,13 @@ mixin ZanoWalletApi {
|
||||
|
||||
void setPassword(String password) => zano.PlainWallet_resetWalletPassword(hWallet, password);
|
||||
|
||||
void closeWallet(int? walletToClose, {bool force = false}) async {
|
||||
Future<void> closeWallet(int? walletToClose, {bool force = false}) async {
|
||||
printV('close_wallet ${walletToClose ?? hWallet}: $force');
|
||||
if (Platform.isWindows || force) {
|
||||
final result = await _closeWallet(walletToClose ?? hWallet);
|
||||
final id = walletToClose ?? hWallet;
|
||||
final result = await _closeWallet(id);
|
||||
printV('close_wallet result $result');
|
||||
openWalletCache.removeWhere((_, cwr) => cwr.walletId == (walletToClose ?? hWallet));
|
||||
openWalletCache.removeWhere((_, cwr) => cwr.walletId == id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,31 +77,34 @@ mixin ZanoWalletApi {
|
||||
|
||||
Future<File> _getWalletSecretsFile() async {
|
||||
final dir = await getWalletDir();
|
||||
final file = File(p.join(dir.path, "zano-secrets.json.bin"));
|
||||
return file;
|
||||
return File(p.join(dir.path, 'zano-secrets.json.bin'));
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _getSecrets() async {
|
||||
final file = await _getWalletSecretsFile();
|
||||
if (!file.existsSync()) {
|
||||
try {
|
||||
final file = await _getWalletSecretsFile();
|
||||
if (!file.existsSync()) {
|
||||
return {};
|
||||
}
|
||||
final data = file.readAsBytesSync();
|
||||
final b64 = convert.base64.encode(data);
|
||||
final respStr = await invokeMethod('decrypt_data', {'buff': b64});
|
||||
final resp = convert.json.decode(respStr);
|
||||
final dataBytes = convert.base64.decode(resp['result']['res_buff'] as String);
|
||||
final dataStr = convert.utf8.decode(dataBytes);
|
||||
return convert.json.decode(dataStr) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
printV('error reading zano secrets $e');
|
||||
return {};
|
||||
}
|
||||
final data = file.readAsBytesSync();
|
||||
final b64 = convert.base64.encode(data);
|
||||
final respStr = await invokeMethod("decrypt_data", {"buff": "$b64"});
|
||||
final resp = convert.json.decode(respStr);
|
||||
final dataBytes = convert.base64.decode(resp["result"]["res_buff"] as String);
|
||||
final dataStr = convert.utf8.decode(dataBytes);
|
||||
final dataObject = convert.json.decode(dataStr);
|
||||
return dataObject as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<void> _setSecrets(Map<String, dynamic> data) async {
|
||||
final dataStr = convert.json.encode(data);
|
||||
final b64 = convert.base64.encode(convert.utf8.encode(dataStr));
|
||||
final respStr = await invokeMethod("encrypt_data", {"buff": "$b64"});
|
||||
final respStr = await invokeMethod('encrypt_data', {'buff': b64});
|
||||
final resp = convert.json.decode(respStr);
|
||||
final dataBytes = convert.base64.decode(resp["result"]["res_buff"] as String);
|
||||
final dataBytes = convert.base64.decode(resp['result']['res_buff'] as String);
|
||||
final file = await _getWalletSecretsFile();
|
||||
file.writeAsBytesSync(dataBytes);
|
||||
}
|
||||
@@ -124,7 +128,25 @@ mixin ZanoWalletApi {
|
||||
return _setWalletSecret("passphrase", passphrase);
|
||||
}
|
||||
|
||||
Future<String?> getBip39Mnemonic() async {
|
||||
return await _getWalletSecret("bip39_mnemonic");
|
||||
}
|
||||
|
||||
Future<void> setBip39Secrets({
|
||||
required String mnemonic,
|
||||
required int creationTimestamp,
|
||||
}) async {
|
||||
final secrets = await _getSecrets();
|
||||
secrets["bip39_mnemonic"] = mnemonic;
|
||||
secrets["creation_timestamp"] = creationTimestamp.toString();
|
||||
await _setSecrets(secrets);
|
||||
}
|
||||
|
||||
Future<String> getSeed() async {
|
||||
final bip39Mnemonic = await getBip39Mnemonic();
|
||||
if (bip39Mnemonic != null && bip39Mnemonic.isNotEmpty) {
|
||||
return bip39Mnemonic;
|
||||
}
|
||||
final passphrase = await getPassphrase();
|
||||
final respStr = await invokeMethod("get_restore_info", {"seed_password": passphrase ?? ""});
|
||||
final resp = convert.json.decode(respStr);
|
||||
@@ -328,10 +350,59 @@ mixin ZanoWalletApi {
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<CreateWalletResult> restoreWalletFromDerivations(
|
||||
String path,
|
||||
String password,
|
||||
String secretDerivation, {
|
||||
bool isAuditable = false,
|
||||
required int creationTimestamp,
|
||||
}) async {
|
||||
printV('restore_wallet_from_derivations path $path');
|
||||
final params = jsonEncode({
|
||||
"creation_timestamp": creationTimestamp,
|
||||
"is_auditable": isAuditable,
|
||||
"pass": password,
|
||||
"path": path,
|
||||
"secret_derivation": secretDerivation,
|
||||
});
|
||||
final json = await callSyncMethod('restore_from_derivations', 0, params);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
final code = map!['error']!['code'] ?? '';
|
||||
final message = map['error']!['message'] ?? '';
|
||||
if (code == Consts.errorWrongSeed) {
|
||||
throw RestoreFromSeedsException(
|
||||
'Error restoring wallet\nPlease check the seed words are correct. Additionally, if you created this wallet with a passphrase please add it under the Advanced Settings page.');
|
||||
} else if (code == Consts.errorAlreadyExists) {
|
||||
throw RestoreFromSeedsException('Error restoring wallet, already exists');
|
||||
}
|
||||
throw RestoreFromSeedsException('Error restoring wallet, $message ($code)');
|
||||
}
|
||||
if (map?['result'] == null) {
|
||||
throw RestoreFromSeedsException('Error restoring wallet, empty response');
|
||||
}
|
||||
final result = CreateWalletResult.fromJson(map!['result'] as Map<String, dynamic>);
|
||||
openWalletCache[path] = result;
|
||||
printV('restore_wallet_from_derivations ${result.name} ${result.wi.address}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<CreateWalletResult> restoreWalletFromSeed(
|
||||
String path, String password, String seed, String? passphrase) async {
|
||||
String path,
|
||||
String password,
|
||||
String seed,
|
||||
String? passphrase, {
|
||||
required int creationTimestamp,
|
||||
}) async {
|
||||
printV('restore_wallet path $path');
|
||||
final json = zano.PlainWallet_restore(seed, path, password, passphrase ?? '');
|
||||
final params = jsonEncode({
|
||||
"creation_timestamp": creationTimestamp,
|
||||
"pass": password,
|
||||
"path": path,
|
||||
"seed_phrase": seed,
|
||||
"seed_pass": passphrase ?? '',
|
||||
});
|
||||
final json = await callSyncMethod('restore', 0, params);
|
||||
final map = jsonDecode(json) as Map<String, dynamic>?;
|
||||
if (map?['error'] != null) {
|
||||
final code = map!['error']!['code'] ?? '';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
@@ -14,8 +15,14 @@ import 'package:hive/hive.dart';
|
||||
import 'package:monero/zano.dart' as zano;
|
||||
|
||||
class ZanoNewWalletCredentials extends WalletCredentials {
|
||||
ZanoNewWalletCredentials({required String name, String? password, required String? passphrase})
|
||||
: super(name: name, password: password, passphrase: passphrase);
|
||||
ZanoNewWalletCredentials({
|
||||
required String name,
|
||||
String? password,
|
||||
required String? passphrase,
|
||||
this.mnemonic,
|
||||
}) : super(name: name, password: password, passphrase: passphrase);
|
||||
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class ZanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
@@ -52,7 +59,9 @@ class ZanoWalletService extends WalletService<
|
||||
ZanoRestoreWalletFromSeedCredentials,
|
||||
ZanoRestoreWalletFromKeysCredentials,
|
||||
ZanoNewWalletCredentials> {
|
||||
ZanoWalletService();
|
||||
ZanoWalletService(this.isDirect);
|
||||
|
||||
final bool isDirect;
|
||||
|
||||
static bool walletFilesExist(String path) =>
|
||||
!File(path).existsSync() && !File('$path.keys').existsSync();
|
||||
@@ -65,7 +74,8 @@ class ZanoWalletService extends WalletService<
|
||||
@override
|
||||
Future<ZanoWallet> create(WalletCredentials credentials, {bool? isTestnet}) async {
|
||||
printV('zanowallet service create isTestnet $isTestnet');
|
||||
return await ZanoWalletBase.create(credentials: credentials);
|
||||
return await ZanoWalletBase.create(
|
||||
credentials: credentials, encryptionFileUtils: encryptionFileUtilsFor(isDirect));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -82,12 +92,21 @@ class ZanoWalletService extends WalletService<
|
||||
}
|
||||
try {
|
||||
final wallet =
|
||||
await ZanoWalletBase.open(name: name, password: password, walletInfo: walletInfo);
|
||||
await ZanoWalletBase.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
|
||||
saveBackup(name);
|
||||
return wallet;
|
||||
} catch (e) {
|
||||
printV('openWallet $name failed: $e');
|
||||
await restoreWalletFilesFromBackup(name);
|
||||
return await ZanoWalletBase.open(name: name, password: password, walletInfo: walletInfo);
|
||||
return await ZanoWalletBase.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,15 +134,24 @@ class ZanoWalletService extends WalletService<
|
||||
throw Exception('Wallet not found');
|
||||
}
|
||||
final currentWallet =
|
||||
ZanoWallet(currentWalletInfo, await currentWalletInfo.getDerivationInfo(), password);
|
||||
ZanoWallet(currentWalletInfo, await currentWalletInfo.getDerivationInfo(), password,
|
||||
encryptionFileUtilsFor(isDirect));
|
||||
|
||||
final oldPath = await pathForWallet(name: currentName, type: getType());
|
||||
final cached = ZanoWalletApi.openWalletCache.remove(oldPath);
|
||||
if (cached != null) {
|
||||
currentWallet.hWallet = cached.walletId;
|
||||
await currentWallet.closeWallet(cached.walletId, force: true);
|
||||
}
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
|
||||
final newWalletInfo = currentWalletInfo;
|
||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||
newWalletInfo.name = newName;
|
||||
|
||||
await newWalletInfo.save();
|
||||
final newDirPath = await pathForWalletDir(name: newName, type: getType());
|
||||
currentWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||
currentWalletInfo.name = newName;
|
||||
currentWalletInfo.dirPath = newDirPath;
|
||||
currentWalletInfo.path = '$newDirPath/$newName';
|
||||
await currentWalletInfo.save();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -135,7 +163,8 @@ class ZanoWalletService extends WalletService<
|
||||
@override
|
||||
Future<ZanoWallet> restoreFromSeed(ZanoRestoreWalletFromSeedCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
return ZanoWalletBase.restore(credentials: credentials);
|
||||
return ZanoWalletBase.restore(
|
||||
credentials: credentials, encryptionFileUtils: encryptionFileUtilsFor(isDirect));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -11,6 +11,8 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
bip39: ^1.0.6
|
||||
bip32: ^2.0.0
|
||||
ffi: ^2.0.1
|
||||
http: ^1.1.0
|
||||
path_provider: ^2.0.11
|
||||
@@ -25,7 +27,7 @@ dependencies:
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
ref: cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
path: impls/monero.dart
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:cw_zano/bip39_seed.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group("Zano bip39 secret derivation", () {
|
||||
group("Test Wallet 1", () {
|
||||
final bip39Seed =
|
||||
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
||||
final expectedSecretDerivation =
|
||||
"bfafd1eb0e43da200c5c11537d355e458e7c326b3bc1b19f4546573d6bac9d0f";
|
||||
final expectedSecretDerivationWithPassphrase =
|
||||
"674ba1caeaf8a79e66a026858c78d4e565032953e98d39bdea12559bcae86a0c";
|
||||
|
||||
test("Get secret derivation from bip39", () {
|
||||
final secretDerivation = getSecretDerivationFromBip39(bip39Seed);
|
||||
expect(secretDerivation, expectedSecretDerivation);
|
||||
});
|
||||
|
||||
test("Get secret derivation from bip39 with passphrase", () {
|
||||
final secretDerivation =
|
||||
getSecretDerivationFromBip39(bip39Seed, passphrase: 'TREZOR');
|
||||
expect(secretDerivation, expectedSecretDerivationWithPassphrase);
|
||||
});
|
||||
});
|
||||
|
||||
group("Test Wallet 2", () {
|
||||
final bip39Seed =
|
||||
'melt veteran patrol echo miss fat wrap apple crowd eyebrow weird boring';
|
||||
final expectedSecretDerivation =
|
||||
"8b2693e1d60b0431e3208762bc8a3f232890f50a3fea1e9605ec10a86afe1800";
|
||||
|
||||
test("Get secret derivation from bip39", () {
|
||||
final secretDerivation = getSecretDerivationFromBip39(bip39Seed);
|
||||
expect(secretDerivation, expectedSecretDerivation);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -86,6 +86,7 @@ class WalletCreationService {
|
||||
case WalletType.dogecoin:
|
||||
case WalletType.nano:
|
||||
case WalletType.zcash:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return true;
|
||||
case WalletType.monero:
|
||||
@@ -93,7 +94,6 @@ class WalletCreationService {
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.banano:
|
||||
case WalletType.zano:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1341,7 +1341,7 @@ Future<void> setup({
|
||||
case WalletType.wownero:
|
||||
return wownero!.createWowneroWalletService(_unspentCoinsInfoSource);
|
||||
case WalletType.zano:
|
||||
return zano!.createZanoWalletService();
|
||||
return zano!.createZanoWalletService(SettingsStoreBase.walletPasswordDirectInput);
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredWalletService(
|
||||
_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
|
||||
|
||||
@@ -17,11 +17,11 @@ bool isBIP39Wallet(WalletType walletType) {
|
||||
case WalletType.monero:
|
||||
case WalletType.dogecoin:
|
||||
case WalletType.zcash:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return true;
|
||||
case WalletType.wownero:
|
||||
case WalletType.haven:
|
||||
case WalletType.zano:
|
||||
case WalletType.none:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -286,7 +286,12 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
|
||||
'wallet_restore_from_seed_blockheight_textfield_key',
|
||||
),
|
||||
onHeightOrDateEntered: widget.onHeightOrDateEntered,
|
||||
hasDatePicker: [WalletType.monero, WalletType.wownero, WalletType.zcash].contains(
|
||||
hasDatePicker: [
|
||||
WalletType.monero,
|
||||
WalletType.wownero,
|
||||
WalletType.zcash,
|
||||
WalletType.zano,
|
||||
].contains(
|
||||
widget.type,
|
||||
),
|
||||
walletType: widget.type,
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -606,7 +607,13 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
||||
final seedWords = seedPhrase.split(' ');
|
||||
|
||||
if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true;
|
||||
if (seedWords.length == 26 && walletRestoreViewModel.type == WalletType.zano) return true;
|
||||
if (walletRestoreViewModel.type == WalletType.zano) {
|
||||
if (seedWords.length == 26) return true;
|
||||
if ([12, 24].contains(seedWords.length) && zano!.isBip39Seed(seedPhrase.trim())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seedWords.length == 12 && walletRestoreViewModel.type == WalletType.monero) {
|
||||
return walletRestoreFromSeedFormKey.currentState?.blockchainHeightKey.currentState
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_switch.dart';
|
||||
import 'package:cake_wallet/utils/date_picker.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/zcash/zcash.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -200,6 +201,8 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||
height = wownero!.getHeightByDate(date: date);
|
||||
} else if (widget.walletType == WalletType.zcash) {
|
||||
height = await zcash!.getHeightByDate(date);
|
||||
} else if (widget.walletType == WalletType.zano) {
|
||||
height = zano!.getHeightByDate(date: date);
|
||||
} else {
|
||||
throw Exception("unknown currency in BlockchainHeightWidget");
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.zcash:
|
||||
case WalletType.zano:
|
||||
case WalletType.decred:
|
||||
return true;
|
||||
|
||||
@@ -74,7 +75,6 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
case WalletType.wownero:
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.zano:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,9 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
|
||||
|
||||
bool isNonSeedWallet = wallet.isNonSeedWallet;
|
||||
|
||||
bool isNotMoneroBip39Wallet =
|
||||
wallet.type == WalletType.monero && di.derivationType != DerivationType.bip39;
|
||||
bool isNotBip39CoinWallet =
|
||||
(wallet.type == WalletType.monero || wallet.type == WalletType.zano) &&
|
||||
di.derivationType != DerivationType.bip39;
|
||||
|
||||
bool isNotDecredBip39Wallet = wallet.type == WalletType.decred &&
|
||||
di.derivationType != DerivationType.bip39 &&
|
||||
@@ -142,7 +143,7 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
|
||||
isElectrumDerivationType ||
|
||||
isSameTypeAsSelectedWallet ||
|
||||
isNonSeedWallet ||
|
||||
isNotMoneroBip39Wallet ||
|
||||
isNotBip39CoinWallet ||
|
||||
isNotDecredBip39Wallet;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
);
|
||||
case WalletType.zcash:
|
||||
return zcash!.createZcashNewWalletCredentials(
|
||||
|
||||
@@ -83,8 +83,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
late final bool hasSeedLanguageSelector =
|
||||
[WalletType.monero, WalletType.haven, WalletType.wownero].contains(type);
|
||||
|
||||
late final bool hasBlockchainHeightSelector =
|
||||
[WalletType.monero, WalletType.haven, WalletType.wownero, WalletType.zcash].contains(type);
|
||||
late final bool hasBlockchainHeightSelector = [
|
||||
WalletType.monero,
|
||||
WalletType.haven,
|
||||
WalletType.wownero,
|
||||
WalletType.zcash,
|
||||
WalletType.zano,
|
||||
].contains(type);
|
||||
|
||||
late final bool hasRestoreFromPrivateKey = [
|
||||
WalletType.ethereum,
|
||||
@@ -204,13 +209,15 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
height: height,
|
||||
);
|
||||
case WalletType.zano:
|
||||
return zano!.createZanoRestoreWalletFromSeedCredentials(
|
||||
final credentials = zano!.createZanoRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
height: height,
|
||||
passphrase: passphrase ?? '',
|
||||
mnemonic: seed,
|
||||
);
|
||||
credentials.derivationInfo = derivationInfo;
|
||||
return credentials;
|
||||
case WalletType.decred:
|
||||
return decred!.createDecredRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
@@ -363,6 +370,17 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||
seedKey: seedKey,
|
||||
node: node,
|
||||
);
|
||||
case WalletType.zano:
|
||||
final mnemonic = (credentials['seed'] as String?)?.trim() ?? '';
|
||||
if (mnemonic.isEmpty) break;
|
||||
if (zano!.isBip39Seed(mnemonic)) {
|
||||
return [
|
||||
DerivationInfo(derivationType: DerivationType.bip39),
|
||||
];
|
||||
}
|
||||
return [
|
||||
DerivationInfo(derivationType: DerivationType.unknown),
|
||||
];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+21
-6
@@ -46,7 +46,7 @@ class CWZano extends Zano {
|
||||
@override
|
||||
List<String> getWordList(String language) {
|
||||
assert(language.toLowerCase() == LanguageList.english.toLowerCase());
|
||||
return EnglishMnemonics.words;
|
||||
return [...zano_bip39.bip39EnglishWords, ...EnglishMnemonics.words];
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -61,9 +61,18 @@ class CWZano extends Zano {
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createZanoNewWalletCredentials(
|
||||
{required String name, required String? password, required String? passphrase}) {
|
||||
return ZanoNewWalletCredentials(name: name, password: password, passphrase: passphrase);
|
||||
WalletCredentials createZanoNewWalletCredentials({
|
||||
required String name,
|
||||
required String? password,
|
||||
required String? passphrase,
|
||||
String? mnemonic,
|
||||
}) {
|
||||
return ZanoNewWalletCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
mnemonic: mnemonic,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -122,8 +131,8 @@ class CWZano extends Zano {
|
||||
// }
|
||||
|
||||
@override
|
||||
WalletService createZanoWalletService() {
|
||||
return ZanoWalletService();
|
||||
WalletService createZanoWalletService(bool isDirect) {
|
||||
return ZanoWalletService(isDirect);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -146,6 +155,12 @@ class CWZano extends Zano {
|
||||
@override
|
||||
bool validateAddress(String address) => ZanoUtils.validateAddress(address);
|
||||
|
||||
@override
|
||||
bool isBip39Seed(String mnemonic) => zano_bip39.isBip39Seed(mnemonic);
|
||||
|
||||
@override
|
||||
int getHeightByDate({required DateTime date}) => ZanoUtils.heightByDate(date);
|
||||
|
||||
@override
|
||||
Map<String, List<int>> debugCallLength() {
|
||||
return api.debugCallLength();
|
||||
|
||||
+2
-2
@@ -2050,8 +2050,8 @@ packages:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
ref: cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
resolved-ref: cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
url: "https://github.com/mrcyjanek/monero_c.git"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
||||
@@ -960,7 +960,7 @@ dependency_overrides:
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
ref: cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
path: ./impls/monero.dart
|
||||
msgpack_dart:
|
||||
git:
|
||||
|
||||
@@ -16,7 +16,7 @@ fi
|
||||
# NOTE: Make sure to update monero_c prebuilds link in workflow files
|
||||
# https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC2/release-bundle.zip
|
||||
git fetch -a
|
||||
git checkout 5952bef2ec01b0b7e57c11613cbdc081dcd727c5
|
||||
git checkout cd4fe366cc8d3c188c91de279f43b29c0e044b15
|
||||
git reset --hard
|
||||
git submodule update --init --force --recursive
|
||||
|
||||
|
||||
+5
-2
@@ -1178,6 +1178,7 @@ import 'package:cw_core/zano_asset.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
""";
|
||||
const zanoCWHeaders = """
|
||||
import 'package:cw_zano/bip39_seed.dart' as zano_bip39;
|
||||
import 'package:cw_zano/mnemonics/english.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_credentials.dart';
|
||||
import 'package:cw_zano/model/zano_transaction_info.dart';
|
||||
@@ -1196,12 +1197,12 @@ abstract class Zano {
|
||||
List<String> getWordList(String language);
|
||||
|
||||
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, required String? password, required String? passphrase});
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, required String? password, required String? passphrase, String? mnemonic});
|
||||
Map<String, String> getKeys(Object wallet);
|
||||
Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency});
|
||||
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee});
|
||||
int formatterParseAmount({required String amount, required CryptoCurrency currency});
|
||||
WalletService createZanoWalletService();
|
||||
WalletService createZanoWalletService(bool isDirect);
|
||||
CryptoCurrency? assetOfTransaction(WalletBase wallet, TransactionInfo tx);
|
||||
List<ZanoAsset> getZanoAssets(WalletBase wallet);
|
||||
String getZanoAssetAddress(CryptoCurrency asset);
|
||||
@@ -1211,6 +1212,8 @@ abstract class Zano {
|
||||
Future<CryptoCurrency?> getZanoAsset(WalletBase wallet, String contractAddress);
|
||||
String getAddress(WalletBase wallet);
|
||||
bool validateAddress(String address);
|
||||
bool isBip39Seed(String mnemonic);
|
||||
int getHeightByDate({required DateTime date});
|
||||
Map<String, List<int>> debugCallLength();
|
||||
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user