From 63f04f8cf39aa6cef766f0fe3c170fe516501fbb Mon Sep 17 00:00:00 2001 From: Hanh Date: Sun, 8 Jan 2023 00:54:55 +0800 Subject: [PATCH] Rewind to checkpoint --- idl/data.fbs | 9 + lib/generated/intl/messages_en.dart | 4 + lib/generated/intl/messages_es.dart | 5 +- lib/generated/intl/messages_fr.dart | 4 + lib/generated/l10n.dart | 20 ++ lib/home.dart | 32 +++ lib/l10n/intl_en.arb | 4 +- lib/l10n/intl_es.arb | 6 +- lib/l10n/intl_fr.arb | 4 +- native/zcash-sync | 2 +- .../warp_api_ffi/lib/data_fb_generated.dart | 213 ++++++++++++++++++ packages/warp_api_ffi/lib/warp_api.dart | 6 + .../warp_api_ffi/lib/warp_api_generated.dart | 21 ++ pubspec.yaml | 2 +- 14 files changed, 325 insertions(+), 7 deletions(-) diff --git a/idl/data.fbs b/idl/data.fbs index 09d02e1..48d69f4 100644 --- a/idl/data.fbs +++ b/idl/data.fbs @@ -140,3 +140,12 @@ table AddressBalance { table AddressBalanceVec { values:[AddressBalance]; } + +table Checkpoint { + height:uint32; + timestamp:uint32; +} + +table CheckpointVec { + values:[Checkpoint]; +} diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index af3a1b6..ee2f183 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -364,6 +364,8 @@ class MessageLookup extends MessageLookupByLibrary { "resumeScan": MessageLookupByLibrary.simpleMessage("Resume Scan"), "retrieveTransactionDetails": MessageLookupByLibrary.simpleMessage( "Retrieve Transaction Details"), + "rewindToCheckpoint": + MessageLookupByLibrary.simpleMessage("Rewind to Checkpoint"), "roundToMillis": MessageLookupByLibrary.simpleMessage("Round to millis"), "saveBackup": MessageLookupByLibrary.simpleMessage("Save Backup"), @@ -376,6 +378,8 @@ class MessageLookup extends MessageLookupByLibrary { "secretShare": MessageLookupByLibrary.simpleMessage("Secret Share"), "seed": MessageLookupByLibrary.simpleMessage("Seed"), "selectAccount": MessageLookupByLibrary.simpleMessage("Select Account"), + "selectCheckpoint": + MessageLookupByLibrary.simpleMessage("Select Checkpoint"), "selectNotesToExcludeFromPayments": MessageLookupByLibrary.simpleMessage( "Select notes to EXCLUDE from payments"), diff --git a/lib/generated/intl/messages_es.dart b/lib/generated/intl/messages_es.dart index 3bb60a4..bd4227a 100644 --- a/lib/generated/intl/messages_es.dart +++ b/lib/generated/intl/messages_es.dart @@ -29,7 +29,7 @@ class MessageLookup extends MessageLookupByLibrary { "Copia de seguridad - ${name} - Requerida para restaurar"; static String m3(rewindHeight) => - "Block reorg detected. Rewind to ${rewindHeight}"; + "Se ha detectado una reorganización de la blockchain. Rebobinar hasta ${rewindHeight}"; static String m4(address, amount) => "Desea firmar una transacción a ${address} por ${amount}"; @@ -370,6 +370,7 @@ class MessageLookup extends MessageLookupByLibrary { "resumeScan": MessageLookupByLibrary.simpleMessage("Reanudar Escaneo"), "retrieveTransactionDetails": MessageLookupByLibrary.simpleMessage( "Obtener detalles de la transacción"), + "rewindToCheckpoint": MessageLookupByLibrary.simpleMessage("Rebobinar"), "roundToMillis": MessageLookupByLibrary.simpleMessage("Redondear a milésimas"), "saveBackup": @@ -384,6 +385,8 @@ class MessageLookup extends MessageLookupByLibrary { "seed": MessageLookupByLibrary.simpleMessage("Semilla"), "selectAccount": MessageLookupByLibrary.simpleMessage("Seleccionar cuenta"), + "selectCheckpoint": + MessageLookupByLibrary.simpleMessage("Seleccionar Fetcha/Altura"), "selectNotesToExcludeFromPayments": MessageLookupByLibrary.simpleMessage( "Seleccionar Notas a EXCLUIR de los pagos"), diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart index 14e08b4..c57d3b8 100644 --- a/lib/generated/intl/messages_fr.dart +++ b/lib/generated/intl/messages_fr.dart @@ -370,6 +370,8 @@ class MessageLookup extends MessageLookupByLibrary { "resumeScan": MessageLookupByLibrary.simpleMessage("Continuer Sync"), "retrieveTransactionDetails": MessageLookupByLibrary.simpleMessage( "Récupérer les détails de la transaction"), + "rewindToCheckpoint": + MessageLookupByLibrary.simpleMessage("Revenir au Bloc"), "roundToMillis": MessageLookupByLibrary.simpleMessage("Arrondir au millième"), "saveBackup": @@ -384,6 +386,8 @@ class MessageLookup extends MessageLookupByLibrary { "seed": MessageLookupByLibrary.simpleMessage("Graine"), "selectAccount": MessageLookupByLibrary.simpleMessage("Choisissez un compte"), + "selectCheckpoint": MessageLookupByLibrary.simpleMessage( + "Selectionner la date du bloc"), "selectNotesToExcludeFromPayments": MessageLookupByLibrary.simpleMessage( "Sélectionnez les billets à EXCLURE des paiements"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 0bb79d3..da22aef 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2881,6 +2881,26 @@ class S { args: [], ); } + + /// `Rewind to Checkpoint` + String get rewindToCheckpoint { + return Intl.message( + 'Rewind to Checkpoint', + name: 'rewindToCheckpoint', + desc: '', + args: [], + ); + } + + /// `Select Checkpoint` + String get selectCheckpoint { + return Intl.message( + 'Select Checkpoint', + name: 'selectCheckpoint', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/home.dart b/lib/home.dart index e364121..b876c7b 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -17,6 +17,7 @@ import 'account.dart'; import 'animated_qr.dart'; import 'budget.dart'; import 'contact.dart'; +import 'db.dart'; import 'history.dart'; import 'generated/l10n.dart'; import 'main.dart'; @@ -189,6 +190,7 @@ class HomeInnerState extends State with SingleTickerProviderState PopupMenuButton( child: Text(s.advanced), itemBuilder: (_) => [ + PopupMenuItem(child: Text(s.rewindToCheckpoint), value: "Rewind"), PopupMenuItem(child: Text(s.convertToWatchonly), enabled: active.canPay, value: "Cold"), PopupMenuItem(child: Text(s.signOffline), enabled: active.canPay, value: "Sign"), PopupMenuItem(child: Text(s.broadcast), value: "Broadcast"), @@ -266,6 +268,9 @@ class HomeInnerState extends State with SingleTickerProviderState case "Pools": Navigator.of(context).pushNamed('/pools'); break; + case "Rewind": + _rewind(); + break; case "Cold": _cold(); break; @@ -312,6 +317,33 @@ class HomeInnerState extends State with SingleTickerProviderState Navigator.of(context).pushNamed('/keytool'); } } + + _rewind() async { + final s = S.of(context); + int? height = null; + final checkpoints = WarpApi.getCheckpoints(active.coin); + final items = checkpoints.map((c) => DropdownMenuItem(value: c.height, + child: Text('${noteDateFormat.format(DateTime.fromMillisecondsSinceEpoch(c.timestamp * 1000))} - ${c.height}') + )).toList(); + final confirmed = await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: Text(s.rewindToCheckpoint), + content: SingleChildScrollView(child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DropdownButtonFormField(items: items, hint: Text(s.selectCheckpoint), onChanged: (v) { height = v; })] + )), + actions: confirmButtons(context, () => Navigator.of(context).pop(true)) + ) + ) ?? false; + final h = height; + if (confirmed && h != null) { + showSnackBar(s.blockReorgDetectedRewind(h)); + WarpApi.rewindTo(h); + } + } _cold() { showDialog( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index be6b6eb..24b5235 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -281,5 +281,7 @@ "newTemplate": "New Template", "name": "Name", "deleteTemplate": "Delete Template?", - "areYouSureYouWantToDeleteThisSendTemplate": "Are you sure you want to delete this send template?" + "areYouSureYouWantToDeleteThisSendTemplate": "Are you sure you want to delete this send template?", + "rewindToCheckpoint": "Rewind to Checkpoint", + "selectCheckpoint": "Select Checkpoint" } diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 13cd810..9dd1329 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -257,7 +257,7 @@ "newLabel": "Nueva", "invalidQrCode": "QR inválido: {message}", "expert": "Expert", - "blockReorgDetectedRewind": "Block reorg detected. Rewind to {rewindHeight}", + "blockReorgDetectedRewind": "Se ha detectado una reorganización de la blockchain. Rebobinar hasta {rewindHeight}", "goToTransaction": "Ver Transacción", "transactions": "Transacciónes", "synchronizationInProgress": "Sincronización en progreso", @@ -279,5 +279,7 @@ "newTemplate": "Nueva plantilla", "name": "Nombre", "deleteTemplate": "Eliminar plantilla?", - "areYouSureYouWantToDeleteThisSendTemplate": "¿Está seguro de que desea eliminar esta plantilla de envío?" + "areYouSureYouWantToDeleteThisSendTemplate": "¿Está seguro de que desea eliminar esta plantilla de envío?", + "rewindToCheckpoint": "Rebobinar", + "selectCheckpoint": "Seleccionar Fetcha/Altura" } diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 5112fb2..6ac5cf4 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -279,5 +279,7 @@ "newTemplate": "Nouveau modèle", "name": "Nom", "deleteTemplate": "Effacer ce modèle?", - "areYouSureYouWantToDeleteThisSendTemplate": "Voulez-vous vraiment supprimer ce modèle?" + "areYouSureYouWantToDeleteThisSendTemplate": "Voulez-vous vraiment supprimer ce modèle?", + "rewindToCheckpoint": "Revenir au Bloc", + "selectCheckpoint": "Selectionner la date du bloc" } diff --git a/native/zcash-sync b/native/zcash-sync index b9726bf..337fe20 160000 --- a/native/zcash-sync +++ b/native/zcash-sync @@ -1 +1 @@ -Subproject commit b9726bf29323140f07732ea664fdc1f3447048d7 +Subproject commit 337fe20b4fe7ec3b839c9d48e0a5308453e3419d diff --git a/packages/warp_api_ffi/lib/data_fb_generated.dart b/packages/warp_api_ffi/lib/data_fb_generated.dart index 7d1d2d9..c2a3960 100644 --- a/packages/warp_api_ffi/lib/data_fb_generated.dart +++ b/packages/warp_api_ffi/lib/data_fb_generated.dart @@ -3014,3 +3014,216 @@ class AddressBalanceVecObjectBuilder extends fb.ObjectBuilder { return fbBuilder.buffer; } } +class Checkpoint { + Checkpoint._(this._bc, this._bcOffset); + factory Checkpoint(List bytes) { + final rootRef = fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader reader = _CheckpointReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + int get height => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 4, 0); + int get timestamp => const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 6, 0); + + @override + String toString() { + return 'Checkpoint{height: ${height}, timestamp: ${timestamp}}'; + } + + CheckpointT unpack() => CheckpointT( + height: height, + timestamp: timestamp); + + static int pack(fb.Builder fbBuilder, CheckpointT? object) { + if (object == null) return 0; + return object.pack(fbBuilder); + } +} + +class CheckpointT implements fb.Packable { + int height; + int timestamp; + + CheckpointT({ + this.height = 0, + this.timestamp = 0}); + + @override + int pack(fb.Builder fbBuilder) { + fbBuilder.startTable(2); + fbBuilder.addUint32(0, height); + fbBuilder.addUint32(1, timestamp); + return fbBuilder.endTable(); + } + + @override + String toString() { + return 'CheckpointT{height: ${height}, timestamp: ${timestamp}}'; + } +} + +class _CheckpointReader extends fb.TableReader { + const _CheckpointReader(); + + @override + Checkpoint createObject(fb.BufferContext bc, int offset) => + Checkpoint._(bc, offset); +} + +class CheckpointBuilder { + CheckpointBuilder(this.fbBuilder); + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(2); + } + + int addHeight(int? height) { + fbBuilder.addUint32(0, height); + return fbBuilder.offset; + } + int addTimestamp(int? timestamp) { + fbBuilder.addUint32(1, timestamp); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class CheckpointObjectBuilder extends fb.ObjectBuilder { + final int? _height; + final int? _timestamp; + + CheckpointObjectBuilder({ + int? height, + int? timestamp, + }) + : _height = height, + _timestamp = timestamp; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish(fb.Builder fbBuilder) { + fbBuilder.startTable(2); + fbBuilder.addUint32(0, _height); + fbBuilder.addUint32(1, _timestamp); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String? fileIdentifier]) { + final fbBuilder = fb.Builder(deduplicateTables: false); + fbBuilder.finish(finish(fbBuilder), fileIdentifier); + return fbBuilder.buffer; + } +} +class CheckpointVec { + CheckpointVec._(this._bc, this._bcOffset); + factory CheckpointVec(List bytes) { + final rootRef = fb.BufferContext.fromBytes(bytes); + return reader.read(rootRef, 0); + } + + static const fb.Reader reader = _CheckpointVecReader(); + + final fb.BufferContext _bc; + final int _bcOffset; + + List? get values => const fb.ListReader(Checkpoint.reader).vTableGetNullable(_bc, _bcOffset, 4); + + @override + String toString() { + return 'CheckpointVec{values: ${values}}'; + } + + CheckpointVecT unpack() => CheckpointVecT( + values: values?.map((e) => e.unpack()).toList()); + + static int pack(fb.Builder fbBuilder, CheckpointVecT? object) { + if (object == null) return 0; + return object.pack(fbBuilder); + } +} + +class CheckpointVecT implements fb.Packable { + List? values; + + CheckpointVecT({ + this.values}); + + @override + int pack(fb.Builder fbBuilder) { + final int? valuesOffset = values == null ? null + : fbBuilder.writeList(values!.map((b) => b.pack(fbBuilder)).toList()); + fbBuilder.startTable(1); + fbBuilder.addOffset(0, valuesOffset); + return fbBuilder.endTable(); + } + + @override + String toString() { + return 'CheckpointVecT{values: ${values}}'; + } +} + +class _CheckpointVecReader extends fb.TableReader { + const _CheckpointVecReader(); + + @override + CheckpointVec createObject(fb.BufferContext bc, int offset) => + CheckpointVec._(bc, offset); +} + +class CheckpointVecBuilder { + CheckpointVecBuilder(this.fbBuilder); + + final fb.Builder fbBuilder; + + void begin() { + fbBuilder.startTable(1); + } + + int addValuesOffset(int? offset) { + fbBuilder.addOffset(0, offset); + return fbBuilder.offset; + } + + int finish() { + return fbBuilder.endTable(); + } +} + +class CheckpointVecObjectBuilder extends fb.ObjectBuilder { + final List? _values; + + CheckpointVecObjectBuilder({ + List? values, + }) + : _values = values; + + /// Finish building, and store into the [fbBuilder]. + @override + int finish(fb.Builder fbBuilder) { + final int? valuesOffset = _values == null ? null + : fbBuilder.writeList(_values!.map((b) => b.getOrCreateOffset(fbBuilder)).toList()); + fbBuilder.startTable(1); + fbBuilder.addOffset(0, valuesOffset); + return fbBuilder.endTable(); + } + + /// Convenience method to serialize to byte list. + @override + Uint8List toBytes([String? fileIdentifier]) { + final fbBuilder = fb.Builder(deduplicateTables: false); + fbBuilder.finish(finish(fbBuilder), fileIdentifier); + return fbBuilder.buffer; + } +} diff --git a/packages/warp_api_ffi/lib/warp_api.dart b/packages/warp_api_ffi/lib/warp_api.dart index 7f23a76..0e9dc88 100644 --- a/packages/warp_api_ffi/lib/warp_api.dart +++ b/packages/warp_api_ffi/lib/warp_api.dart @@ -543,6 +543,12 @@ class WarpApi { static void invertExcluded(int coin, int id) { unwrapResultU8(warp_api_lib.invert_excluded(coin, id)); } + + static List getCheckpoints(int coin) { + final r = unwrapResultBytes(warp_api_lib.get_checkpoints(coin)); + final quotes = CheckpointVec(r); + return quotes.values!; + } } String signOnlyIsolateFn(SignOnlyParams params) { diff --git a/packages/warp_api_ffi/lib/warp_api_generated.dart b/packages/warp_api_ffi/lib/warp_api_generated.dart index e08dc3b..ce33aa1 100644 --- a/packages/warp_api_ffi/lib/warp_api_generated.dart +++ b/packages/warp_api_ffi/lib/warp_api_generated.dart @@ -1215,6 +1215,19 @@ class NativeLibrary { late final _dart_invert_excluded _invert_excluded = _invert_excluded_ptr.asFunction<_dart_invert_excluded>(); + CResult______u8 get_checkpoints( + int coin, + ) { + return _get_checkpoints( + coin, + ); + } + + late final _get_checkpoints_ptr = + _lookup>('get_checkpoints'); + late final _dart_get_checkpoints _get_checkpoints = + _get_checkpoints_ptr.asFunction<_dart_get_checkpoints>(); + int has_cuda() { return _has_cuda(); } @@ -2180,6 +2193,14 @@ typedef _dart_invert_excluded = CResult_u8 Function( int id, ); +typedef _c_get_checkpoints = CResult______u8 Function( + ffi.Uint8 coin, +); + +typedef _dart_get_checkpoints = CResult______u8 Function( + int coin, +); + typedef _c_has_cuda = ffi.Int8 Function(); typedef _dart_has_cuda = int Function(); diff --git a/pubspec.yaml b/pubspec.yaml index fed6e36..26643e2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.2.17+345 +version: 1.2.17+346 environment: sdk: ">=2.12.0 <3.0.0"