From 778764177e2ec9980da604d953925d07db3cda9c Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Mon, 15 Jan 2024 20:25:31 -0800 Subject: [PATCH 01/60] refactor: move ScanActivity and related classes to common --- .../wallet/common}/ui/scan/CameraManager.java | 34 +++++----- .../wallet/common}/ui/scan/ScanActivity.java | 62 +++++++++---------- .../wallet/common}/ui/scan/ScanViewModel.java | 13 ++-- .../wallet/common}/ui/scan/ScannerView.java | 16 ++--- .../wallet/common}/util/OnFirstPreDraw.java | 2 +- common/src/main/res/layout/scan_activity.xml | 34 ++++++++++ common/src/main/res/values/colors.xml | 7 +++ wallet/res/layout/scan_activity.xml | 17 ----- wallet/res/values/colors.xml | 7 +-- wallet/res/values/strings.xml | 4 -- 10 files changed, 105 insertions(+), 91 deletions(-) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/ui/scan/CameraManager.java (97%) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/ui/scan/ScanActivity.java (98%) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/ui/scan/ScanViewModel.java (63%) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/ui/scan/ScannerView.java (98%) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/util/OnFirstPreDraw.java (98%) create mode 100644 common/src/main/res/layout/scan_activity.xml delete mode 100644 wallet/res/layout/scan_activity.xml diff --git a/wallet/src/de/schildbach/wallet/ui/scan/CameraManager.java b/common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java similarity index 97% rename from wallet/src/de/schildbach/wallet/ui/scan/CameraManager.java rename to common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java index 25f326ca6f..d085277365 100644 --- a/wallet/src/de/schildbach/wallet/ui/scan/CameraManager.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java @@ -15,19 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.ui.scan; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.zxing.PlanarYUVLuminanceSource; +package org.dash.wallet.common.ui.scan; import android.annotation.SuppressLint; import android.graphics.Rect; @@ -37,6 +25,18 @@ import android.hardware.Camera.PreviewCallback; import android.view.TextureView; +import com.google.zxing.PlanarYUVLuminanceSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + /** * @author Andreas Schildbach */ @@ -80,9 +80,9 @@ public Camera open(final TextureView textureView, final int displayOrientation) displayOrientation); camera = Camera.open(cameraId); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) camera.setDisplayOrientation((720 - displayOrientation - cameraInfo.orientation) % 360); - else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) + else if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) camera.setDisplayOrientation((720 - displayOrientation + cameraInfo.orientation) % 360); else throw new IllegalStateException("facing: " + cameraInfo.facing); @@ -146,14 +146,14 @@ private int determineCameraId() { // prefer back-facing camera for (int i = 0; i < cameraCount; i++) { Camera.getCameraInfo(i, cameraInfo); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) return i; } // fall back to front-facing camera for (int i = 0; i < cameraCount; i++) { Camera.getCameraInfo(i, cameraInfo); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) + if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) return i; } diff --git a/wallet/src/de/schildbach/wallet/ui/scan/ScanActivity.java b/common/src/main/java/org/dash/wallet/common/ui/scan/ScanActivity.java similarity index 98% rename from wallet/src/de/schildbach/wallet/ui/scan/ScanActivity.java rename to common/src/main/java/org/dash/wallet/common/ui/scan/ScanActivity.java index faef9a6fec..416265025a 100644 --- a/wallet/src/de/schildbach/wallet/ui/scan/ScanActivity.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/ScanActivity.java @@ -15,42 +15,13 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.ui.scan; - -import java.util.EnumMap; -import java.util.Map; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import org.dash.wallet.common.ui.BaseAlertDialogBuilder; -import org.dash.wallet.common.ui.BaseDialogFragment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.DecodeHintType; -import com.google.zxing.LuminanceSource; -import com.google.zxing.PlanarYUVLuminanceSource; -import com.google.zxing.ReaderException; -import com.google.zxing.Result; -import com.google.zxing.ResultPoint; -import com.google.zxing.ResultPointCallback; -import com.google.zxing.common.HybridBinarizer; -import com.google.zxing.qrcode.QRCodeReader; - -import dagger.hilt.android.AndroidEntryPoint; -import de.schildbach.wallet.ui.LockScreenActivity; -import de.schildbach.wallet.util.OnFirstPreDraw; -import de.schildbach.wallet_test.R; -import kotlin.Unit; +package org.dash.wallet.common.ui.scan; import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.ActivityOptions; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -88,12 +59,38 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.LuminanceSource; +import com.google.zxing.PlanarYUVLuminanceSource; +import com.google.zxing.ReaderException; +import com.google.zxing.Result; +import com.google.zxing.ResultPoint; +import com.google.zxing.ResultPointCallback; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.qrcode.QRCodeReader; + +import org.dash.wallet.common.R; +import org.dash.wallet.common.SecureActivity; +import org.dash.wallet.common.ui.BaseDialogFragment; +import org.dash.wallet.common.util.OnFirstPreDraw; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.EnumMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import dagger.hilt.android.AndroidEntryPoint; +import kotlin.Unit; + /** * @author Andreas Schildbach */ @SuppressWarnings("deprecation") @AndroidEntryPoint -public final class ScanActivity extends LockScreenActivity +public final class ScanActivity extends SecureActivity implements SurfaceTextureListener, ActivityCompat.OnRequestPermissionsResultCallback { private static final String INTENT_EXTRA_SCENE_TRANSITION_X = "scene_transition_x"; private static final String INTENT_EXTRA_SCENE_TRANSITION_Y = "scene_transition_y"; @@ -185,7 +182,7 @@ public void onChanged(final Void v) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - getIntent().putExtra(INTENT_EXTRA_KEEP_UNLOCKED, true); + turnOffAutoLogout(); setContentView(R.layout.scan_activity); contentView = findViewById(android.R.id.content); scannerView = (ScannerView) findViewById(R.id.scan_activity_mask); @@ -267,6 +264,7 @@ protected void onDestroy() { // We're removing the requested orientation because if we don't, somehow the requested orientation is // bleeding through to the calling activity, forcing it into a locked state until it is restarted. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + turnOnAutoLogout(); super.onDestroy(); } diff --git a/wallet/src/de/schildbach/wallet/ui/scan/ScanViewModel.java b/common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java similarity index 63% rename from wallet/src/de/schildbach/wallet/ui/scan/ScanViewModel.java rename to common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java index dda90478d4..cbd5f9bc14 100644 --- a/wallet/src/de/schildbach/wallet/ui/scan/ScanViewModel.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright the original author or authors. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -12,20 +12,21 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -package de.schildbach.wallet.ui.scan; +package org.dash.wallet.common.ui.scan; import androidx.lifecycle.ViewModel; -import de.schildbach.wallet.ui.util.SingleLiveEventExt; +import org.dash.wallet.common.data.SingleLiveEvent; + /** * @author Andreas Schildbach */ public class ScanViewModel extends ViewModel { - public final SingleLiveEventExt showPermissionWarnDialog = new SingleLiveEventExt<>(); - public final SingleLiveEventExt showProblemWarnDialog = new SingleLiveEventExt<>(); + public final SingleLiveEvent showPermissionWarnDialog = new SingleLiveEvent<>(); + public final SingleLiveEvent showProblemWarnDialog = new SingleLiveEvent<>(); } diff --git a/wallet/src/de/schildbach/wallet/ui/scan/ScannerView.java b/common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java similarity index 98% rename from wallet/src/de/schildbach/wallet/ui/scan/ScannerView.java rename to common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java index dd82cde03a..fa4c4d2686 100644 --- a/wallet/src/de/schildbach/wallet/ui/scan/ScannerView.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java @@ -15,13 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.ui.scan; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import com.google.zxing.ResultPoint; +package org.dash.wallet.common.ui.scan; import android.content.Context; import android.content.res.Resources; @@ -35,7 +29,13 @@ import android.util.AttributeSet; import android.view.View; -import de.schildbach.wallet_test.R; +import com.google.zxing.ResultPoint; + +import org.dash.wallet.common.R; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; /** * @author Andreas Schildbach diff --git a/wallet/src/de/schildbach/wallet/util/OnFirstPreDraw.java b/common/src/main/java/org/dash/wallet/common/util/OnFirstPreDraw.java similarity index 98% rename from wallet/src/de/schildbach/wallet/util/OnFirstPreDraw.java rename to common/src/main/java/org/dash/wallet/common/util/OnFirstPreDraw.java index d30f46d824..3d78470aec 100644 --- a/wallet/src/de/schildbach/wallet/util/OnFirstPreDraw.java +++ b/common/src/main/java/org/dash/wallet/common/util/OnFirstPreDraw.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.util; +package org.dash.wallet.common.util; import android.view.View; import android.view.ViewTreeObserver; diff --git a/common/src/main/res/layout/scan_activity.xml b/common/src/main/res/layout/scan_activity.xml new file mode 100644 index 0000000000..0c8336cda5 --- /dev/null +++ b/common/src/main/res/layout/scan_activity.xml @@ -0,0 +1,34 @@ + + + + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/values/colors.xml b/common/src/main/res/values/colors.xml index 0650e03521..9a38535377 100644 --- a/common/src/main/res/values/colors.xml +++ b/common/src/main/res/values/colors.xml @@ -110,4 +110,11 @@ #C4C8CC #AAAEB3 #5D5F61 + + + #60000000 + #cc0000 + #ff6600 + #b0000000 + #c099cc00 \ No newline at end of file diff --git a/wallet/res/layout/scan_activity.xml b/wallet/res/layout/scan_activity.xml deleted file mode 100644 index b0b2b353c5..0000000000 --- a/wallet/res/layout/scan_activity.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/wallet/res/values/colors.xml b/wallet/res/values/colors.xml index ea2a59acb7..78554f9ebb 100644 --- a/wallet/res/values/colors.xml +++ b/wallet/res/values/colors.xml @@ -1,10 +1,5 @@ - - #60000000 - #cc0000 - #ff6600 - #b0000000 - #c099cc00 + \ No newline at end of file diff --git a/wallet/res/values/strings.xml b/wallet/res/values/strings.xml index e11a5a883e..20b5ae1fda 100644 --- a/wallet/res/values/strings.xml +++ b/wallet/res/values/strings.xml @@ -225,10 +225,6 @@ Encrypting… Decrypting… Done - Sorry - The camera has a problem. You probably need to restart the device. - Camera permission - In order to scan QR codes, you need to grant permission to use the camera. Cannot read data:\n%s Cannot recognize input:\n%s Invalid Dash URI:\n%s From 85dcdb720d7e1355d3b731fe061ee13d10c6299c Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 08:24:29 -0800 Subject: [PATCH 02/60] fix: align with Bitcoin Wallet code and replace deprecated functions --- .../wallet/common/ui/scan/CameraManager.java | 25 +++++++------------ .../wallet/common/ui/scan/ScannerView.java | 17 ++++++------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java b/common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java index d085277365..e3301a647f 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/CameraManager.java @@ -44,8 +44,8 @@ public final class CameraManager { private static final int MIN_FRAME_SIZE = 240; private static final int MAX_FRAME_SIZE = 600; - private static final int MIN_PREVIEW_PIXELS = 470 * 320; // normal screen - private static final int MAX_PREVIEW_PIXELS = 1280 * 720; + private static final int MIN_PREVIEW_PIXELS = 640 * 480; // normal screen + private static final int MAX_PREVIEW_PIXELS = 1920 * 1080; private Camera camera; private CameraInfo cameraInfo = new CameraInfo(); @@ -95,6 +95,8 @@ else if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) final int width = textureView.getWidth(); final int height = textureView.getHeight(); + log.info("texture size is {}/{}, picked preview size is {}/{}", width, height, cameraResolution.width, + cameraResolution.height); final int rawSize = Math.min(width * 2 / 3, height * 2 / 3); final int frameSize = Math.max(MIN_FRAME_SIZE, Math.min(MAX_FRAME_SIZE, rawSize)); @@ -172,19 +174,10 @@ public void close() { } } - private static final Comparator numPixelComparator = new Comparator() { - @Override - public int compare(final Camera.Size size1, final Camera.Size size2) { - final int pixels1 = size1.height * size1.width; - final int pixels2 = size2.height * size2.width; - - if (pixels1 < pixels2) - return 1; - else if (pixels1 > pixels2) - return -1; - else - return 0; - } + private static final Comparator NUM_PIXEL_COMPARATOR = (size1, size2) -> { + final int pixels1 = size1.height * size1.width; + final int pixels2 = size2.height * size2.width; + return -Integer.compare(pixels1, pixels2); }; private static Camera.Size findBestPreviewSizeValue(final Camera.Parameters parameters, int width, int height) { @@ -202,7 +195,7 @@ private static Camera.Size findBestPreviewSizeValue(final Camera.Parameters para // sort by size, descending final List supportedPreviewSizes = new ArrayList(rawSupportedSizes); - Collections.sort(supportedPreviewSizes, numPixelComparator); + Collections.sort(supportedPreviewSizes, NUM_PIXEL_COMPARATOR); Camera.Size bestSize = null; float diff = Float.POSITIVE_INFINITY; diff --git a/common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java b/common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java index fa4c4d2686..6eece74945 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/ScannerView.java @@ -55,16 +55,17 @@ public class ScannerView extends View { private final Map dots = new HashMap(16); private Rect frame; private final Matrix matrix = new Matrix(); + private final float[] point = new float[2]; public ScannerView(final Context context, final AttributeSet attrs) { super(context, attrs); final Resources res = getResources(); - maskColor = res.getColor(R.color.scan_mask); - maskResultColor = res.getColor(R.color.scan_result_view); - laserColor = res.getColor(R.color.scan_laser); - dotColor = res.getColor(R.color.scan_dot); - dotResultColor = res.getColor(R.color.scan_result_dots); + maskColor = context.getColor(R.color.scan_mask); + maskResultColor = context.getColor(R.color.scan_result_view); + laserColor = context.getColor(R.color.scan_laser); + dotColor = context.getColor(R.color.scan_dot); + dotResultColor = context.getColor(R.color.scan_result_dots); maskPaint = new Paint(); maskPaint.setStyle(Style.FILL); @@ -110,10 +111,8 @@ public void onDraw(final Canvas canvas) { final long now = System.currentTimeMillis(); - final int width = canvas.getWidth(); - final int height = canvas.getHeight(); - - final float[] point = new float[2]; + final int width = getWidth(); + final int height = getHeight(); // draw mask darkened maskPaint.setColor(isResult ? maskResultColor : maskColor); From aab4935b124f283bb65a03686b29eecd85f33a89 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 08:27:43 -0800 Subject: [PATCH 03/60] refactor: use ScanActivity in common module --- .../src/de/schildbach/wallet/ui/SendingAddressesFragment.java | 2 +- wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt | 2 +- .../src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt | 2 +- .../de/schildbach/wallet/ui/payments/SweepWalletFragment.java | 2 +- wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt | 2 +- .../src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java b/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java index 2774a28497..b1b523ee4d 100644 --- a/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java +++ b/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java @@ -29,6 +29,7 @@ import org.bitcoinj.uri.BitcoinURIParseException; import org.bitcoinj.wallet.Wallet; import org.dash.wallet.common.ui.BaseAlertDialogBuilder; +import org.dash.wallet.common.ui.scan.ScanActivity; import org.dash.wallet.common.util.Qr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +39,6 @@ import de.schildbach.wallet.data.AddressBookProvider; import de.schildbach.wallet.data.PaymentIntent; import de.schildbach.wallet.ui.util.InputParser.StringInputParser; -import de.schildbach.wallet.ui.scan.ScanActivity; import de.schildbach.wallet.ui.send.SendCoinsActivity; import de.schildbach.wallet.util.BitmapFragment; import de.schildbach.wallet.util.Toast; diff --git a/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt b/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt index f1f6234bbc..f2692a4485 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt @@ -38,7 +38,6 @@ import de.schildbach.wallet.data.PaymentIntent import de.schildbach.wallet.ui.* import de.schildbach.wallet.ui.payments.PaymentsFragment import de.schildbach.wallet.ui.payments.SweepWalletActivity -import de.schildbach.wallet.ui.scan.ScanActivity import de.schildbach.wallet.ui.send.SendCoinsActivity import de.schildbach.wallet.ui.transactions.TaxCategoryExplainerDialogFragment import de.schildbach.wallet.ui.transactions.TransactionDetailsDialogFragment @@ -56,6 +55,7 @@ import org.dash.wallet.common.Configuration import org.dash.wallet.common.services.AuthenticationManager import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.ui.dialogs.AdaptiveDialog +import org.dash.wallet.common.ui.scan.ScanActivity import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.common.util.safeNavigate import org.slf4j.LoggerFactory diff --git a/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt b/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt index e4f8b7fd4e..7bb8be32ef 100644 --- a/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt @@ -28,7 +28,6 @@ import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint import de.schildbach.wallet.payments.parsers.PaymentIntentParser import de.schildbach.wallet.payments.parsers.PaymentIntentParserException -import de.schildbach.wallet.ui.scan.ScanActivity import de.schildbach.wallet.ui.send.SendCoinsActivity import de.schildbach.wallet_test.R import de.schildbach.wallet_test.databinding.FragmentPaymentsPayBinding @@ -36,6 +35,7 @@ import kotlinx.coroutines.launch import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.ui.dialogs.AdaptiveDialog +import org.dash.wallet.common.ui.scan.ScanActivity import org.dash.wallet.common.ui.viewBinding import javax.inject.Inject diff --git a/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java b/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java index f71fa40dca..5d60c27f90 100644 --- a/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java +++ b/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java @@ -64,6 +64,7 @@ import org.dash.wallet.common.services.LeftoverBalanceException; import org.dash.wallet.common.ui.CurrencyTextView; import org.dash.wallet.common.ui.dialogs.AdaptiveDialog; +import org.dash.wallet.common.ui.scan.ScanActivity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -88,7 +89,6 @@ import de.schildbach.wallet.payments.SendCoinsOfflineTask; import de.schildbach.wallet.ui.util.InputParser.StringInputParser; import de.schildbach.wallet.ui.transactions.TransactionResultActivity; -import de.schildbach.wallet.ui.scan.ScanActivity; import de.schildbach.wallet.util.WalletUtils; import de.schildbach.wallet_test.R; import kotlin.Unit; diff --git a/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt index 607268a2b9..8937fa0c90 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt @@ -39,11 +39,11 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint import de.schildbach.wallet.payments.parsers.PaymentIntentParser -import de.schildbach.wallet.ui.scan.ScanActivity import de.schildbach.wallet_test.R import de.schildbach.wallet_test.databinding.FragmentAddressInputBinding import kotlinx.coroutines.launch import org.dash.wallet.common.services.analytics.AnalyticsConstants +import org.dash.wallet.common.ui.scan.ScanActivity import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.common.util.KeyboardUtil import org.dash.wallet.common.util.observe diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java index 7d214283b5..283a6e0554 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java @@ -27,13 +27,13 @@ import org.bitcoinj.core.Transaction; import org.bitcoinj.core.VerificationException; import org.dash.wallet.common.ui.dialogs.AdaptiveDialog; +import org.dash.wallet.common.ui.scan.ScanActivity; import de.schildbach.wallet.WalletApplication; import de.schildbach.wallet.data.PaymentIntent; import de.schildbach.wallet.ui.LockScreenActivity; import de.schildbach.wallet.ui.ShortcutComponentActivity; import de.schildbach.wallet.ui.util.InputParser.StringInputParser; -import de.schildbach.wallet.ui.scan.ScanActivity; import de.schildbach.wallet.ui.payments.SweepWalletActivity; import de.schildbach.wallet_test.R; import kotlin.Unit; From c857c40e07f2830ed9d6679a49f018d4068615f8 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 10:29:16 -0800 Subject: [PATCH 04/60] refactor: move scan strings to common --- common/src/main/res/values/strings.xml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index bb45fc1613..aa7f0c7fa1 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -101,4 +101,26 @@ available Not available Log In + + + Sorry + The camera has a problem. You probably need to restart the device. + Camera permission + In order to scan QR codes, you need to grant permission to use the camera. + + + Send Dash + Show content in the clipboard + Tap the address from the clipboard to paste it + Not a valid %s Address or URL request + Recipient Address + + + Cannot read data:\n%s + Cannot recognize input:\n%s + Invalid Dash URI:\n%s + Got invalid Dash address!\n(Mixing up mainnet/testnet?) + Cannot verify payment request:\n%s + Invalid payment request:\n%s + Invalid transaction:\n%s \ No newline at end of file From e05027d6893a384fc1fa7ee4b6eef2c164191fb3 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 10:29:32 -0800 Subject: [PATCH 05/60] fix(maya): update strings for networks --- integrations/maya/src/main/res/values/strings-maya.xml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/integrations/maya/src/main/res/values/strings-maya.xml b/integrations/maya/src/main/res/values/strings-maya.xml index 4199d9fd7c..5653b73ebe 100644 --- a/integrations/maya/src/main/res/values/strings-maya.xml +++ b/integrations/maya/src/main/res/values/strings-maya.xml @@ -31,10 +31,16 @@ USDT Ethereum USD Coin - USDT + Tether + KUJI + Kujira + USK + USK + WSTETH + Wrapped stETH Maya Error The Maya service is not available. Please try again later. Please report this issue to the Dash Wallet support team using "Report Issue" from the main menu. - + \ No newline at end of file From 60bbf853fd8b02887cc1b43c767950effc5ed528 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 10:33:19 -0800 Subject: [PATCH 06/60] refactor: Io and AddressUtil strings to common --- .../dash/wallet/common/util/AddressUtil.java | 56 +++++++++++++++++++ .../java/org/dash/wallet/common}/util/Io.java | 4 +- wallet/proguard.cfg | 2 +- .../payments/parsers/PaymentIntentParser.kt | 6 +- .../wallet/service/WalletFactory.kt | 2 +- .../schildbach/wallet/ui/AddressAndLabel.java | 6 +- .../wallet/ui/util/InputParser.java | 6 +- .../schildbach/wallet/ui/util/WalletUri.java | 6 +- .../schildbach/wallet/util/AddressUtil.java | 41 -------------- .../de/schildbach/wallet/util/CryptoTest.java | 2 +- 10 files changed, 73 insertions(+), 58 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/util/AddressUtil.java rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/util/Io.java (95%) delete mode 100644 wallet/src/de/schildbach/wallet/util/AddressUtil.java diff --git a/common/src/main/java/org/dash/wallet/common/util/AddressUtil.java b/common/src/main/java/org/dash/wallet/common/util/AddressUtil.java new file mode 100644 index 0000000000..3562989e7f --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/util/AddressUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.util; + +import org.bitcoinj.core.Address; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.params.TestNet3Params; +import org.bitcoinj.uri.BitcoinURI; + +public class AddressUtil { + + public static NetworkParameters getParametersFromAddress(String address, NetworkParameters currentNetworkParameters) throws AddressFormatException { + NetworkParameters networkParameters = Address.getParametersFromAddress(address); + if (networkParameters.equals(TestNet3Params.get())) { + return currentNetworkParameters; + } else { + return networkParameters; + } + } + + public static Address fromString(NetworkParameters params, String base58, NetworkParameters currentNetworkParameters) throws AddressFormatException { + NetworkParameters networkParameters = (params != null) ? params : getParametersFromAddress(base58, currentNetworkParameters); + return Address.fromString(networkParameters, base58); + } + + public static Address getCorrectAddress(BitcoinURI bitcoinUri, NetworkParameters currentNetworkParameters) { + Address address = bitcoinUri.getAddress(); + if (address != null) { + NetworkParameters networkParameters = address.getParameters(); + if (networkParameters.equals(TestNet3Params.get()) && !currentNetworkParameters.equals(TestNet3Params.get())) { + try { + return Address.fromString(currentNetworkParameters, address.toString()); + } catch (AddressFormatException.WrongNetwork x) { + return address; + } + } + } + return address; + } +} diff --git a/wallet/src/de/schildbach/wallet/util/Io.java b/common/src/main/java/org/dash/wallet/common/util/Io.java similarity index 95% rename from wallet/src/de/schildbach/wallet/util/Io.java rename to common/src/main/java/org/dash/wallet/common/util/Io.java index cba53b8816..21823a002e 100644 --- a/wallet/src/de/schildbach/wallet/util/Io.java +++ b/common/src/main/java/org/dash/wallet/common/util/Io.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.util; +package org.dash.wallet.common.util; import java.io.IOException; import java.io.InputStream; diff --git a/wallet/proguard.cfg b/wallet/proguard.cfg index 0c13d3d24f..005c34668a 100644 --- a/wallet/proguard.cfg +++ b/wallet/proguard.cfg @@ -94,7 +94,7 @@ -dontnote ch.qos.logback.core.rolling.helper.FileStoreUtil # Bitcoin Wallet --dontnote de.schildbach.wallet.util.Io +-dontnote org.dash.wallet.common.util.Io -keepattributes InnerClasses diff --git a/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt b/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt index 63d923bbb2..f8c1856845 100644 --- a/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt +++ b/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt @@ -22,8 +22,6 @@ import com.google.protobuf.InvalidProtocolBufferException import com.google.protobuf.UninitializedMessageException import de.schildbach.wallet.Constants import de.schildbach.wallet.data.PaymentIntent -import de.schildbach.wallet.util.AddressUtil -import de.schildbach.wallet.util.Io import de.schildbach.wallet_test.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -39,7 +37,9 @@ import org.bitcoinj.protocols.payments.PaymentProtocolException.InvalidPaymentUR import org.bitcoinj.protocols.payments.PaymentProtocolException.PkiVerificationException import org.bitcoinj.uri.BitcoinURI import org.bitcoinj.uri.BitcoinURIParseException +import org.dash.wallet.common.util.AddressUtil import org.dash.wallet.common.util.Base43 +import org.dash.wallet.common.util.Io import org.dash.wallet.common.util.ResourceString import org.slf4j.LoggerFactory import java.io.ByteArrayOutputStream @@ -86,7 +86,7 @@ object PaymentIntentParser { } else if (inputStr.startsWith(Constants.DASH_SCHEME + ":")) { try { val bitcoinUri = BitcoinURI(null, inputStr) - val address = AddressUtil.getCorrectAddress(bitcoinUri) + val address = AddressUtil.getCorrectAddress(bitcoinUri, Constants.NETWORK_PARAMETERS) if (address != null && Constants.NETWORK_PARAMETERS != address.parameters) { throw BitcoinURIParseException("mismatched network") diff --git a/wallet/src/de/schildbach/wallet/service/WalletFactory.kt b/wallet/src/de/schildbach/wallet/service/WalletFactory.kt index 0ef4108f1f..4dc040f0e8 100644 --- a/wallet/src/de/schildbach/wallet/service/WalletFactory.kt +++ b/wallet/src/de/schildbach/wallet/service/WalletFactory.kt @@ -24,7 +24,6 @@ import com.google.common.base.Preconditions import de.schildbach.wallet.Constants import de.schildbach.wallet.WalletApplication import de.schildbach.wallet.util.Crypto -import de.schildbach.wallet.util.Io import de.schildbach.wallet.util.Iso8601Format import org.bitcoinj.core.AddressFormatException import org.bitcoinj.core.DumpedPrivateKey @@ -39,6 +38,7 @@ import org.bitcoinj.wallet.Wallet import org.bitcoinj.wallet.WalletExtension import org.bitcoinj.wallet.WalletProtobufSerializer import org.bitcoinj.wallet.authentication.AuthenticationGroupExtension +import org.dash.wallet.common.util.Io import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.BufferedReader diff --git a/wallet/src/de/schildbach/wallet/ui/AddressAndLabel.java b/wallet/src/de/schildbach/wallet/ui/AddressAndLabel.java index b0669aa073..7dc8dfb43f 100644 --- a/wallet/src/de/schildbach/wallet/ui/AddressAndLabel.java +++ b/wallet/src/de/schildbach/wallet/ui/AddressAndLabel.java @@ -22,11 +22,11 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.NetworkParameters; +import org.dash.wallet.common.util.AddressUtil; import com.google.common.base.Objects; import de.schildbach.wallet.Constants; -import de.schildbach.wallet.util.AddressUtil; import android.os.Parcel; import android.os.Parcelable; @@ -45,7 +45,7 @@ public AddressAndLabel(final Address address, @Nullable final String label) { public AddressAndLabel(final NetworkParameters addressParams, final String address, @Nullable final String label) throws AddressFormatException.WrongNetwork { - this(AddressUtil.fromString(addressParams, address), label); + this(AddressUtil.fromString(addressParams, address, Constants.NETWORK_PARAMETERS), label); } @Override @@ -87,7 +87,7 @@ public AddressAndLabel[] newArray(final int size) { }; private AddressAndLabel(final Parcel in) { - address = AddressUtil.fromString(Constants.NETWORK_PARAMETERS, in.readString()); + address = AddressUtil.fromString(Constants.NETWORK_PARAMETERS, in.readString(), Constants.NETWORK_PARAMETERS); label = in.readString(); } } diff --git a/wallet/src/de/schildbach/wallet/ui/util/InputParser.java b/wallet/src/de/schildbach/wallet/ui/util/InputParser.java index ea10107e3b..996c665ca3 100644 --- a/wallet/src/de/schildbach/wallet/ui/util/InputParser.java +++ b/wallet/src/de/schildbach/wallet/ui/util/InputParser.java @@ -41,7 +41,9 @@ import org.bitcoinj.protocols.payments.PaymentSession; import org.bitcoinj.uri.BitcoinURI; import org.bitcoinj.uri.BitcoinURIParseException; +import org.dash.wallet.common.util.AddressUtil; import org.dash.wallet.common.util.Base43; +import org.dash.wallet.common.util.Io; import org.dash.wallet.common.util.Qr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,8 +62,6 @@ import de.schildbach.wallet.Constants; import de.schildbach.wallet.data.PaymentIntent; import de.schildbach.wallet.ui.send.SendCoinsActivity; -import de.schildbach.wallet.util.AddressUtil; -import de.schildbach.wallet.util.Io; import de.schildbach.wallet_test.R; /** @@ -108,7 +108,7 @@ public void parse() { } else if (input.startsWith(Constants.DASH_SCHEME + ":")) { try { final BitcoinURI bitcoinUri = new BitcoinURI(null, input); - final Address address = AddressUtil.getCorrectAddress(bitcoinUri); + final Address address = AddressUtil.getCorrectAddress(bitcoinUri, Constants.NETWORK_PARAMETERS); if (address != null && !Constants.NETWORK_PARAMETERS.equals(address.getParameters())) throw new BitcoinURIParseException("mismatched network"); diff --git a/wallet/src/de/schildbach/wallet/ui/util/WalletUri.java b/wallet/src/de/schildbach/wallet/ui/util/WalletUri.java index 4441f435a1..3abac6cf35 100644 --- a/wallet/src/de/schildbach/wallet/ui/util/WalletUri.java +++ b/wallet/src/de/schildbach/wallet/ui/util/WalletUri.java @@ -24,9 +24,9 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.uri.BitcoinURI; import org.bitcoinj.uri.BitcoinURIParseException; +import org.dash.wallet.common.util.AddressUtil; import de.schildbach.wallet.Constants; -import de.schildbach.wallet.util.AddressUtil; public class WalletUri { @@ -70,7 +70,7 @@ public static WalletUri parse(Uri input) throws BitcoinURIParseException { String rawAddress = input.getQueryParameter(FIELD_PAY); Address address; try { - address = AddressUtil.fromString(null, rawAddress); + address = AddressUtil.fromString(null, rawAddress, Constants.NETWORK_PARAMETERS); } catch (Exception e) { throw new BitcoinURIParseException(e.getMessage()); } @@ -120,7 +120,7 @@ public boolean isAddressRequest() { public Address getPayAddress() { String pay = sourceUri.getQueryParameter(FIELD_PAY); - return (pay != null) ? AddressUtil.fromString(null, pay) : null; + return (pay != null) ? AddressUtil.fromString(null, pay, Constants.NETWORK_PARAMETERS) : null; } public Coin getAmount() { diff --git a/wallet/src/de/schildbach/wallet/util/AddressUtil.java b/wallet/src/de/schildbach/wallet/util/AddressUtil.java deleted file mode 100644 index 7f7b528f18..0000000000 --- a/wallet/src/de/schildbach/wallet/util/AddressUtil.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.schildbach.wallet.util; - -import org.bitcoinj.core.Address; -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.params.TestNet3Params; -import org.bitcoinj.uri.BitcoinURI; - -import de.schildbach.wallet.Constants; - -public class AddressUtil { - - public static NetworkParameters getParametersFromAddress(String address) throws AddressFormatException { - NetworkParameters networkParameters = Address.getParametersFromAddress(address); - if (networkParameters.equals(TestNet3Params.get())) { - return Constants.NETWORK_PARAMETERS; - } else { - return networkParameters; - } - } - - public static Address fromString(NetworkParameters params, String base58) throws AddressFormatException { - NetworkParameters networkParameters = (params != null) ? params : getParametersFromAddress(base58); - return Address.fromString(networkParameters, base58); - } - - public static Address getCorrectAddress(BitcoinURI bitcoinUri) { - Address address = bitcoinUri.getAddress(); - if (address != null) { - NetworkParameters networkParameters = address.getParameters(); - if (networkParameters.equals(TestNet3Params.get()) && !Constants.NETWORK_PARAMETERS.equals(TestNet3Params.get())) { - try { - return Address.fromString(Constants.NETWORK_PARAMETERS, address.toString()); - } catch (AddressFormatException.WrongNetwork x) { - return address; - } - } - } - return address; - } -} diff --git a/wallet/test/de/schildbach/wallet/util/CryptoTest.java b/wallet/test/de/schildbach/wallet/util/CryptoTest.java index d61ebf679c..669b3440b1 100644 --- a/wallet/test/de/schildbach/wallet/util/CryptoTest.java +++ b/wallet/test/de/schildbach/wallet/util/CryptoTest.java @@ -24,11 +24,11 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import org.bitcoinj.wallet.WalletProtobufSerializer; +import org.dash.wallet.common.util.Io; import org.junit.Test; import com.google.common.base.Charsets; From 3dc3cc3a462650d95f7b0bb95bf94b3e74012d5f Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 11:02:50 -0800 Subject: [PATCH 07/60] refactor: move Bluetooth and PaymentIntent to common --- .../wallet/common}/data/PaymentIntent.java | 43 +++++++++++-------- .../dash/wallet/common}/util/Bluetooth.java | 5 +-- .../org/dash/wallet/common/util/Constants.kt | 4 ++ wallet/res/navigation/nav_send.xml | 4 +- .../wallet/offline/AcceptBluetoothThread.java | 2 +- .../wallet/offline/DirectPaymentTask.java | 2 +- .../payments/RequestPaymentRequestTask.java | 2 +- .../wallet/payments/SendCoinsTaskRunner.kt | 7 +-- .../payments/parsers/PaymentIntentParser.kt | 2 +- .../wallet/ui/ImportSharedImageActivity.kt | 2 +- .../wallet/ui/SendingAddressesFragment.java | 6 +-- .../wallet/ui/WalletUriHandlerActivity.java | 2 +- .../wallet/ui/main/WalletActivity.java | 2 +- .../wallet/ui/main/WalletFragment.kt | 2 +- .../ui/payments/SweepWalletFragment.java | 4 +- .../wallet/ui/send/PaymentProtocolFragment.kt | 2 +- .../ui/send/PaymentProtocolViewModel.kt | 4 +- .../wallet/ui/send/SendCoinsActivity.kt | 2 +- .../wallet/ui/send/SendCoinsBaseViewModel.kt | 6 +-- .../wallet/ui/send/SendCoinsFragment.kt | 3 +- .../wallet/ui/send/SendCoinsQrActivity.java | 2 +- .../wallet/ui/send/SendCoinsViewModel.kt | 2 +- .../wallet/ui/util/InputParser.java | 4 +- .../schildbach/wallet/util/CrashReporter.java | 1 + .../schildbach/wallet/util/BluetoothTest.java | 1 + 25 files changed, 65 insertions(+), 51 deletions(-) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/data/PaymentIntent.java (93%) rename {wallet/src/de/schildbach/wallet => common/src/main/java/org/dash/wallet/common}/util/Bluetooth.java (96%) diff --git a/wallet/src/de/schildbach/wallet/data/PaymentIntent.java b/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java similarity index 93% rename from wallet/src/de/schildbach/wallet/data/PaymentIntent.java rename to common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java index a1e0f0b5f8..d52619cd5b 100644 --- a/wallet/src/de/schildbach/wallet/data/PaymentIntent.java +++ b/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.data; +package org.dash.wallet.common.data; import static com.google.common.base.Preconditions.checkArgument; @@ -27,6 +27,8 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.params.MainNetParams; import org.bitcoinj.script.ScriptException; import org.bitcoinj.core.Transaction; import org.bitcoinj.protocols.payments.PaymentProtocol; @@ -36,18 +38,20 @@ import org.bitcoinj.script.ScriptPattern; import org.bitcoinj.uri.BitcoinURI; import org.bitcoinj.wallet.SendRequest; +import org.dash.wallet.common.util.Bluetooth; import org.dash.wallet.common.util.GenericUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.io.BaseEncoding; +import org.dash.wallet.common.util.Constants; -import de.schildbach.wallet.Constants; -import de.schildbach.wallet.util.Bluetooth; import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.NonNull; + /** * @author Andreas Schildbach */ @@ -80,8 +84,13 @@ public boolean hasAmount() { return amount != null && amount.signum() != 0; } + @NonNull @Override public String toString() { + return toString(MainNetParams.get()); + } + + public String toString(NetworkParameters params) { final StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName()); @@ -89,7 +98,7 @@ public String toString() { builder.append(hasAmount() ? amount.toPlainString() : "null"); builder.append(','); if (ScriptPattern.isP2PKH(script) || ScriptPattern.isP2SH(script)) - builder.append(script.getToAddress(Constants.NETWORK_PARAMETERS)); + builder.append(script.getToAddress(params)); else if (ScriptPattern.isP2PK(script)) builder.append(Constants.HEX.encode(ScriptPattern.extractKeyFromP2PK(script))); else if (ScriptPattern.isSentToMultisig(script)) @@ -223,15 +232,15 @@ public static PaymentIntent fromAddress(final Address address, @Nullable final S return new PaymentIntent(address, addressLabel); } - public static PaymentIntent fromAddress(final String address, @Nullable final String addressLabel) + public static PaymentIntent fromAddress(final String address, @Nullable final String addressLabel, NetworkParameters params) throws AddressFormatException { - return new PaymentIntent(Address.fromString(Constants.NETWORK_PARAMETERS, address), addressLabel); + return new PaymentIntent(Address.fromString(params, address), addressLabel); } public static PaymentIntent from(final String address, @Nullable final String addressLabel, - @Nullable final Coin amount) throws AddressFormatException { + @Nullable final Coin amount, NetworkParameters params) throws AddressFormatException { return new PaymentIntent(null, null, null, - buildSimplePayTo(amount, Address.fromString(Constants.NETWORK_PARAMETERS, address)), addressLabel, null, + buildSimplePayTo(amount, Address.fromString(params, address)), addressLabel, null, null, null, null); } @@ -283,8 +292,8 @@ public PaymentIntent mergeWithEditedValues(@Nullable final Coin editedAmount, return new PaymentIntent(standard, payeeName, payeeVerifiedBy, outputs, memo, null, payeeData, null, null, useInstantX); } - public SendRequest toSendRequest() { - final Transaction transaction = new Transaction(Constants.NETWORK_PARAMETERS); + public SendRequest toSendRequest(NetworkParameters params) { + final Transaction transaction = new Transaction(params); for (final PaymentIntent.Output output : outputs) transaction.addOutput(output.amount, output.script); return SendRequest.forTx(transaction); @@ -310,12 +319,12 @@ public boolean hasAddress() { return script.isSentToAddress() || script.isPayToScriptHash() || script.isSentToRawPubKey(); } - public Address getAddress() { + public Address getAddress(NetworkParameters params) { if (!hasAddress()) throw new IllegalStateException(); final Script script = outputs[0].script; - return script.getToAddress(Constants.NETWORK_PARAMETERS, true); + return script.getToAddress(params, true); } public boolean mayEditAddress() { @@ -393,13 +402,13 @@ public boolean isBluetoothPaymentRequestUrl() { * payment intent that is checked if it extends this one * @return true if it extends */ - public boolean isExtendedBy(final PaymentIntent other, boolean ignoreDetails) { + public boolean isExtendedBy(final PaymentIntent other, boolean ignoreDetails, NetworkParameters params) { // shortcut via hash if (standard == Standard.BIP21 && other.standard == Standard.BIP70) if (paymentRequestHash != null && Arrays.equals(paymentRequestHash, other.paymentRequestHash)) return true; - return ignoreDetails || (equalsAmount(other) && equalsAddress(other)); + return ignoreDetails || (equalsAmount(other) && equalsAddress(other, params)); } public boolean equalsAmount(final PaymentIntent other) { @@ -411,11 +420,11 @@ public boolean equalsAmount(final PaymentIntent other) { return true; } - public boolean equalsAddress(final PaymentIntent other) { + public boolean equalsAddress(final PaymentIntent other, NetworkParameters params) { final boolean hasAddress = hasAddress(); if (hasAddress != other.hasAddress()) return false; - if (hasAddress && !getAddress().equals(other.getAddress())) + if (hasAddress && !getAddress(params).equals(other.getAddress(params))) return false; return true; } diff --git a/wallet/src/de/schildbach/wallet/util/Bluetooth.java b/common/src/main/java/org/dash/wallet/common/util/Bluetooth.java similarity index 96% rename from wallet/src/de/schildbach/wallet/util/Bluetooth.java rename to common/src/main/java/org/dash/wallet/common/util/Bluetooth.java index 185e7b35a6..2543042b51 100644 --- a/wallet/src/de/schildbach/wallet/util/Bluetooth.java +++ b/common/src/main/java/org/dash/wallet/common/util/Bluetooth.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.util; +package org.dash.wallet.common.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -23,7 +23,6 @@ import javax.annotation.Nullable; -import org.dash.wallet.common.util.GenericUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/common/src/main/java/org/dash/wallet/common/util/Constants.kt b/common/src/main/java/org/dash/wallet/common/util/Constants.kt index 471feac04e..50a7677ed1 100644 --- a/common/src/main/java/org/dash/wallet/common/util/Constants.kt +++ b/common/src/main/java/org/dash/wallet/common/util/Constants.kt @@ -17,6 +17,7 @@ package org.dash.wallet.common.util +import com.google.common.io.BaseEncoding import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.bitcoinj.core.Coin @@ -66,4 +67,7 @@ object Constants { HttpLoggingInterceptor { log.debug(it) }.setLevel(HttpLoggingInterceptor.Level.BASIC) ) .build() + + @JvmField + val HEX: BaseEncoding = BaseEncoding.base16().lowerCase() } diff --git a/wallet/res/navigation/nav_send.xml b/wallet/res/navigation/nav_send.xml index 7644097422..5adcbf5aad 100644 --- a/wallet/res/navigation/nav_send.xml +++ b/wallet/res/navigation/nav_send.xml @@ -28,7 +28,7 @@ + app:argType="org.dash.wallet.common.data.PaymentIntent" /> + app:argType="org.dash.wallet.common.data.PaymentIntent" /> \ No newline at end of file diff --git a/wallet/src/de/schildbach/wallet/offline/AcceptBluetoothThread.java b/wallet/src/de/schildbach/wallet/offline/AcceptBluetoothThread.java index da7a7702f5..a8a6fc4f8e 100644 --- a/wallet/src/de/schildbach/wallet/offline/AcceptBluetoothThread.java +++ b/wallet/src/de/schildbach/wallet/offline/AcceptBluetoothThread.java @@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory; import de.schildbach.wallet.Constants; -import de.schildbach.wallet.util.Bluetooth; +import org.dash.wallet.common.util.Bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; diff --git a/wallet/src/de/schildbach/wallet/offline/DirectPaymentTask.java b/wallet/src/de/schildbach/wallet/offline/DirectPaymentTask.java index 4f2b66c781..a5acfd6dcd 100644 --- a/wallet/src/de/schildbach/wallet/offline/DirectPaymentTask.java +++ b/wallet/src/de/schildbach/wallet/offline/DirectPaymentTask.java @@ -31,7 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.schildbach.wallet.util.Bluetooth; +import org.dash.wallet.common.util.Bluetooth; import de.schildbach.wallet_test.R; import android.bluetooth.BluetoothAdapter; diff --git a/wallet/src/de/schildbach/wallet/payments/RequestPaymentRequestTask.java b/wallet/src/de/schildbach/wallet/payments/RequestPaymentRequestTask.java index d4cd96340b..4b927b4d36 100644 --- a/wallet/src/de/schildbach/wallet/payments/RequestPaymentRequestTask.java +++ b/wallet/src/de/schildbach/wallet/payments/RequestPaymentRequestTask.java @@ -30,7 +30,7 @@ import javax.annotation.Nullable; -import de.schildbach.wallet.data.PaymentIntent; +import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet.ui.util.InputParser; import de.schildbach.wallet_test.R; import okhttp3.CacheControl; diff --git a/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt b/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt index 131a064bed..ae27d74c63 100644 --- a/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt +++ b/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt @@ -17,8 +17,9 @@ package de.schildbach.wallet.payments import androidx.annotation.VisibleForTesting +import de.schildbach.wallet.Constants.NETWORK_PARAMETERS import de.schildbach.wallet.WalletApplication -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.payments.parsers.PaymentIntentParser import de.schildbach.wallet.security.SecurityFunctions import de.schildbach.wallet.security.SecurityGuard @@ -142,7 +143,7 @@ class SendCoinsTaskRunner @Inject constructor( val paymentIntent = PaymentIntentParser.parse(byteStream, contentType) - if (!basePaymentIntent.isExtendedBy(paymentIntent, true)) { + if (!basePaymentIntent.isExtendedBy(paymentIntent, true, NETWORK_PARAMETERS)) { log.info("BIP72 trust check failed") throw IllegalStateException("BIP72 trust check failed: $requestUrl") } @@ -236,7 +237,7 @@ class SendCoinsTaskRunner @Inject constructor( val wallet = walletData.wallet ?: throw RuntimeException(WALLET_EXCEPTION_MESSAGE) // to make sure the correct instance of Transaction class is used in toSendRequest() method paymentIntent.setInstantX(false) - val sendRequest = paymentIntent.toSendRequest() + val sendRequest = paymentIntent.toSendRequest(NETWORK_PARAMETERS) sendRequest.coinSelector = ZeroConfCoinSelector.get() sendRequest.useInstantSend = false sendRequest.feePerKb = Constants.ECONOMIC_FEE diff --git a/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt b/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt index f8c1856845..09fbdea6da 100644 --- a/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt +++ b/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt @@ -21,7 +21,7 @@ import com.google.common.hash.Hashing import com.google.protobuf.InvalidProtocolBufferException import com.google.protobuf.UninitializedMessageException import de.schildbach.wallet.Constants -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet_test.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/wallet/src/de/schildbach/wallet/ui/ImportSharedImageActivity.kt b/wallet/src/de/schildbach/wallet/ui/ImportSharedImageActivity.kt index f8e02fd19e..df330ac7e9 100644 --- a/wallet/src/de/schildbach/wallet/ui/ImportSharedImageActivity.kt +++ b/wallet/src/de/schildbach/wallet/ui/ImportSharedImageActivity.kt @@ -26,7 +26,7 @@ import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.google.zxing.* import de.schildbach.wallet.WalletApplication -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.ui.payments.SweepWalletActivity import de.schildbach.wallet.ui.send.SendCoinsActivity import de.schildbach.wallet.ui.util.InputParser.StringInputParser diff --git a/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java b/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java index b1b523ee4d..e76bcb75f5 100644 --- a/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java +++ b/wallet/src/de/schildbach/wallet/ui/SendingAddressesFragment.java @@ -37,7 +37,7 @@ import de.schildbach.wallet.Constants; import de.schildbach.wallet.WalletApplication; import de.schildbach.wallet.data.AddressBookProvider; -import de.schildbach.wallet.data.PaymentIntent; +import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet.ui.util.InputParser.StringInputParser; import de.schildbach.wallet.ui.send.SendCoinsActivity; import de.schildbach.wallet.util.BitmapFragment; @@ -164,7 +164,7 @@ protected void handlePaymentIntent(final PaymentIntent paymentIntent) { @Override public void run() { if (paymentIntent.hasAddress()) { - final Address address = paymentIntent.getAddress(); + final Address address = paymentIntent.getAddress(Constants.NETWORK_PARAMETERS); if (!wallet.isPubKeyHashMine(address.getHash160())) EditAddressBookEntryFragment.edit(getFragmentManager(), address); else { @@ -330,7 +330,7 @@ private String getLabel(final int position) { } private void handleSend(final String address) { - SendCoinsActivity.Companion.start(activity, PaymentIntent.fromAddress(address, null)); + SendCoinsActivity.Companion.start(activity, PaymentIntent.fromAddress(address, null, Constants.NETWORK_PARAMETERS)); } private void handleRemove(final String address) { diff --git a/wallet/src/de/schildbach/wallet/ui/WalletUriHandlerActivity.java b/wallet/src/de/schildbach/wallet/ui/WalletUriHandlerActivity.java index 5b2362444e..5dc8c48aee 100644 --- a/wallet/src/de/schildbach/wallet/ui/WalletUriHandlerActivity.java +++ b/wallet/src/de/schildbach/wallet/ui/WalletUriHandlerActivity.java @@ -36,7 +36,7 @@ import de.schildbach.wallet.Constants; import de.schildbach.wallet.WalletApplication; -import de.schildbach.wallet.data.PaymentIntent; +import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet.integration.android.BitcoinIntegration; import de.schildbach.wallet.ui.main.WalletActivity; import de.schildbach.wallet.ui.send.SendCoinsActivity; diff --git a/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java b/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java index e9589e8fb5..50a237b3cc 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java +++ b/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java @@ -43,7 +43,7 @@ import dagger.hilt.android.AndroidEntryPoint; import de.schildbach.wallet.Constants; -import de.schildbach.wallet.data.PaymentIntent; +import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet.ui.AbstractBindServiceActivity; import de.schildbach.wallet.ui.EncryptKeysDialogFragment; import de.schildbach.wallet.ui.EncryptNewKeyChainDialogFragment; diff --git a/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt b/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt index f2692a4485..4e3730c9c0 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/main/WalletFragment.kt @@ -34,7 +34,7 @@ import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback import com.google.android.material.transition.MaterialFadeThrough import dagger.hilt.android.AndroidEntryPoint -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.ui.* import de.schildbach.wallet.ui.payments.PaymentsFragment import de.schildbach.wallet.ui.payments.SweepWalletActivity diff --git a/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java b/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java index 5d60c27f90..295cec42a7 100644 --- a/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java +++ b/wallet/src/de/schildbach/wallet/ui/payments/SweepWalletFragment.java @@ -60,7 +60,6 @@ import org.bitcoinj.wallet.Wallet.BalanceType; import org.bitcoinj.wallet.WalletTransaction; import org.dash.wallet.common.Configuration; -import org.dash.wallet.common.services.ExchangeRatesProvider; import org.dash.wallet.common.services.LeftoverBalanceException; import org.dash.wallet.common.ui.CurrencyTextView; import org.dash.wallet.common.ui.dialogs.AdaptiveDialog; @@ -75,13 +74,12 @@ import java.util.TreeSet; import javax.annotation.Nullable; -import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; import de.schildbach.wallet.Constants; import de.schildbach.wallet.WalletApplication; import de.schildbach.wallet.data.StaticFeeLoader; -import de.schildbach.wallet.data.PaymentIntent; +import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet.payments.DecodePrivateKeyTask; import de.schildbach.wallet.data.FeeCategory; diff --git a/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolFragment.kt index 1e363a22fb..1b9154d312 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolFragment.kt @@ -25,7 +25,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.livedata.Status import de.schildbach.wallet.ui.transactions.TransactionResultActivity import de.schildbach.wallet_test.R diff --git a/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt b/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt index bc928424c8..9ad70d4dca 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/PaymentProtocolViewModel.kt @@ -27,7 +27,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import de.schildbach.wallet.Constants import de.schildbach.wallet.WalletApplication -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.livedata.Resource import de.schildbach.wallet.offline.DirectPaymentTask import de.schildbach.wallet.offline.DirectPaymentTask.HttpPaymentTask @@ -145,7 +145,7 @@ class PaymentProtocolViewModel @Inject constructor( val requestCallback = object : RequestPaymentRequestTask.ResultCallback { override fun onPaymentIntent(paymentIntent: PaymentIntent) { - if (basePaymentIntent.isExtendedBy(paymentIntent, true)) { + if (basePaymentIntent.isExtendedBy(paymentIntent, true, Constants.NETWORK_PARAMETERS)) { finalPaymentIntent = paymentIntent createBaseSendRequest(paymentIntent) } else { diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt index f9ae4c697e..6a74f8122a 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt @@ -30,7 +30,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment import dagger.hilt.android.AndroidEntryPoint import de.schildbach.wallet.Constants -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.integration.android.BitcoinIntegration import de.schildbach.wallet.payments.parsers.PaymentIntentParser import de.schildbach.wallet.payments.parsers.PaymentIntentParserException diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt index 03f5674cef..8c6dbc18fe 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsBaseViewModel.kt @@ -19,12 +19,12 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import de.schildbach.wallet.data.PaymentIntent +import de.schildbach.wallet.Constants +import org.dash.wallet.common.data.PaymentIntent import org.bitcoinj.utils.MonetaryFormat import org.bitcoinj.wallet.SendRequest import org.dash.wallet.common.Configuration import org.dash.wallet.common.WalletDataProvider -import org.dash.wallet.common.util.Constants import javax.inject.Inject @HiltViewModel @@ -47,7 +47,7 @@ open class SendCoinsBaseViewModel @Inject constructor( basePaymentIntent = paymentIntent if (paymentIntent.hasAddress()) { // avoid the exception for a missing address in a BIP70 payment request - _address.value = paymentIntent.address.toBase58() + _address.value = paymentIntent.getAddress(Constants.NETWORK_PARAMETERS).toBase58() } } diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.kt index ecf22f22a1..dfe388732c 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsFragment.kt @@ -28,6 +28,7 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint +import de.schildbach.wallet.Constants import de.schildbach.wallet.integration.android.BitcoinIntegration import de.schildbach.wallet.ui.LockScreenActivity import de.schildbach.wallet.ui.transactions.TransactionResultActivity @@ -240,7 +241,7 @@ class SendCoinsFragment: Fragment(R.layout.send_coins_fragment) { private suspend fun showPaymentConfirmation() { val dryRunRequest = viewModel.dryrunSendRequest ?: return - val address = viewModel.basePaymentIntent.address?.toBase58() ?: return + val address = viewModel.basePaymentIntent.getAddress(Constants.NETWORK_PARAMETERS)?.toBase58() ?: return val txFee = dryRunRequest.tx.fee val amount: Coin? diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java index 283a6e0554..a7a49646ce 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsQrActivity.java @@ -30,7 +30,7 @@ import org.dash.wallet.common.ui.scan.ScanActivity; import de.schildbach.wallet.WalletApplication; -import de.schildbach.wallet.data.PaymentIntent; +import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet.ui.LockScreenActivity; import de.schildbach.wallet.ui.ShortcutComponentActivity; import de.schildbach.wallet.ui.util.InputParser.StringInputParser; diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt index 0f4a1c3474..2ab9b14e39 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsViewModel.kt @@ -21,7 +21,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import de.schildbach.wallet.WalletApplication -import de.schildbach.wallet.data.PaymentIntent +import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.database.dao.BlockchainStateDao import de.schildbach.wallet.payments.MaxOutputAmountCoinSelector import de.schildbach.wallet.payments.SendCoinsTaskRunner diff --git a/wallet/src/de/schildbach/wallet/ui/util/InputParser.java b/wallet/src/de/schildbach/wallet/ui/util/InputParser.java index 996c665ca3..7bc8ac731f 100644 --- a/wallet/src/de/schildbach/wallet/ui/util/InputParser.java +++ b/wallet/src/de/schildbach/wallet/ui/util/InputParser.java @@ -60,8 +60,8 @@ import java.util.regex.Pattern; import de.schildbach.wallet.Constants; -import de.schildbach.wallet.data.PaymentIntent; -import de.schildbach.wallet.ui.send.SendCoinsActivity; +import org.dash.wallet.common.data.PaymentIntent; + import de.schildbach.wallet_test.R; /** diff --git a/wallet/src/de/schildbach/wallet/util/CrashReporter.java b/wallet/src/de/schildbach/wallet/util/CrashReporter.java index 5871581773..1fa7a978c7 100644 --- a/wallet/src/de/schildbach/wallet/util/CrashReporter.java +++ b/wallet/src/de/schildbach/wallet/util/CrashReporter.java @@ -38,6 +38,7 @@ import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.wallet.Wallet; +import org.dash.wallet.common.util.Bluetooth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/wallet/test/de/schildbach/wallet/util/BluetoothTest.java b/wallet/test/de/schildbach/wallet/util/BluetoothTest.java index 3e413c9b5f..8fe021b1e4 100644 --- a/wallet/test/de/schildbach/wallet/util/BluetoothTest.java +++ b/wallet/test/de/schildbach/wallet/util/BluetoothTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.dash.wallet.common.util.Bluetooth; import org.junit.Test; /** From e02c771b8df1d2543519139bd48f13c494a9e142 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 11:18:07 -0800 Subject: [PATCH 08/60] refactor: fix AndroidManifest.xml for ScanActivity --- wallet/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/AndroidManifest.xml b/wallet/AndroidManifest.xml index 88528a7027..13797bea70 100644 --- a/wallet/AndroidManifest.xml +++ b/wallet/AndroidManifest.xml @@ -173,7 +173,7 @@ android:label="@string/block_info" android:theme="@style/LockScreenActivity.Child.Theme" /> Date: Tue, 16 Jan 2024 11:19:10 -0800 Subject: [PATCH 09/60] refactor: fix AndroidManifest.xml for ScanActivity --- .../common/payments/parsers/AddressParser.kt | 74 +++++++++++++++ .../payments/parsers/BitcoinMainNetParams.kt | 27 ++++++ .../parsers/BitcoinPaymentIntentParser.kt | 93 +++++++++++++++++++ .../parsers/DashPaymentIntentParser.kt | 24 ++--- .../payments/parsers/PaymentIntentParser.kt | 24 +++++ .../parsers/PaymentIntentParserException.kt | 25 +++++ .../payments/parsers/PaymentIntentParsers.kt | 34 +++++++ .../org/dash/wallet/common/util/Constants.kt | 8 +- wallet/res/values/strings.xml | 9 -- .../src/de/schildbach/wallet/Constants.java | 2 - .../wallet/payments/SendCoinsTaskRunner.kt | 8 +- .../wallet/payments/parsers/AddressParser.kt | 50 ---------- .../wallet/ui/payments/PaymentsPayFragment.kt | 7 +- .../wallet/ui/send/AddressInputFragment.kt | 5 +- .../wallet/ui/send/AddressInputViewModel.kt | 5 +- .../wallet/ui/send/SendCoinsActivity.kt | 10 +- .../wallet/ui/util/InputParser.java | 15 ++- 17 files changed, 327 insertions(+), 93 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt rename wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt => common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt (94%) create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParserException.kt create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt delete mode 100644 wallet/src/de/schildbach/wallet/payments/parsers/AddressParser.kt diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt new file mode 100644 index 0000000000..c27d3eff57 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.bitcoinj.core.Address +import org.bitcoinj.core.Base58 +import org.bitcoinj.core.NetworkParameters + +class AddressParser(pattern: String, val params: NetworkParameters?) { + companion object { + private val PATTERN_BITCOIN_ADDRESS = "[${Base58.ALPHABET.joinToString(separator = "")}]{20,40}" + private val PATTERN_ETHEREUM_ADDRESS = "0x[a-fA-F0-9]{40}" + fun getDashAddressParser(params: NetworkParameters): AddressParser { + return AddressParser(PATTERN_BITCOIN_ADDRESS, params) + } + + fun getBitcoinAddressParser(): AddressParser { + return AddressParser(PATTERN_BITCOIN_ADDRESS, BitcoinMainNetParams()) + } + + fun getEthereumAddressParser(): AddressParser { + return AddressParser(PATTERN_ETHEREUM_ADDRESS, null) + } + + fun get(currency: String, params: NetworkParameters? = null): AddressParser { + return when (currency) { + "bitcoin" -> getBitcoinAddressParser() + "dash" -> getDashAddressParser(params!!) + else -> getEthereumAddressParser() + } + } + } + + private val addressPattern = Regex(pattern) + + fun exactMatch(inputText: String): Boolean { + return addressPattern.matches(inputText) + } + + fun findAll(inputText: String): List { + val matches = addressPattern.findAll(inputText) + val validRanges = mutableListOf() + + for (match in matches) { + val addressCandidate = match.value + + try { + params?.let { Address.fromString(params, addressCandidate) } + val startIndex = match.range.first + val endIndex = match.range.last + 1 + validRanges.add(startIndex..endIndex) + } catch (e: Exception) { + // Invalid address, skipping + } + } + + return validRanges + } +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt new file mode 100644 index 0000000000..9181970bd9 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.bitcoinj.params.MainNetParams + +class BitcoinMainNetParams : MainNetParams() { + init { + addressHeader = 1 + p2shHeader = 3 + } +} \ No newline at end of file diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt new file mode 100644 index 0000000000..d7bd5bb992 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.bitcoinj.core.Address +import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.NetworkParameters +import org.bitcoinj.params.MainNetParams +import org.bitcoinj.uri.BitcoinURI +import org.bitcoinj.uri.BitcoinURIParseException +import org.dash.wallet.common.R +import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.util.ResourceString +import org.slf4j.LoggerFactory + +class BitcoinPaymentIntentParser : PaymentIntentParser("bitcoin", BitcoinMainNetParams()) { + private val log = LoggerFactory.getLogger(BitcoinPaymentIntentParser::class.java) + private val addressParser = AddressParser.getBitcoinAddressParser() + + override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { + var inputStr = input + +// if (supportAnypayUrls) { +// // replaces Anypay scheme with the Dash one +// // ie "pay:?r=https://(...)" become "dash:?r=https://(...)" +// if (input.startsWith(Constants.ANYPAY_SCHEME + ":")) { +// inputStr = input.replaceFirst(Constants.ANYPAY_SCHEME.toRegex(), Constants.DASH_SCHEME) +// } +// } + + if (inputStr.startsWith("$currency:") || inputStr.startsWith("${currency.uppercase()}:")) { + try { + val bitcoinUri = BitcoinURI(null, inputStr) + val address = bitcoinUri.address; // AddressUtil.getCorrectAddress(bitcoinUri) + + if (address != null && params != null && params != address.parameters) { + throw BitcoinURIParseException("mismatched network") + } + + return@withContext PaymentIntent.fromBitcoinUri(bitcoinUri) + } catch (ex: BitcoinURIParseException) { + log.info("got invalid bitcoin uri: '$inputStr'", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf(inputStr) + ) + ) + } + } else if (addressParser.exactMatch(inputStr)) { + try { + val address = Address.fromString(params, inputStr) + return@withContext PaymentIntent.fromAddress(address, null) + } catch (ex: AddressFormatException) { + log.info("got invalid address", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf() + ) + ) + } + } + + log.info("cannot classify: '{}'", input) + throw PaymentIntentParserException( + IllegalArgumentException(input), + ResourceString( + R.string.error, + listOf(input) + ) + ) + } +} diff --git a/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt similarity index 94% rename from wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt rename to common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt index 09fbdea6da..c6f7fd842f 100644 --- a/wallet/src/de/schildbach/wallet/payments/parsers/PaymentIntentParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Dash Core Group. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,19 +15,17 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.payments.parsers +package org.dash.wallet.common.payments.parsers import com.google.common.hash.Hashing import com.google.protobuf.InvalidProtocolBufferException import com.google.protobuf.UninitializedMessageException -import de.schildbach.wallet.Constants -import org.dash.wallet.common.data.PaymentIntent -import de.schildbach.wallet_test.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.bitcoin.protocols.payments.Protos.PaymentRequest import org.bitcoinj.core.Address import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.NetworkParameters import org.bitcoinj.crypto.TrustStoreLoader.DefaultTrustStoreLoader import org.bitcoinj.protocols.payments.PaymentProtocol import org.bitcoinj.protocols.payments.PaymentProtocolException @@ -37,8 +35,11 @@ import org.bitcoinj.protocols.payments.PaymentProtocolException.InvalidPaymentUR import org.bitcoinj.protocols.payments.PaymentProtocolException.PkiVerificationException import org.bitcoinj.uri.BitcoinURI import org.bitcoinj.uri.BitcoinURIParseException +import org.dash.wallet.common.R +import org.dash.wallet.common.data.PaymentIntent import org.dash.wallet.common.util.AddressUtil import org.dash.wallet.common.util.Base43 +import org.dash.wallet.common.util.Constants import org.dash.wallet.common.util.Io import org.dash.wallet.common.util.ResourceString import org.slf4j.LoggerFactory @@ -49,14 +50,13 @@ import java.io.InputStream import java.security.KeyStoreException import java.util.* -class PaymentIntentParserException( - innerException: Exception, - val localizedMessage: ResourceString -) : Exception(innerException) - -object PaymentIntentParser { +class DashPaymentIntentParser(params: NetworkParameters) : PaymentIntentParser("dash", params) { private val log = LoggerFactory.getLogger(PaymentIntentParser::class.java) + private val addressParser = AddressParser.getDashAddressParser(params) + override suspend fun parse(input: String): PaymentIntent { + return parse(input, true) + } suspend fun parse(input: String, supportAnypayUrls: Boolean): PaymentIntent = withContext(Dispatchers.Default) { var inputStr = input @@ -103,7 +103,7 @@ object PaymentIntentParser { ) ) } - } else if (AddressParser.exactMatch(inputStr)) { + } else if (addressParser.exactMatch(inputStr)) { try { val address = Address.fromString(Constants.NETWORK_PARAMETERS, inputStr) return@withContext PaymentIntent.fromAddress(address, null) diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt new file mode 100644 index 0000000000..9b2bcbdbbf --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.bitcoinj.core.NetworkParameters +import org.dash.wallet.common.data.PaymentIntent +abstract class PaymentIntentParser(val currency: String, val params: NetworkParameters?) { + abstract suspend fun parse(input: String): PaymentIntent +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParserException.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParserException.kt new file mode 100644 index 0000000000..a145188654 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParserException.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.dash.wallet.common.util.ResourceString + +class PaymentIntentParserException( + innerException: Exception, + val localizedMessage: ResourceString +) : Exception(innerException) diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt new file mode 100644 index 0000000000..9dc98e4351 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +object PaymentIntentParsers { + private val processors = hashMapOf() + + init { + add("bitcoin", BitcoinPaymentIntentParser()) + add("dash", BitcoinPaymentIntentParser()) + } + fun add(currency: String, parser: PaymentIntentParser) { + processors[currency] = parser + } + + fun get(currency: String): PaymentIntentParser? { + return processors[currency.lowercase()] + } +} \ No newline at end of file diff --git a/common/src/main/java/org/dash/wallet/common/util/Constants.kt b/common/src/main/java/org/dash/wallet/common/util/Constants.kt index 50a7677ed1..c50352b555 100644 --- a/common/src/main/java/org/dash/wallet/common/util/Constants.kt +++ b/common/src/main/java/org/dash/wallet/common/util/Constants.kt @@ -21,6 +21,7 @@ import com.google.common.io.BaseEncoding import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.bitcoinj.core.Coin +import org.bitcoinj.core.NetworkParameters import org.bitcoinj.params.MainNetParams import org.bitcoinj.utils.MonetaryFormat import org.slf4j.Logger @@ -67,7 +68,12 @@ object Constants { HttpLoggingInterceptor { log.debug(it) }.setLevel(HttpLoggingInterceptor.Level.BASIC) ) .build() - @JvmField val HEX: BaseEncoding = BaseEncoding.base16().lowerCase() + @JvmField + val NETWORK_PARAMETERS: NetworkParameters = MainNetParams.get() + @JvmField + var ANYPAY_SCHEME = "pay" + @JvmField + var DASH_SCHEME = "dash" } diff --git a/wallet/res/values/strings.xml b/wallet/res/values/strings.xml index 20b5ae1fda..df70fe0348 100644 --- a/wallet/res/values/strings.xml +++ b/wallet/res/values/strings.xml @@ -225,13 +225,6 @@ Encrypting… Decrypting… Done - Cannot read data:\n%s - Cannot recognize input:\n%s - Invalid Dash URI:\n%s - Got invalid Dash address!\n(Mixing up mainnet/testnet?) - Cannot verify payment request:\n%s - Invalid payment request:\n%s - Invalid transaction:\n%s Settings Diagnostics Labs @@ -376,8 +369,6 @@ Convert Dash · No account needed Recipient Address - Show content in the clipboard - Tap the address from the clipboard to paste it Not a valid Dash Address or URL request Send to Address diff --git a/wallet/src/de/schildbach/wallet/Constants.java b/wallet/src/de/schildbach/wallet/Constants.java index 1ca5af5c4a..54cb9cd776 100644 --- a/wallet/src/de/schildbach/wallet/Constants.java +++ b/wallet/src/de/schildbach/wallet/Constants.java @@ -235,8 +235,6 @@ public final static class Files { public static long EARLIEST_HD_SEED_CREATION_TIME = 1427610960L; public static String WALLET_URI_SCHEME = "dashwallet"; - public static String ANYPAY_SCHEME = "pay"; - public static String DASH_SCHEME = "dash"; public static boolean ENABLE_ZERO_FEES = false; //Enable Zero Fee's on TestNet only. diff --git a/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt b/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt index ae27d74c63..6dfd8dd404 100644 --- a/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt +++ b/wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt @@ -20,7 +20,6 @@ import androidx.annotation.VisibleForTesting import de.schildbach.wallet.Constants.NETWORK_PARAMETERS import de.schildbach.wallet.WalletApplication import org.dash.wallet.common.data.PaymentIntent -import de.schildbach.wallet.payments.parsers.PaymentIntentParser import de.schildbach.wallet.security.SecurityFunctions import de.schildbach.wallet.security.SecurityGuard import de.schildbach.wallet.service.PackageInfoProvider @@ -42,6 +41,7 @@ import org.bitcoinj.protocols.payments.PaymentProtocolException.InvalidPaymentRe import org.bitcoinj.script.ScriptException import org.bitcoinj.wallet.* import org.dash.wallet.common.WalletDataProvider +import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser import org.dash.wallet.common.services.DirectPayException import org.dash.wallet.common.services.LeftoverBalanceException import org.dash.wallet.common.services.SendPaymentService @@ -63,6 +63,8 @@ class SendCoinsTaskRunner @Inject constructor( private val log = LoggerFactory.getLogger(SendCoinsTaskRunner::class.java) } + private val paymentIntentParser = DashPaymentIntentParser(Constants.NETWORK_PARAMETERS) + @Throws(LeftoverBalanceException::class) override suspend fun sendCoins( address: Address, @@ -121,7 +123,7 @@ class SendCoinsTaskRunner @Inject constructor( override suspend fun payWithDashUrl(dashUri: String): Transaction { return withContext(Dispatchers.IO) { - val paymentIntent = PaymentIntentParser.parse(dashUri, false) + val paymentIntent = paymentIntentParser.parse(dashUri, false) createPaymentRequest(paymentIntent) } } @@ -141,7 +143,7 @@ class SendCoinsTaskRunner @Inject constructor( throw IOException("Null response for the payment request: $requestUrl") } - val paymentIntent = PaymentIntentParser.parse(byteStream, contentType) + val paymentIntent = paymentIntentParser.parse(byteStream, contentType) if (!basePaymentIntent.isExtendedBy(paymentIntent, true, NETWORK_PARAMETERS)) { log.info("BIP72 trust check failed") diff --git a/wallet/src/de/schildbach/wallet/payments/parsers/AddressParser.kt b/wallet/src/de/schildbach/wallet/payments/parsers/AddressParser.kt deleted file mode 100644 index cab390d793..0000000000 --- a/wallet/src/de/schildbach/wallet/payments/parsers/AddressParser.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2023 Dash Core Group. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package de.schildbach.wallet.payments.parsers - -import de.schildbach.wallet.Constants -import org.bitcoinj.core.Address -import org.bitcoinj.core.Base58 - -object AddressParser { - private val PATTERN_BITCOIN_ADDRESS = Regex("[${Base58.ALPHABET.joinToString(separator = "")}]{20,40}") - - fun exactMatch(inputText: String): Boolean { - return PATTERN_BITCOIN_ADDRESS.matches(inputText) - } - - fun findAll(inputText: String): List { - val matches = PATTERN_BITCOIN_ADDRESS.findAll(inputText) - val validRanges = mutableListOf() - - for (match in matches) { - val addressCandidate = match.value - - try { - Address.fromString(Constants.NETWORK_PARAMETERS, addressCandidate) - val startIndex = match.range.first - val endIndex = match.range.last + 1 - validRanges.add(startIndex..endIndex) - } catch (e: Exception) { - // Invalid address, skipping - } - } - - return validRanges - } -} diff --git a/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt b/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt index 7bb8be32ef..3eb7e24837 100644 --- a/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/payments/PaymentsPayFragment.kt @@ -26,12 +26,13 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.FragmentNavigator import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint -import de.schildbach.wallet.payments.parsers.PaymentIntentParser -import de.schildbach.wallet.payments.parsers.PaymentIntentParserException +import de.schildbach.wallet.Constants +import org.dash.wallet.common.payments.parsers.PaymentIntentParserException import de.schildbach.wallet.ui.send.SendCoinsActivity import de.schildbach.wallet_test.R import de.schildbach.wallet_test.databinding.FragmentPaymentsPayBinding import kotlinx.coroutines.launch +import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.ui.dialogs.AdaptiveDialog @@ -82,7 +83,7 @@ class PaymentsPayFragment : Fragment(R.layout.fragment_payments_pay) { private fun handleString(input: String) { lifecycleScope.launch { try { - val paymentIntent = PaymentIntentParser.parse(input, true) + val paymentIntent = DashPaymentIntentParser(Constants.NETWORK_PARAMETERS).parse(input, true) SendCoinsActivity.start(requireContext(), paymentIntent) } catch (ex: PaymentIntentParserException) { AdaptiveDialog.create( diff --git a/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt index 8937fa0c90..0ff92b0bae 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt @@ -38,10 +38,11 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint -import de.schildbach.wallet.payments.parsers.PaymentIntentParser +import de.schildbach.wallet.Constants import de.schildbach.wallet_test.R import de.schildbach.wallet_test.databinding.FragmentAddressInputBinding import kotlinx.coroutines.launch +import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.ui.scan.ScanActivity import org.dash.wallet.common.ui.viewBinding @@ -157,7 +158,7 @@ class AddressInputFragment : Fragment(R.layout.fragment_address_input) { val input = binding.addressInput.text.toString().trim() try { - val paymentIntent = PaymentIntentParser.parse(input, true) + val paymentIntent = DashPaymentIntentParser(Constants.NETWORK_PARAMETERS).parse(input, true) binding.inputWrapper.isErrorEnabled = false binding.errorText.isVisible = false SendCoinsActivity.start(requireContext(), paymentIntent) diff --git a/wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt b/wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt index bb14c3ee30..cc715d90a7 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt @@ -21,12 +21,13 @@ import android.content.ClipDescription import android.content.ClipboardManager import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import de.schildbach.wallet.payments.parsers.AddressParser import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import org.dash.wallet.common.payments.parsers.AddressParser import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService +import org.dash.wallet.common.util.Constants import javax.inject.Inject data class AddressInputUIState( @@ -56,7 +57,7 @@ class AddressInputViewModel @Inject constructor( fun showClipboardContent() { val text = getClipboardInput() - val addressRanges = AddressParser.findAll(text) + val addressRanges = AddressParser.getDashAddressParser(Constants.NETWORK_PARAMETERS).findAll(text) _uiState.value = _uiState.value.copy(clipboardText = text, addressRanges = addressRanges) analyticsService.logEvent(AnalyticsConstants.AddressInput.SHOW_CLIPBOARD, mapOf()) } diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt index 6a74f8122a..090de1f17c 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt @@ -30,10 +30,8 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment import dagger.hilt.android.AndroidEntryPoint import de.schildbach.wallet.Constants -import org.dash.wallet.common.data.PaymentIntent import de.schildbach.wallet.integration.android.BitcoinIntegration -import de.schildbach.wallet.payments.parsers.PaymentIntentParser -import de.schildbach.wallet.payments.parsers.PaymentIntentParserException +import org.dash.wallet.common.payments.parsers.PaymentIntentParserException import de.schildbach.wallet.ui.LockScreenActivity import de.schildbach.wallet.ui.util.InputParser import de.schildbach.wallet.util.Nfc @@ -42,6 +40,8 @@ import de.schildbach.wallet_test.databinding.ActivitySendCoinsBinding import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import org.bitcoinj.protocols.payments.PaymentProtocol +import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser import org.dash.wallet.common.ui.dialogs.AdaptiveDialog import org.dash.wallet.common.util.ResourceString import java.io.FileNotFoundException @@ -125,7 +125,7 @@ open class SendCoinsActivity : LockScreenActivity() { return if ((action == Intent.ACTION_VIEW || action == NfcAdapter.ACTION_NDEF_DISCOVERED) && intentUri?.hasValidScheme() == true ) { - PaymentIntentParser.parse(intentUri.toString(), true) + DashPaymentIntentParser(Constants.NETWORK_PARAMETERS).parse(intentUri.toString(), true) } else if (action == NfcAdapter.ACTION_NDEF_DISCOVERED && mimeType == PaymentProtocol.MIMETYPE_PAYMENTREQUEST) { val ndefMessage = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.get(0) as? NdefMessage val ndefMessagePayload = ndefMessage?.let { @@ -230,5 +230,5 @@ open class SendCoinsActivity : LockScreenActivity() { } private fun Uri.hasValidScheme() = - this.scheme == Constants.DASH_SCHEME || this.scheme == Constants.ANYPAY_SCHEME + this.scheme == org.dash.wallet.common.util.Constants.DASH_SCHEME || this.scheme == org.dash.wallet.common.util.Constants.ANYPAY_SCHEME } diff --git a/wallet/src/de/schildbach/wallet/ui/util/InputParser.java b/wallet/src/de/schildbach/wallet/ui/util/InputParser.java index 7bc8ac731f..6fefc92301 100644 --- a/wallet/src/de/schildbach/wallet/ui/util/InputParser.java +++ b/wallet/src/de/schildbach/wallet/ui/util/InputParser.java @@ -41,6 +41,7 @@ import org.bitcoinj.protocols.payments.PaymentSession; import org.bitcoinj.uri.BitcoinURI; import org.bitcoinj.uri.BitcoinURIParseException; +import org.dash.wallet.common.data.PaymentIntent; import org.dash.wallet.common.util.AddressUtil; import org.dash.wallet.common.util.Base43; import org.dash.wallet.common.util.Io; @@ -59,7 +60,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; + import de.schildbach.wallet.Constants; +import static org.dash.wallet.common.util.Constants.DASH_SCHEME; +import static org.dash.wallet.common.util.Constants.ANYPAY_SCHEME; +import org.dash.wallet.common.util.AddressUtil; +import org.dash.wallet.common.util.Io; +import de.schildbach.wallet.ui.send.SendCoinsActivity; import org.dash.wallet.common.data.PaymentIntent; import de.schildbach.wallet_test.R; @@ -77,8 +84,8 @@ public StringInputParser(final String input, boolean supportAnypayUrls) { if (supportAnypayUrls) { // replaces Anypay scheme with the Dash one // ie "pay:?r=https://(...)" become "dash:?r=https://(...)" - if (input.startsWith(Constants.ANYPAY_SCHEME + ":")) { - this.input = input.replaceFirst(Constants.ANYPAY_SCHEME, Constants.DASH_SCHEME); + if (input.startsWith(ANYPAY_SCHEME + ":")) { + this.input = input.replaceFirst(ANYPAY_SCHEME, DASH_SCHEME); return; } } @@ -87,7 +94,7 @@ public StringInputParser(final String input, boolean supportAnypayUrls) { @Override public void parse() { - if (input.startsWith(Constants.DASH_SCHEME.toUpperCase() + ":-")) { + if (input.startsWith(DASH_SCHEME.toUpperCase() + ":-")) { try { final byte[] serializedPaymentRequest = Base43.decode(input.substring(9)); @@ -105,7 +112,7 @@ public void parse() { error(x, R.string.input_parser_invalid_paymentrequest, x.getMessage()); } - } else if (input.startsWith(Constants.DASH_SCHEME + ":")) { + } else if (input.startsWith(DASH_SCHEME + ":")) { try { final BitcoinURI bitcoinUri = new BitcoinURI(null, input); final Address address = AddressUtil.getCorrectAddress(bitcoinUri, Constants.NETWORK_PARAMETERS); From 60858413f0d0df5bc8c473b54b4f792e0e434927 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 16 Jan 2024 21:20:23 -0800 Subject: [PATCH 10/60] fix: use correct bitcoin address parameters --- .../common/payments/parsers/BitcoinMainNetParams.kt | 8 ++++---- .../payments/parsers/BitcoinPaymentIntentParser.kt | 12 ++---------- .../common/payments/parsers/PaymentIntentParsers.kt | 13 ++++++++----- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt index 9181970bd9..aee11ada48 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ import org.bitcoinj.params.MainNetParams class BitcoinMainNetParams : MainNetParams() { init { - addressHeader = 1 - p2shHeader = 3 + addressHeader = 0 // addresses starting with 1 + p2shHeader = 5 // addresses starting with 3 } -} \ No newline at end of file +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt index d7bd5bb992..afdd9b854a 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt @@ -37,18 +37,10 @@ class BitcoinPaymentIntentParser : PaymentIntentParser("bitcoin", BitcoinMainNet override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { var inputStr = input -// if (supportAnypayUrls) { -// // replaces Anypay scheme with the Dash one -// // ie "pay:?r=https://(...)" become "dash:?r=https://(...)" -// if (input.startsWith(Constants.ANYPAY_SCHEME + ":")) { -// inputStr = input.replaceFirst(Constants.ANYPAY_SCHEME.toRegex(), Constants.DASH_SCHEME) -// } -// } - if (inputStr.startsWith("$currency:") || inputStr.startsWith("${currency.uppercase()}:")) { try { - val bitcoinUri = BitcoinURI(null, inputStr) - val address = bitcoinUri.address; // AddressUtil.getCorrectAddress(bitcoinUri) + val bitcoinUri = BitcoinURI(params, inputStr) + val address = bitcoinUri.address if (address != null && params != null && params != address.parameters) { throw BitcoinURIParseException("mismatched network") diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt index 9dc98e4351..4cfd6cbf78 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,14 +21,17 @@ object PaymentIntentParsers { private val processors = hashMapOf() init { - add("bitcoin", BitcoinPaymentIntentParser()) - add("dash", BitcoinPaymentIntentParser()) + add("bitcoin", "btc", BitcoinPaymentIntentParser()) } - fun add(currency: String, parser: PaymentIntentParser) { + + @JvmStatic + fun add(currency: String, code: String, parser: PaymentIntentParser) { processors[currency] = parser + processors[code] = parser } + @JvmStatic fun get(currency: String): PaymentIntentParser? { return processors[currency.lowercase()] } -} \ No newline at end of file +} From 852464fff2ac171c44367e48693db60d4b74366a Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Wed, 17 Jan 2024 07:45:10 -0800 Subject: [PATCH 11/60] feat: add AddressInputFragment to common and use in maya --- .../ui/address_input}/AddressInputFragment.kt | 36 ++++++++----- .../address_input}/AddressInputViewModel.kt | 54 +++++++++++++++++-- common/src/main/res/drawable/ic_scan_qr.xml | 30 +++++++++++ .../res/layout/fragment_address_input.xml | 7 +-- .../ui/MayaCryptoCurrencyPickerFragment.kt | 28 ++++++++-- .../maya/ui/MayaEnterAmountFragment.kt | 33 ++++++++++++ .../res/layout/fragment_maya_enter_amount.xml | 38 +++++++++++++ .../maya/src/main/res/navigation/nav_maya.xml | 50 ++++++++++++++++- wallet/res/navigation/nav_home.xml | 2 +- .../schildbach/wallet/WalletApplication.java | 3 ++ .../wallet/ui/main/WalletActivity.java | 41 ++++++++++++++ .../wallet/ui/send/SendCoinsActivity.kt | 2 + 12 files changed, 300 insertions(+), 24 deletions(-) rename {wallet/src/de/schildbach/wallet/ui/send => common/src/main/java/org/dash/wallet/common/ui/address_input}/AddressInputFragment.kt (86%) rename {wallet/src/de/schildbach/wallet/ui/send => common/src/main/java/org/dash/wallet/common/ui/address_input}/AddressInputViewModel.kt (67%) create mode 100644 common/src/main/res/drawable/ic_scan_qr.xml rename {wallet => common/src/main}/res/layout/fragment_address_input.xml (95%) create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt create mode 100644 integrations/maya/src/main/res/layout/fragment_maya_enter_amount.xml diff --git a/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt similarity index 86% rename from wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt rename to common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt index 0ff92b0bae..4b21db9f4e 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/AddressInputFragment.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Dash Core Group. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.ui.send +package org.dash.wallet.common.ui.address_input import android.app.Activity import android.os.Build @@ -34,15 +34,13 @@ import androidx.core.content.res.ResourcesCompat import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels +import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint -import de.schildbach.wallet.Constants -import de.schildbach.wallet_test.R -import de.schildbach.wallet_test.databinding.FragmentAddressInputBinding import kotlinx.coroutines.launch -import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser +import org.dash.wallet.common.R +import org.dash.wallet.common.databinding.FragmentAddressInputBinding import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.ui.scan.ScanActivity import org.dash.wallet.common.ui.viewBinding @@ -52,7 +50,7 @@ import org.dash.wallet.common.util.observe @AndroidEntryPoint class AddressInputFragment : Fragment(R.layout.fragment_address_input) { private val binding by viewBinding(FragmentAddressInputBinding::bind) - private val viewModel by viewModels() + private val viewModel by activityViewModels() private val scanLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() @@ -67,11 +65,25 @@ class AddressInputFragment : Fragment(R.layout.fragment_address_input) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.clearInput() binding.toolbar.setNavigationOnClickListener { findNavController().popBackStack() } + // the view model defaults to using Dash as the currency + requireArguments().getString("currency")?.let { + viewModel.currency = it + binding.errorText.text = getString(R.string.not_valid_address, it) + } + requireArguments().getString("title")?.let { + binding.toolbarTitle.text = it + } + requireArguments().getString("hint")?.let { + binding.inputWrapper.hint = it + } + viewModel.nextAction = requireArguments().getInt("nextAction") + binding.addressInput.doOnTextChanged { text, _, _, _ -> viewModel.setInput(text.toString()) binding.continueBtn.isEnabled = !text.isNullOrEmpty() @@ -158,13 +170,13 @@ class AddressInputFragment : Fragment(R.layout.fragment_address_input) { val input = binding.addressInput.text.toString().trim() try { - val paymentIntent = DashPaymentIntentParser(Constants.NETWORK_PARAMETERS).parse(input, true) + viewModel.parsePaymentIntent(input) + viewModel.setAddressResult(input) binding.inputWrapper.isErrorEnabled = false binding.errorText.isVisible = false - SendCoinsActivity.start(requireContext(), paymentIntent) - viewModel.logEvent(AnalyticsConstants.AddressInput.CONTINUE) - requireActivity().overridePendingTransition(R.anim.slide_in_right, R.anim.activity_stay) } catch (ex: Exception) { + println(ex) + ex.printStackTrace() binding.inputWrapper.isErrorEnabled = true binding.errorText.isVisible = true } diff --git a/wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt similarity index 67% rename from wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt rename to common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt index cc715d90a7..effb261a4c 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/AddressInputViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Dash Core Group. + * Copyright 2022 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package de.schildbach.wallet.ui.send +package org.dash.wallet.common.ui.address_input import android.content.ClipDescription import android.content.ClipboardManager @@ -24,7 +24,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import org.dash.wallet.common.WalletDataProvider +import org.dash.wallet.common.data.PaymentIntent import org.dash.wallet.common.payments.parsers.AddressParser +import org.dash.wallet.common.payments.parsers.PaymentIntentParsers import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.util.Constants @@ -37,15 +40,39 @@ data class AddressInputUIState( val addressInput: String = "" ) +data class AddressInputResult( + val valid: Boolean = false, + val addressInput: String = "", + val paymentIntent: PaymentIntent? = null, + val currency: String = Constants.DASH_CURRENCY, + val nextAction: Int = 0 +) + @HiltViewModel class AddressInputViewModel @Inject constructor( private val clipboardManager: ClipboardManager, - private val analyticsService: AnalyticsService + private val analyticsService: AnalyticsService, + private val walletDataProvider: WalletDataProvider ): ViewModel() { + private var addressParser: AddressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) + var currency: String = "Dash" + set(value) { + field = value + addressParser = AddressParser.get( + field, + if (value.lowercase() == "dash") walletDataProvider.networkParameters else null + ) + } + var nextAction: Int = -1 private val _uiState = MutableStateFlow(AddressInputUIState()) val uiState: StateFlow = _uiState.asStateFlow() + private var paymentIntent: PaymentIntent? = null + private val _addressResult = MutableStateFlow(AddressInputResult()) + val addressResult: StateFlow + get() = _addressResult + private val clipboardListener = ClipboardManager.OnPrimaryClipChangedListener { _uiState.value = _uiState.value.copy(hasClipboardText = hasClipboardInput()) } @@ -57,16 +84,31 @@ class AddressInputViewModel @Inject constructor( fun showClipboardContent() { val text = getClipboardInput() - val addressRanges = AddressParser.getDashAddressParser(Constants.NETWORK_PARAMETERS).findAll(text) + val addressRanges = addressParser.findAll(text) _uiState.value = _uiState.value.copy(clipboardText = text, addressRanges = addressRanges) analyticsService.logEvent(AnalyticsConstants.AddressInput.SHOW_CLIPBOARD, mapOf()) } + fun clearInput() { + _uiState.value = AddressInputUIState(hasClipboardText = hasClipboardInput()) + currency = "dash" + addressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) + _addressResult.value = AddressInputResult() + } + fun setInput(text: String) { _uiState.value = _uiState.value.copy(addressInput = text) analyticsService.logEvent(AnalyticsConstants.AddressInput.ADDRESS_TAP, mapOf()) } + suspend fun parsePaymentIntent(input: String) { + paymentIntent = PaymentIntentParsers.get(currency)!!.parse(input) + } + + fun setAddressResult(input: String) { + _addressResult.value = AddressInputResult(true, input, paymentIntent, currency, nextAction) + } + private fun hasClipboardInput(): Boolean { if (clipboardManager.hasPrimaryClip()) { val clipDescription = clipboardManager.primaryClip?.description ?: return false @@ -105,4 +147,8 @@ class AddressInputViewModel @Inject constructor( super.onCleared() clipboardManager.removePrimaryClipChangedListener(clipboardListener) } + + fun clearResult() { + _addressResult.value = AddressInputResult() + } } diff --git a/common/src/main/res/drawable/ic_scan_qr.xml b/common/src/main/res/drawable/ic_scan_qr.xml new file mode 100644 index 0000000000..93f0b2843d --- /dev/null +++ b/common/src/main/res/drawable/ic_scan_qr.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/wallet/res/layout/fragment_address_input.xml b/common/src/main/res/layout/fragment_address_input.xml similarity index 95% rename from wallet/res/layout/fragment_address_input.xml rename to common/src/main/res/layout/fragment_address_input.xml index 3e3f302bf3..b1a971eed2 100644 --- a/wallet/res/layout/fragment_address_input.xml +++ b/common/src/main/res/layout/fragment_address_input.xml @@ -23,7 +23,7 @@ android:background="@color/background_primary" android:orientation="vertical" android:fitsSystemWindows="true" - tools:context="de.schildbach.wallet.ui.send.AddressInputFragment"> + tools:context="org.dash.wallet.common.ui.address_input.AddressInputFragment"> @@ -75,7 +76,7 @@ android:layout_marginHorizontal="30dp" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/not_dash_address" + android:text="@string/not_valid_address" android:visibility="gone" tools:visibility="visible" /> diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt index 816dfc2f86..62a89a325e 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt @@ -34,6 +34,7 @@ import org.dash.wallet.common.ui.recyclerview.IconifiedListAdapter import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.common.util.GenericUtils import org.dash.wallet.common.util.observe +import org.dash.wallet.common.util.safeNavigate import org.dash.wallet.integrations.maya.R import org.dash.wallet.integrations.maya.databinding.FragmentCurrencyPickerBinding import org.dash.wallet.integrations.maya.model.PoolInfo @@ -82,6 +83,14 @@ class MayaCryptoCurrencyPickerFragment : Fragment(R.layout.fragment_currency_pic requireContext().getString(R.string.cryptocurrency_ethereum_code), requireContext().getString(R.string.cryptocurrency_ethereum_network) ), + "KUJI.KUJI" to IconifiedViewItem( + requireContext().getString(R.string.cryptocurrency_kuji_code), + requireContext().getString(R.string.cryptocurrency_kuji_network) + ), + "KUJI.USK" to IconifiedViewItem( + requireContext().getString(R.string.cryptocurrency_usk_code), + requireContext().getString(R.string.cryptocurrency_usk_network) + ), "DASH.DASH" to IconifiedViewItem( requireContext().getString(R.string.cryptocurrency_dash_code), requireContext().getString(R.string.cryptocurrency_dash_network) @@ -94,6 +103,10 @@ class MayaCryptoCurrencyPickerFragment : Fragment(R.layout.fragment_currency_pic requireContext().getString(R.string.cryptocurrency_tether_code), requireContext().getString(R.string.cryptocurrency_tether_network) ), + "ETH.WSTETH-0X7F39C581F595B53C5CB19BD0B3F8DA6C935E2CA0" to IconifiedViewItem( + requireContext().getString(R.string.cryptocurrency_wsteth_code), + requireContext().getString(R.string.cryptocurrency_wsteth_network) + ), "THOR.RUNE" to IconifiedViewItem( requireContext().getString(R.string.cryptocurrency_rune_code), requireContext().getString(R.string.cryptocurrency_rune_network) @@ -151,8 +164,17 @@ class MayaCryptoCurrencyPickerFragment : Fragment(R.layout.fragment_currency_pic } } - fun clickListener(pool: PoolInfo) { - AdaptiveDialog.simple("${pool.currencyCode} was chosen", "Close").show(requireActivity()) { - } + private fun clickListener(pool: PoolInfo) { + safeNavigate( + MayaCryptoCurrencyPickerFragmentDirections.mayaCurrencyPickerToAddressInput( + pool.currencyCode, + "Convert DASH to BTC", + "BTC Address", + R.id.mayaAddressInput_to_EnterAmount + ) + ) + + // AdaptiveDialog.simple("${pool.currencyCode} was chosen", "Close").show(requireActivity()) { + // } } } diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt new file mode 100644 index 0000000000..2a61685b09 --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.maya.ui + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import org.dash.wallet.common.ui.viewBinding +import org.dash.wallet.integrations.maya.R +import org.dash.wallet.integrations.maya.databinding.FragmentMayaEnterAmountBinding + +class MayaEnterAmountFragment : Fragment(R.layout.fragment_maya_enter_amount) { + private val binding by viewBinding(FragmentMayaEnterAmountBinding::bind) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.currency.text = requireArguments().getString("currency") + } +} diff --git a/integrations/maya/src/main/res/layout/fragment_maya_enter_amount.xml b/integrations/maya/src/main/res/layout/fragment_maya_enter_amount.xml new file mode 100644 index 0000000000..faad50f6c1 --- /dev/null +++ b/integrations/maya/src/main/res/layout/fragment_maya_enter_amount.xml @@ -0,0 +1,38 @@ + + + + + + + + + \ No newline at end of file diff --git a/integrations/maya/src/main/res/navigation/nav_maya.xml b/integrations/maya/src/main/res/navigation/nav_maya.xml index 37fc60a06d..2eb762acbf 100644 --- a/integrations/maya/src/main/res/navigation/nav_maya.xml +++ b/integrations/maya/src/main/res/navigation/nav_maya.xml @@ -21,6 +21,54 @@ android:id="@+id/mayaCurrencyPickerFragment" android:name="org.dash.wallet.integrations.maya.ui.MayaCryptoCurrencyPickerFragment" android:label="mayaPortalFragment" - tools:layout="@layout/fragment_currency_picker" /> + tools:layout="@layout/fragment_currency_picker" > + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wallet/res/navigation/nav_home.xml b/wallet/res/navigation/nav_home.xml index 38b7a08658..b8db964e5a 100644 --- a/wallet/res/navigation/nav_home.xml +++ b/wallet/res/navigation/nav_home.xml @@ -288,7 +288,7 @@ diff --git a/wallet/src/de/schildbach/wallet/WalletApplication.java b/wallet/src/de/schildbach/wallet/WalletApplication.java index baf255e3cc..10461de706 100644 --- a/wallet/src/de/schildbach/wallet/WalletApplication.java +++ b/wallet/src/de/schildbach/wallet/WalletApplication.java @@ -73,6 +73,8 @@ import org.dash.wallet.common.Configuration; import org.dash.wallet.common.InteractionAwareActivity; import org.dash.wallet.common.WalletDataProvider; +import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser; +import org.dash.wallet.common.payments.parsers.PaymentIntentParsers; import org.dash.wallet.common.services.LeftoverBalanceException; import org.dash.wallet.common.transactions.filters.TransactionFilter; import org.dash.wallet.common.transactions.TransactionWrapper; @@ -209,6 +211,7 @@ public void onCreate() { config = new Configuration(PreferenceManager.getDefaultSharedPreferences(this), getResources()); autoLogout = new AutoLogout(config); autoLogout.registerDeviceInteractiveReceiver(this); + PaymentIntentParsers.add("dash", "DASH", new DashPaymentIntentParser(Constants.NETWORK_PARAMETERS)); registerActivityLifecycleCallbacks(new ActivitiesTracker() { @Override public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) { diff --git a/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java b/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java index 50a237b3cc..cf67807843 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java +++ b/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java @@ -27,15 +27,22 @@ import android.os.PowerManager; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.ActivityKt; +import androidx.navigation.NavController; +import androidx.navigation.NavDirections; import com.google.common.collect.ImmutableList; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.wallet.Wallet; +import org.dash.wallet.common.services.analytics.AnalyticsConstants; import org.dash.wallet.common.ui.BaseAlertDialogBuilder; +import org.dash.wallet.common.ui.address_input.AddressInputViewModel; import org.dash.wallet.common.ui.dialogs.AdaptiveDialog; +import org.dash.wallet.common.util.FlowExtKt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +56,7 @@ import de.schildbach.wallet.ui.EncryptNewKeyChainDialogFragment; import de.schildbach.wallet.ui.ReportIssueDialogBuilder; import de.schildbach.wallet.ui.RestoreWalletFromSeedDialogFragment; +import de.schildbach.wallet.ui.send.SendCoinsActivity; import de.schildbach.wallet.ui.util.InputParser.BinaryInputParser; import de.schildbach.wallet.ui.widget.UpgradeWalletDisclaimerDialog; import de.schildbach.wallet.util.CrashReporter; @@ -77,6 +85,7 @@ public static Intent createIntent(Context context) { private BaseAlertDialogBuilder baseAlertDialogBuilder; private MainViewModel viewModel; + private AddressInputViewModel addressInputViewModel; ActivityResultLauncher requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), result -> WalletActivityExt.INSTANCE.requestDisableBatteryOptimisation(WalletActivity.this)); @@ -86,6 +95,7 @@ protected void onCreate(final Bundle savedInstanceState) { baseAlertDialogBuilder = new BaseAlertDialogBuilder(this); viewModel = new ViewModelProvider(this).get(MainViewModel.class); + addressInputViewModel = new ViewModelProvider(this).get(AddressInputViewModel.class); setContentView(R.layout.activity_main); WalletActivityExt.INSTANCE.setupBottomNavigation(this, viewModel); @@ -112,6 +122,37 @@ protected void onCreate(final Bundle savedInstanceState) { currencies.component2() ) ); + + FlowExtKt.observe(addressInputViewModel.getAddressResult(), this, (addressInputResult, continuation) -> { + if (addressInputResult.getValid()) { + if (addressInputViewModel.getCurrency().equals("dash")) { + SendCoinsActivity.Companion.start(this, addressInputResult.getPaymentIntent()); + viewModel.logEvent(AnalyticsConstants.AddressInput.CONTINUE); + overridePendingTransition(R.anim.slide_in_right, R.anim.activity_stay); + addressInputViewModel.clearResult(); + } else if (addressInputResult.getNextAction() > 0){ + NavController nav = ActivityKt.findNavController(this, R.id.nav_host_fragment); + nav.navigate(new NavDirections() { + @Override + public int getActionId() { + return addressInputResult.getNextAction(); + } + + @NonNull + @Override + public Bundle getArguments() { + Bundle bundle = new Bundle(); + bundle.putParcelable("paymentIntent", addressInputResult.getPaymentIntent()); + bundle.putString("currency", addressInputResult.toString()); + return bundle; + } + }); + } + addressInputViewModel.clearResult(); + + } + return Unit.INSTANCE; + }); } @Override diff --git a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt index 090de1f17c..64c82f449f 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/SendCoinsActivity.kt @@ -54,10 +54,12 @@ open class SendCoinsActivity : LockScreenActivity() { const val ACTION_SEND_FROM_WALLET_URI = "de.schildbach.wallet.action.SEND_FROM_WALLET_URI" const val INTENT_EXTRA_PAYMENT_INTENT = "paymentIntent" + @JvmStatic fun start(context: Context, paymentIntent: PaymentIntent?) { start(context, null, paymentIntent, false) } + @JvmStatic fun start(context: Context, action: String?, paymentIntent: PaymentIntent?, keepUnlocked: Boolean) { val intent = Intent(context, SendCoinsActivity::class.java) From 0b9f50944e27d3db69d65097cc37d8385cbd82fe Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Wed, 17 Jan 2024 08:56:30 -0800 Subject: [PATCH 12/60] refactor: derive fragments from AddressInputFragment --- .../wallet/common/data/PaymentIntent.java | 2 +- .../wallet/common/payments/parsers/Parsers.kt | 50 ++++++++++++++++++ .../ui/address_input/AddressInputFragment.kt | 26 ++++++---- .../ui/address_input/AddressInputViewModel.kt | 31 +++++------ .../maya/ui/MayaAddressInputFragment.kt | 29 +++++------ .../maya/ui/MayaEnterAmountFragment.kt | 9 +++- .../maya/src/main/res/navigation/nav_maya.xml | 2 +- wallet/res/navigation/nav_home.xml | 2 +- .../schildbach/wallet/WalletApplication.java | 4 -- .../wallet/ui/main/WalletActivity.java | 38 -------------- .../ui/send/DashAddressInputFragment.kt | 52 +++++++++++++++++++ 11 files changed, 157 insertions(+), 88 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt rename common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt => integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt (54%) create mode 100644 wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt diff --git a/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java b/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java index d52619cd5b..a22036c9d5 100644 --- a/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java +++ b/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2014-2015 the original author or authors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt new file mode 100644 index 0000000000..1cc8991023 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +// TODO: this may need to be a class, so we can define a custom set of parsers +// that a #[AddressInputViewModel] can use to parse addresses and payments intents +// for Maya, there may be custom parsers that generate the correct PaymentIntent +// that we don't want used or visible to the wallet module. Currently this has global scope. + +object Parsers { + private val paymentIntentParsers = hashMapOf() + private val addressParsers = hashMapOf() + + init { + add("bitcoin", "btc", BitcoinPaymentIntentParser(), AddressParser.getBitcoinAddressParser()) + } + + @JvmStatic + fun add(currency: String, code: String, paymentIntentParser: PaymentIntentParser, addressParser: AddressParser) { + paymentIntentParsers[currency] = paymentIntentParser + paymentIntentParsers[code] = paymentIntentParser + addressParsers[currency] = addressParser + addressParsers[code] = addressParser + } + + @JvmStatic + fun getPaymentIntentParser(currency: String): PaymentIntentParser? { + return paymentIntentParsers[currency.lowercase()] + } + + @JvmStatic + fun getAddressParser(currency: String): AddressParser? { + return addressParsers[currency.lowercase()] + } +} diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt index 4b21db9f4e..a55d479753 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt @@ -34,7 +34,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint @@ -47,10 +47,18 @@ import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.common.util.KeyboardUtil import org.dash.wallet.common.util.observe +/** + * Address input fragment + * + * This is an abstract class with a continueAction funciton that must be overridden in any derived class + * + * By default this class, through the view model #[AddressInputViewModel], will be using DASH as the currency. + * + */ @AndroidEntryPoint -class AddressInputFragment : Fragment(R.layout.fragment_address_input) { - private val binding by viewBinding(FragmentAddressInputBinding::bind) - private val viewModel by activityViewModels() +abstract class AddressInputFragment : Fragment(R.layout.fragment_address_input) { + protected val binding by viewBinding(FragmentAddressInputBinding::bind) + protected val viewModel by viewModels() private val scanLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() @@ -65,7 +73,6 @@ class AddressInputFragment : Fragment(R.layout.fragment_address_input) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.clearInput() binding.toolbar.setNavigationOnClickListener { findNavController().popBackStack() @@ -117,7 +124,7 @@ class AddressInputFragment : Fragment(R.layout.fragment_address_input) { } binding.continueBtn.setOnClickListener { - continueAction() + doContinueAction() } binding.showClipboardBtn.setOnClickListener { @@ -164,17 +171,18 @@ class AddressInputFragment : Fragment(R.layout.fragment_address_input) { KeyboardUtil.showSoftKeyboard(requireContext(), binding.addressInput) } - - private fun continueAction() { + abstract fun continueAction() + private fun doContinueAction() { lifecycleScope.launch { val input = binding.addressInput.text.toString().trim() - try { viewModel.parsePaymentIntent(input) viewModel.setAddressResult(input) + continueAction() binding.inputWrapper.isErrorEnabled = false binding.errorText.isVisible = false } catch (ex: Exception) { + // TODO: remove this before completing PR println(ex) ex.printStackTrace() binding.inputWrapper.isErrorEnabled = true diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt index effb261a4c..5aedfca78c 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2023 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.asStateFlow import org.dash.wallet.common.WalletDataProvider import org.dash.wallet.common.data.PaymentIntent import org.dash.wallet.common.payments.parsers.AddressParser -import org.dash.wallet.common.payments.parsers.PaymentIntentParsers +import org.dash.wallet.common.payments.parsers.Parsers import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.util.Constants @@ -55,13 +55,10 @@ class AddressInputViewModel @Inject constructor( private val walletDataProvider: WalletDataProvider ): ViewModel() { private var addressParser: AddressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) - var currency: String = "Dash" + var currency: String = Constants.DASH_CURRENCY set(value) { field = value - addressParser = AddressParser.get( - field, - if (value.lowercase() == "dash") walletDataProvider.networkParameters else null - ) + addressParser = Parsers.getAddressParser(field)!! } var nextAction: Int = -1 @@ -89,12 +86,12 @@ class AddressInputViewModel @Inject constructor( analyticsService.logEvent(AnalyticsConstants.AddressInput.SHOW_CLIPBOARD, mapOf()) } - fun clearInput() { - _uiState.value = AddressInputUIState(hasClipboardText = hasClipboardInput()) - currency = "dash" - addressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) - _addressResult.value = AddressInputResult() - } +// fun clearInput() { +// _uiState.value = AddressInputUIState(hasClipboardText = hasClipboardInput()) +// currency = "dash" +// addressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) +// _addressResult.value = AddressInputResult() +// } fun setInput(text: String) { _uiState.value = _uiState.value.copy(addressInput = text) @@ -102,7 +99,7 @@ class AddressInputViewModel @Inject constructor( } suspend fun parsePaymentIntent(input: String) { - paymentIntent = PaymentIntentParsers.get(currency)!!.parse(input) + paymentIntent = Parsers.getPaymentIntentParser(currency)!!.parse(input) } fun setAddressResult(input: String) { @@ -148,7 +145,7 @@ class AddressInputViewModel @Inject constructor( clipboardManager.removePrimaryClipChangedListener(clipboardListener) } - fun clearResult() { - _addressResult.value = AddressInputResult() - } +// fun clearResult() { +// _addressResult.value = AddressInputResult() +// } } diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt similarity index 54% rename from common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt rename to integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt index 4cfd6cbf78..2dbe9d9c1e 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParsers.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt @@ -15,23 +15,20 @@ * along with this program. If not, see . */ -package org.dash.wallet.common.payments.parsers +package org.dash.wallet.integrations.maya.ui -object PaymentIntentParsers { - private val processors = hashMapOf() +import org.dash.wallet.common.ui.address_input.AddressInputFragment +import org.dash.wallet.common.util.safeNavigate - init { - add("bitcoin", "btc", BitcoinPaymentIntentParser()) - } - - @JvmStatic - fun add(currency: String, code: String, parser: PaymentIntentParser) { - processors[currency] = parser - processors[code] = parser - } - - @JvmStatic - fun get(currency: String): PaymentIntentParser? { - return processors[currency.lowercase()] +class MayaAddressInputFragment : AddressInputFragment() { + override fun continueAction() { + safeNavigate( + MayaAddressInputFragmentDirections.mayaAddressInputToEnterAmount( + viewModel.currency, + viewModel.addressResult.value?.paymentIntent!! + ) + ) + // TODO: add event monitoring here + // viewModel.logEvent(AnalyticsConstants.AddressInput.CONTINUE) } } diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt index 2a61685b09..c02a0623a9 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaEnterAmountFragment.kt @@ -17,9 +17,11 @@ package org.dash.wallet.integrations.maya.ui +import android.os.Build import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment +import org.dash.wallet.common.data.PaymentIntent import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.integrations.maya.R import org.dash.wallet.integrations.maya.databinding.FragmentMayaEnterAmountBinding @@ -28,6 +30,11 @@ class MayaEnterAmountFragment : Fragment(R.layout.fragment_maya_enter_amount) { private val binding by viewBinding(FragmentMayaEnterAmountBinding::bind) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.currency.text = requireArguments().getString("currency") + val paymentIntent: PaymentIntent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requireArguments().getParcelable("paymentIntent", PaymentIntent::class.java) + } else { + requireArguments().getParcelable("paymentIntent") + } + binding.currency.text = requireArguments().getString("currency") + ": " + paymentIntent?.toString() } } diff --git a/integrations/maya/src/main/res/navigation/nav_maya.xml b/integrations/maya/src/main/res/navigation/nav_maya.xml index 2eb762acbf..6bd2eae8f2 100644 --- a/integrations/maya/src/main/res/navigation/nav_maya.xml +++ b/integrations/maya/src/main/res/navigation/nav_maya.xml @@ -33,7 +33,7 @@ diff --git a/wallet/src/de/schildbach/wallet/WalletApplication.java b/wallet/src/de/schildbach/wallet/WalletApplication.java index 10461de706..344b5b7d70 100644 --- a/wallet/src/de/schildbach/wallet/WalletApplication.java +++ b/wallet/src/de/schildbach/wallet/WalletApplication.java @@ -73,8 +73,6 @@ import org.dash.wallet.common.Configuration; import org.dash.wallet.common.InteractionAwareActivity; import org.dash.wallet.common.WalletDataProvider; -import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser; -import org.dash.wallet.common.payments.parsers.PaymentIntentParsers; import org.dash.wallet.common.services.LeftoverBalanceException; import org.dash.wallet.common.transactions.filters.TransactionFilter; import org.dash.wallet.common.transactions.TransactionWrapper; @@ -108,7 +106,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -211,7 +208,6 @@ public void onCreate() { config = new Configuration(PreferenceManager.getDefaultSharedPreferences(this), getResources()); autoLogout = new AutoLogout(config); autoLogout.registerDeviceInteractiveReceiver(this); - PaymentIntentParsers.add("dash", "DASH", new DashPaymentIntentParser(Constants.NETWORK_PARAMETERS)); registerActivityLifecycleCallbacks(new ActivitiesTracker() { @Override public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) { diff --git a/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java b/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java index cf67807843..a9d1b1abd9 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java +++ b/wallet/src/de/schildbach/wallet/ui/main/WalletActivity.java @@ -30,19 +30,13 @@ import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.lifecycle.ViewModelProvider; -import androidx.navigation.ActivityKt; -import androidx.navigation.NavController; -import androidx.navigation.NavDirections; - import com.google.common.collect.ImmutableList; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.wallet.Wallet; -import org.dash.wallet.common.services.analytics.AnalyticsConstants; import org.dash.wallet.common.ui.BaseAlertDialogBuilder; import org.dash.wallet.common.ui.address_input.AddressInputViewModel; import org.dash.wallet.common.ui.dialogs.AdaptiveDialog; -import org.dash.wallet.common.util.FlowExtKt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +50,6 @@ import de.schildbach.wallet.ui.EncryptNewKeyChainDialogFragment; import de.schildbach.wallet.ui.ReportIssueDialogBuilder; import de.schildbach.wallet.ui.RestoreWalletFromSeedDialogFragment; -import de.schildbach.wallet.ui.send.SendCoinsActivity; import de.schildbach.wallet.ui.util.InputParser.BinaryInputParser; import de.schildbach.wallet.ui.widget.UpgradeWalletDisclaimerDialog; import de.schildbach.wallet.util.CrashReporter; @@ -122,37 +115,6 @@ protected void onCreate(final Bundle savedInstanceState) { currencies.component2() ) ); - - FlowExtKt.observe(addressInputViewModel.getAddressResult(), this, (addressInputResult, continuation) -> { - if (addressInputResult.getValid()) { - if (addressInputViewModel.getCurrency().equals("dash")) { - SendCoinsActivity.Companion.start(this, addressInputResult.getPaymentIntent()); - viewModel.logEvent(AnalyticsConstants.AddressInput.CONTINUE); - overridePendingTransition(R.anim.slide_in_right, R.anim.activity_stay); - addressInputViewModel.clearResult(); - } else if (addressInputResult.getNextAction() > 0){ - NavController nav = ActivityKt.findNavController(this, R.id.nav_host_fragment); - nav.navigate(new NavDirections() { - @Override - public int getActionId() { - return addressInputResult.getNextAction(); - } - - @NonNull - @Override - public Bundle getArguments() { - Bundle bundle = new Bundle(); - bundle.putParcelable("paymentIntent", addressInputResult.getPaymentIntent()); - bundle.putString("currency", addressInputResult.toString()); - return bundle; - } - }); - } - addressInputViewModel.clearResult(); - - } - return Unit.INSTANCE; - }); } @Override diff --git a/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt new file mode 100644 index 0000000000..fea783effc --- /dev/null +++ b/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.wallet.ui.send + +import android.os.Bundle +import android.view.View +import com.google.common.base.Preconditions +import de.schildbach.wallet_test.R +import org.dash.wallet.common.payments.parsers.AddressParser.Companion.getDashAddressParser +import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser +import org.dash.wallet.common.payments.parsers.Parsers +import org.dash.wallet.common.services.analytics.AnalyticsConstants +import org.dash.wallet.common.ui.address_input.AddressInputFragment +import org.dash.wallet.common.util.Constants + +class DashAddressInputFragment : AddressInputFragment() { + + init { + if (Parsers.getAddressParser(Constants.DASH_CURRENCY) == null) { + Parsers.add( + "dash", + "DASH", + DashPaymentIntentParser(de.schildbach.wallet.Constants.NETWORK_PARAMETERS), + getDashAddressParser(de.schildbach.wallet.Constants.NETWORK_PARAMETERS) + ) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + Preconditions.checkState(viewModel.currency == Constants.DASH_CURRENCY) + } + override fun continueAction() { + SendCoinsActivity.start(requireActivity(), viewModel.addressResult.value?.paymentIntent!!) + viewModel.logEvent(AnalyticsConstants.AddressInput.CONTINUE) + requireActivity().overridePendingTransition(R.anim.slide_in_right, R.anim.activity_stay) + } +} From 321c23aecc4e8183ec12f69da1983178ac8daed2 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Wed, 17 Jan 2024 09:41:29 -0800 Subject: [PATCH 13/60] refactor: simplify AddressInputFragment and view model --- .../wallet/common/data/PaymentIntent.java | 2 + .../common/payments/parsers/AddressParser.kt | 4 +- .../parsers/EthereumPaymentIntentParser.kt | 84 +++++++++++++++++++ .../wallet/common/payments/parsers/Parsers.kt | 1 + .../ui/address_input/AddressInputFragment.kt | 3 +- .../ui/address_input/AddressInputViewModel.kt | 23 ++--- .../wallet/common/ui/scan/ScanViewModel.java | 4 +- .../maya/ui/MayaAddressInputFragment.kt | 2 +- .../ui/MayaCryptoCurrencyPickerFragment.kt | 5 +- .../maya/src/main/res/navigation/nav_maya.xml | 3 - .../maya/src/main/res/values/strings-maya.xml | 2 + .../ui/send/DashAddressInputFragment.kt | 2 +- 12 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt diff --git a/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java b/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java index a22036c9d5..b880f8893f 100644 --- a/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java +++ b/common/src/main/java/org/dash/wallet/common/data/PaymentIntent.java @@ -103,6 +103,8 @@ else if (ScriptPattern.isP2PK(script)) builder.append(Constants.HEX.encode(ScriptPattern.extractKeyFromP2PK(script))); else if (ScriptPattern.isSentToMultisig(script)) builder.append("multisig"); + else if (ScriptPattern.isOpReturn(script)) + builder.append(script); else builder.append("unknown"); builder.append(']'); diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt index c27d3eff57..1da4671d8c 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt @@ -24,12 +24,12 @@ import org.bitcoinj.core.NetworkParameters class AddressParser(pattern: String, val params: NetworkParameters?) { companion object { private val PATTERN_BITCOIN_ADDRESS = "[${Base58.ALPHABET.joinToString(separator = "")}]{20,40}" - private val PATTERN_ETHEREUM_ADDRESS = "0x[a-fA-F0-9]{40}" + private const val PATTERN_ETHEREUM_ADDRESS = "0x[a-fA-F0-9]{40}" fun getDashAddressParser(params: NetworkParameters): AddressParser { return AddressParser(PATTERN_BITCOIN_ADDRESS, params) } - fun getBitcoinAddressParser(): AddressParser { + fun getBitcoinAddressParser(): AddressParser { return AddressParser(PATTERN_BITCOIN_ADDRESS, BitcoinMainNetParams()) } diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt new file mode 100644 index 0000000000..1cbd448606 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.Coin +import org.bitcoinj.script.ScriptBuilder +import org.dash.wallet.common.R +import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.util.ResourceString +import org.slf4j.LoggerFactory + +class EthereumPaymentIntentParser : PaymentIntentParser("ethereum", null) { + private val log = LoggerFactory.getLogger(EthereumPaymentIntentParser::class.java) + private val addressParser = AddressParser.getEthereumAddressParser() + + override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { + var inputStr = input + + if (inputStr.startsWith("$currency:") || inputStr.startsWith("${currency.uppercase()}:")) { + try { + val hexAddress = inputStr.substring(currency.length) + return@withContext createPaymentIntent(hexAddress) + } catch (ex: Exception) { + log.info("got invalid uri: '$inputStr'", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf(inputStr) + ) + ) + } + } else if (addressParser.exactMatch(inputStr)) { + try { + return@withContext createPaymentIntent(inputStr) + } catch (ex: AddressFormatException) { + log.info("got invalid address", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf() + ) + ) + } + } + + log.info("cannot classify: '{}'", input) + throw PaymentIntentParserException( + IllegalArgumentException(input), + ResourceString( + R.string.error, + listOf(input) + ) + ) + } + + private fun createPaymentIntent(inputStr: String): PaymentIntent { + val metadata = "SWAP:ETH.ETH:${inputStr.substring(2)}" + return PaymentIntent( + null, "ethereum", null, + arrayOf(PaymentIntent.Output(Coin.ZERO, ScriptBuilder.createOpReturnScript(metadata.toByteArray()))), + "ethereum network", null, null, null, null + ) + } +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt index 1cc8991023..56de276561 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt @@ -28,6 +28,7 @@ object Parsers { init { add("bitcoin", "btc", BitcoinPaymentIntentParser(), AddressParser.getBitcoinAddressParser()) + add("ethereum", "eth", EthereumPaymentIntentParser(), AddressParser.getEthereumAddressParser()) } @JvmStatic diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt index a55d479753..e2dfa76326 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2023 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -89,7 +89,6 @@ abstract class AddressInputFragment : Fragment(R.layout.fragment_address_input) requireArguments().getString("hint")?.let { binding.inputWrapper.hint = it } - viewModel.nextAction = requireArguments().getInt("nextAction") binding.addressInput.doOnTextChanged { text, _, _, _ -> viewModel.setInput(text.toString()) diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt index 5aedfca78c..21b1d3bd5d 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt @@ -44,15 +44,14 @@ data class AddressInputResult( val valid: Boolean = false, val addressInput: String = "", val paymentIntent: PaymentIntent? = null, - val currency: String = Constants.DASH_CURRENCY, - val nextAction: Int = 0 + val currency: String = Constants.DASH_CURRENCY ) @HiltViewModel class AddressInputViewModel @Inject constructor( private val clipboardManager: ClipboardManager, private val analyticsService: AnalyticsService, - private val walletDataProvider: WalletDataProvider + walletDataProvider: WalletDataProvider ): ViewModel() { private var addressParser: AddressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) var currency: String = Constants.DASH_CURRENCY @@ -60,14 +59,13 @@ class AddressInputViewModel @Inject constructor( field = value addressParser = Parsers.getAddressParser(field)!! } - var nextAction: Int = -1 private val _uiState = MutableStateFlow(AddressInputUIState()) val uiState: StateFlow = _uiState.asStateFlow() private var paymentIntent: PaymentIntent? = null - private val _addressResult = MutableStateFlow(AddressInputResult()) - val addressResult: StateFlow + private var _addressResult = AddressInputResult() + val addressResult: AddressInputResult get() = _addressResult private val clipboardListener = ClipboardManager.OnPrimaryClipChangedListener { @@ -86,13 +84,6 @@ class AddressInputViewModel @Inject constructor( analyticsService.logEvent(AnalyticsConstants.AddressInput.SHOW_CLIPBOARD, mapOf()) } -// fun clearInput() { -// _uiState.value = AddressInputUIState(hasClipboardText = hasClipboardInput()) -// currency = "dash" -// addressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) -// _addressResult.value = AddressInputResult() -// } - fun setInput(text: String) { _uiState.value = _uiState.value.copy(addressInput = text) analyticsService.logEvent(AnalyticsConstants.AddressInput.ADDRESS_TAP, mapOf()) @@ -103,7 +94,7 @@ class AddressInputViewModel @Inject constructor( } fun setAddressResult(input: String) { - _addressResult.value = AddressInputResult(true, input, paymentIntent, currency, nextAction) + _addressResult = AddressInputResult(true, input, paymentIntent, currency) } private fun hasClipboardInput(): Boolean { @@ -144,8 +135,4 @@ class AddressInputViewModel @Inject constructor( super.onCleared() clipboardManager.removePrimaryClipChangedListener(clipboardListener) } - -// fun clearResult() { -// _addressResult.value = AddressInputResult() -// } } diff --git a/common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java b/common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java index cbd5f9bc14..fe797013c6 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java +++ b/common/src/main/java/org/dash/wallet/common/ui/scan/ScanViewModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright the original author or authors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ package org.dash.wallet.common.ui.scan; diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt index 2dbe9d9c1e..286088b83d 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt @@ -25,7 +25,7 @@ class MayaAddressInputFragment : AddressInputFragment() { safeNavigate( MayaAddressInputFragmentDirections.mayaAddressInputToEnterAmount( viewModel.currency, - viewModel.addressResult.value?.paymentIntent!! + viewModel.addressResult.paymentIntent!! ) ) // TODO: add event monitoring here diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt index 62a89a325e..b9f8f305d9 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt @@ -168,9 +168,8 @@ class MayaCryptoCurrencyPickerFragment : Fragment(R.layout.fragment_currency_pic safeNavigate( MayaCryptoCurrencyPickerFragmentDirections.mayaCurrencyPickerToAddressInput( pool.currencyCode, - "Convert DASH to BTC", - "BTC Address", - R.id.mayaAddressInput_to_EnterAmount + getString(R.string.maya_address_input_title, pool.currencyCode), + getString(R.string.maya_address_input_hint, pool.currencyCode) ) ) diff --git a/integrations/maya/src/main/res/navigation/nav_maya.xml b/integrations/maya/src/main/res/navigation/nav_maya.xml index 6bd2eae8f2..5693b8c491 100644 --- a/integrations/maya/src/main/res/navigation/nav_maya.xml +++ b/integrations/maya/src/main/res/navigation/nav_maya.xml @@ -47,9 +47,6 @@ android:name="hint" app:argType="string" /> - Convert Dash Convert Dash to From Dash Wallet to any crypto + %s Address + Convert DASH to %s BTC Bitcoin diff --git a/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt index fea783effc..ac0cdc9729 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt @@ -45,7 +45,7 @@ class DashAddressInputFragment : AddressInputFragment() { Preconditions.checkState(viewModel.currency == Constants.DASH_CURRENCY) } override fun continueAction() { - SendCoinsActivity.start(requireActivity(), viewModel.addressResult.value?.paymentIntent!!) + SendCoinsActivity.start(requireActivity(), viewModel.addressResult.paymentIntent!!) viewModel.logEvent(AnalyticsConstants.AddressInput.CONTINUE) requireActivity().overridePendingTransition(R.anim.slide_in_right, R.anim.activity_stay) } From 4a2a37088aa4d6aa537a49fb1bc1323d29b845e9 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Wed, 17 Jan 2024 12:57:34 -0800 Subject: [PATCH 14/60] refactor: make PaymentProcessors a class --- .../common/payments/parsers/AddressParser.kt | 16 ++-- .../parsers/DashPaymentIntentParser.kt | 14 ++-- .../payments/parsers/DashPaymentParsers.kt | 32 ++++++++ .../payments/parsers/PaymentIntentParser.kt | 2 +- .../parsers/{Parsers.kt => PaymentParsers.kt} | 27 +++---- .../ui/address_input/AddressInputViewModel.kt | 13 ++-- .../payments/parsers/BEP2AddressParser.kt | 49 +++++++++++++ .../parsers/BEP2PaymentIntentParser.kt | 73 +++++++++++++++++++ .../parsers/BitcoinPaymentIntentParser.kt | 31 ++++---- .../parsers/EthereumPaymentIntentParser.kt | 35 +++------ .../parsers/MayaPaymentIntentParser.kt | 38 ++++++++++ .../payments/parsers/MayaPaymentParsers.kt | 39 ++++++++++ .../parsers/RunePaymentIntentProcessor.kt | 20 +++++ .../payments/parsers/ThorAddressParser.kt | 20 +++++ .../maya/ui/MayaAddressInputFragment.kt | 9 +++ .../ui/MayaCryptoCurrencyPickerFragment.kt | 1 + .../integrations/maya/ui/MayaViewModel.kt | 2 + .../ui/send/DashAddressInputFragment.kt | 22 ++---- 18 files changed, 348 insertions(+), 95 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentParsers.kt rename common/src/main/java/org/dash/wallet/common/payments/parsers/{Parsers.kt => PaymentParsers.kt} (60%) create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2AddressParser.kt create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2PaymentIntentParser.kt rename {common/src/main/java/org/dash/wallet/common => integrations/maya/src/main/java/org/dash/wallet/integrations/maya}/payments/parsers/BitcoinPaymentIntentParser.kt (70%) rename {common/src/main/java/org/dash/wallet/common => integrations/maya/src/main/java/org/dash/wallet/integrations/maya}/payments/parsers/EthereumPaymentIntentParser.kt (65%) create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentIntentParser.kt create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/RunePaymentIntentProcessor.kt create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/ThorAddressParser.kt diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt index 1da4671d8c..b4607ed581 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt @@ -21,7 +21,7 @@ import org.bitcoinj.core.Address import org.bitcoinj.core.Base58 import org.bitcoinj.core.NetworkParameters -class AddressParser(pattern: String, val params: NetworkParameters?) { +open class AddressParser(pattern: String, val params: NetworkParameters?) { companion object { private val PATTERN_BITCOIN_ADDRESS = "[${Base58.ALPHABET.joinToString(separator = "")}]{20,40}" private const val PATTERN_ETHEREUM_ADDRESS = "0x[a-fA-F0-9]{40}" @@ -36,14 +36,6 @@ class AddressParser(pattern: String, val params: NetworkParameters?) { fun getEthereumAddressParser(): AddressParser { return AddressParser(PATTERN_ETHEREUM_ADDRESS, null) } - - fun get(currency: String, params: NetworkParameters? = null): AddressParser { - return when (currency) { - "bitcoin" -> getBitcoinAddressParser() - "dash" -> getDashAddressParser(params!!) - else -> getEthereumAddressParser() - } - } } private val addressPattern = Regex(pattern) @@ -60,7 +52,7 @@ class AddressParser(pattern: String, val params: NetworkParameters?) { val addressCandidate = match.value try { - params?.let { Address.fromString(params, addressCandidate) } + verifyAddress(addressCandidate) val startIndex = match.range.first val endIndex = match.range.last + 1 validRanges.add(startIndex..endIndex) @@ -71,4 +63,8 @@ class AddressParser(pattern: String, val params: NetworkParameters?) { return validRanges } + + protected open fun verifyAddress(addressCandidate: String) { + params?.let { Address.fromString(params, addressCandidate) } + } } diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt index c6f7fd842f..1ca35eb33a 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentIntentParser.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ package org.dash.wallet.common.payments.parsers @@ -51,7 +51,7 @@ import java.security.KeyStoreException import java.util.* class DashPaymentIntentParser(params: NetworkParameters) : PaymentIntentParser("dash", params) { - private val log = LoggerFactory.getLogger(PaymentIntentParser::class.java) + private val log = LoggerFactory.getLogger(DashPaymentIntentParser::class.java) private val addressParser = AddressParser.getDashAddressParser(params) override suspend fun parse(input: String): PaymentIntent { @@ -86,9 +86,9 @@ class DashPaymentIntentParser(params: NetworkParameters) : PaymentIntentParser(" } else if (inputStr.startsWith(Constants.DASH_SCHEME + ":")) { try { val bitcoinUri = BitcoinURI(null, inputStr) - val address = AddressUtil.getCorrectAddress(bitcoinUri, Constants.NETWORK_PARAMETERS) + val address = AddressUtil.getCorrectAddress(bitcoinUri, params) - if (address != null && Constants.NETWORK_PARAMETERS != address.parameters) { + if (address != null && params != address.parameters) { throw BitcoinURIParseException("mismatched network") } @@ -105,7 +105,7 @@ class DashPaymentIntentParser(params: NetworkParameters) : PaymentIntentParser(" } } else if (addressParser.exactMatch(inputStr)) { try { - val address = Address.fromString(Constants.NETWORK_PARAMETERS, inputStr) + val address = Address.fromString(params, inputStr) return@withContext PaymentIntent.fromAddress(address, null) } catch (ex: AddressFormatException) { log.info("got invalid address", ex) @@ -216,7 +216,7 @@ class DashPaymentIntentParser(params: NetworkParameters) : PaymentIntentParser(" ) } - if (paymentSession.networkParameters != Constants.NETWORK_PARAMETERS) { + if (paymentSession.networkParameters != params) { throw InvalidNetwork( "cannot handle payment request network: " + paymentSession.networkParameters ) diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentParsers.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentParsers.kt new file mode 100644 index 0000000000..f401e7412d --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/DashPaymentParsers.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.bitcoinj.core.NetworkParameters +import org.dash.wallet.common.util.Constants + +class DashPaymentParsers(val params: NetworkParameters) : PaymentParsers() { + init { + add( + Constants.DASH_CURRENCY, + Constants.DASH_CURRENCY, + DashPaymentIntentParser(params), + AddressParser.getDashAddressParser(params) + ) + } +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt index 9b2bcbdbbf..0b64cd7719 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentIntentParser.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentParsers.kt similarity index 60% rename from common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt rename to common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentParsers.kt index 56de276561..ad76b0baf6 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/Parsers.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/PaymentParsers.kt @@ -17,34 +17,27 @@ package org.dash.wallet.common.payments.parsers -// TODO: this may need to be a class, so we can define a custom set of parsers -// that a #[AddressInputViewModel] can use to parse addresses and payments intents -// for Maya, there may be custom parsers that generate the correct PaymentIntent -// that we don't want used or visible to the wallet module. Currently this has global scope. +/** + * Payment parsers include PaymentIntentParsers and AddressParsers for a list of coins + * + * @constructor Create empty object with no parsers + */ -object Parsers { +open class PaymentParsers { private val paymentIntentParsers = hashMapOf() private val addressParsers = hashMapOf() - init { - add("bitcoin", "btc", BitcoinPaymentIntentParser(), AddressParser.getBitcoinAddressParser()) - add("ethereum", "eth", EthereumPaymentIntentParser(), AddressParser.getEthereumAddressParser()) - } - - @JvmStatic fun add(currency: String, code: String, paymentIntentParser: PaymentIntentParser, addressParser: AddressParser) { - paymentIntentParsers[currency] = paymentIntentParser - paymentIntentParsers[code] = paymentIntentParser - addressParsers[currency] = addressParser - addressParsers[code] = addressParser + paymentIntentParsers[currency.lowercase()] = paymentIntentParser + paymentIntentParsers[code.lowercase()] = paymentIntentParser + addressParsers[currency.lowercase()] = addressParser + addressParsers[code.lowercase()] = addressParser } - @JvmStatic fun getPaymentIntentParser(currency: String): PaymentIntentParser? { return paymentIntentParsers[currency.lowercase()] } - @JvmStatic fun getAddressParser(currency: String): AddressParser? { return addressParsers[currency.lowercase()] } diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt index 21b1d3bd5d..98177dad67 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt @@ -26,8 +26,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import org.dash.wallet.common.WalletDataProvider import org.dash.wallet.common.data.PaymentIntent -import org.dash.wallet.common.payments.parsers.AddressParser -import org.dash.wallet.common.payments.parsers.Parsers +import org.dash.wallet.common.payments.parsers.PaymentParsers import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.util.Constants @@ -53,11 +52,13 @@ class AddressInputViewModel @Inject constructor( private val analyticsService: AnalyticsService, walletDataProvider: WalletDataProvider ): ViewModel() { - private var addressParser: AddressParser = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) + lateinit var paymentParsers: PaymentParsers + //private lateinit var addressParser: AddressParser// = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) + //private lateinit var paymentIntentParser: PaymentIntentParser var currency: String = Constants.DASH_CURRENCY set(value) { field = value - addressParser = Parsers.getAddressParser(field)!! + //addressParser = paymentParsers.getAddressParser(field)!! } private val _uiState = MutableStateFlow(AddressInputUIState()) @@ -79,7 +80,7 @@ class AddressInputViewModel @Inject constructor( fun showClipboardContent() { val text = getClipboardInput() - val addressRanges = addressParser.findAll(text) + val addressRanges = paymentParsers.getAddressParser(currency)!!.findAll(text) _uiState.value = _uiState.value.copy(clipboardText = text, addressRanges = addressRanges) analyticsService.logEvent(AnalyticsConstants.AddressInput.SHOW_CLIPBOARD, mapOf()) } @@ -90,7 +91,7 @@ class AddressInputViewModel @Inject constructor( } suspend fun parsePaymentIntent(input: String) { - paymentIntent = Parsers.getPaymentIntentParser(currency)!!.parse(input) + paymentIntent = paymentParsers.getPaymentIntentParser(currency)!!.parse(input) } fun setAddressResult(input: String) { diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2AddressParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2AddressParser.kt new file mode 100644 index 0000000000..3b66c631a9 --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2AddressParser.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.maya.payments.parsers + +import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.Base58 +import org.dash.wallet.common.payments.parsers.AddressParser + +open class BEP2AddressParser(val prefix: String) : AddressParser( + "[$prefix${Base58.ALPHABET.joinToString(separator = "")}]{24}", + null +) { + override fun verifyAddress(address: String) { + if (!address.startsWith(prefix)) { + throw AddressFormatException.InvalidPrefix("$prefix is not allowed for BEP2 $prefix addres") + } + if (address.length != 42) { + throw AddressFormatException.InvalidDataLength( + "length is incorrect for BEP2 $prefix address, ${address.length} != 42" + ) + } + + try { + val decoded = Base58.decode(address.substring(4)) + if (decoded.size != 24) { + throw AddressFormatException.InvalidDataLength( + "length is invalid after decoding base58 for BEP2 $prefix addres, ${decoded.size} != 24" + ) + } + } catch (e: Exception) { + throw AddressFormatException("BEP2 $prefix address is invalid: ${e.message}") + } + } +} diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2PaymentIntentParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2PaymentIntentParser.kt new file mode 100644 index 0000000000..9a6d1d7b3c --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BEP2PaymentIntentParser.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.maya.payments.parsers + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.bitcoinj.core.AddressFormatException +import org.dash.wallet.common.R +import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.payments.parsers.PaymentIntentParserException +import org.dash.wallet.common.util.ResourceString +import org.slf4j.LoggerFactory + +open class BEP2PaymentIntentParser(currency: String, prefix: String, asset: String) : + MayaPaymentIntentParser(currency, asset, null) { + + private val log = LoggerFactory.getLogger(BEP2PaymentIntentParser::class.java) + private val addressParser = BEP2AddressParser(prefix) + override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { + if (input.startsWith("$currency:") || input.startsWith("${currency.uppercase()}:")) { + try { + val hexAddress = input.substring(currency.length) + return@withContext createPaymentIntent(hexAddress) + } catch (ex: Exception) { + log.info("got invalid uri: '$input'", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf(input) + ) + ) + } + } else if (addressParser.exactMatch(input)) { + try { + return@withContext createPaymentIntent(input) + } catch (ex: AddressFormatException) { + log.info("got invalid address", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf() + ) + ) + } + } + + log.info("cannot classify: '{}'", input) + throw PaymentIntentParserException( + IllegalArgumentException(input), + ResourceString( + R.string.error, + listOf(input) + ) + ) + } +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt similarity index 70% rename from common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt rename to integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt index afdd9b854a..568de18b5a 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinPaymentIntentParser.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -12,55 +12,54 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -package org.dash.wallet.common.payments.parsers +package org.dash.wallet.integrations.maya.payments.parsers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.bitcoinj.core.Address import org.bitcoinj.core.AddressFormatException -import org.bitcoinj.core.NetworkParameters -import org.bitcoinj.params.MainNetParams import org.bitcoinj.uri.BitcoinURI import org.bitcoinj.uri.BitcoinURIParseException import org.dash.wallet.common.R import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.payments.parsers.AddressParser +import org.dash.wallet.common.payments.parsers.BitcoinMainNetParams +import org.dash.wallet.common.payments.parsers.PaymentIntentParserException import org.dash.wallet.common.util.ResourceString import org.slf4j.LoggerFactory -class BitcoinPaymentIntentParser : PaymentIntentParser("bitcoin", BitcoinMainNetParams()) { +class BitcoinPaymentIntentParser : MayaPaymentIntentParser("BTC", "BTC.BTC", BitcoinMainNetParams()) { private val log = LoggerFactory.getLogger(BitcoinPaymentIntentParser::class.java) private val addressParser = AddressParser.getBitcoinAddressParser() override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { - var inputStr = input - - if (inputStr.startsWith("$currency:") || inputStr.startsWith("${currency.uppercase()}:")) { + if (input.startsWith("$currency:") || input.startsWith("${currency.uppercase()}:")) { try { - val bitcoinUri = BitcoinURI(params, inputStr) + val bitcoinUri = BitcoinURI(params, input) val address = bitcoinUri.address if (address != null && params != null && params != address.parameters) { throw BitcoinURIParseException("mismatched network") } - return@withContext PaymentIntent.fromBitcoinUri(bitcoinUri) + return@withContext createPaymentIntent(bitcoinUri.address.toString()) } catch (ex: BitcoinURIParseException) { - log.info("got invalid bitcoin uri: '$inputStr'", ex) + log.info("got invalid bitcoin uri: '$input'", ex) throw PaymentIntentParserException( ex, ResourceString( R.string.error, - listOf(inputStr) + listOf(input) ) ) } - } else if (addressParser.exactMatch(inputStr)) { + } else if (addressParser.exactMatch(input)) { try { - val address = Address.fromString(params, inputStr) - return@withContext PaymentIntent.fromAddress(address, null) + val address = Address.fromString(params, input) + return@withContext createPaymentIntent(address.toString()) } catch (ex: AddressFormatException) { log.info("got invalid address", ex) throw PaymentIntentParserException( diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/EthereumPaymentIntentParser.kt similarity index 65% rename from common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt rename to integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/EthereumPaymentIntentParser.kt index 1cbd448606..9287db35e1 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/EthereumPaymentIntentParser.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/EthereumPaymentIntentParser.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Dash Core Group. + * Copyright 2024 Dash Core Group. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -12,45 +12,43 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -package org.dash.wallet.common.payments.parsers +package org.dash.wallet.integrations.maya.payments.parsers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.bitcoinj.core.AddressFormatException -import org.bitcoinj.core.Coin -import org.bitcoinj.script.ScriptBuilder import org.dash.wallet.common.R import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.payments.parsers.AddressParser +import org.dash.wallet.common.payments.parsers.PaymentIntentParserException import org.dash.wallet.common.util.ResourceString import org.slf4j.LoggerFactory -class EthereumPaymentIntentParser : PaymentIntentParser("ethereum", null) { +class EthereumPaymentIntentParser(asset: String) : MayaPaymentIntentParser("ethereum", asset, null) { private val log = LoggerFactory.getLogger(EthereumPaymentIntentParser::class.java) private val addressParser = AddressParser.getEthereumAddressParser() override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { - var inputStr = input - - if (inputStr.startsWith("$currency:") || inputStr.startsWith("${currency.uppercase()}:")) { + if (input.startsWith("$currency:") || input.startsWith("${currency.uppercase()}:")) { try { - val hexAddress = inputStr.substring(currency.length) + val hexAddress = input.substring(currency.length) return@withContext createPaymentIntent(hexAddress) } catch (ex: Exception) { - log.info("got invalid uri: '$inputStr'", ex) + log.info("got invalid uri: '$input'", ex) throw PaymentIntentParserException( ex, ResourceString( R.string.error, - listOf(inputStr) + listOf(input) ) ) } - } else if (addressParser.exactMatch(inputStr)) { + } else if (addressParser.exactMatch(input)) { try { - return@withContext createPaymentIntent(inputStr) + return@withContext createPaymentIntent(input) } catch (ex: AddressFormatException) { log.info("got invalid address", ex) throw PaymentIntentParserException( @@ -72,13 +70,4 @@ class EthereumPaymentIntentParser : PaymentIntentParser("ethereum", null) { ) ) } - - private fun createPaymentIntent(inputStr: String): PaymentIntent { - val metadata = "SWAP:ETH.ETH:${inputStr.substring(2)}" - return PaymentIntent( - null, "ethereum", null, - arrayOf(PaymentIntent.Output(Coin.ZERO, ScriptBuilder.createOpReturnScript(metadata.toByteArray()))), - "ethereum network", null, null, null, null - ) - } } diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentIntentParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentIntentParser.kt new file mode 100644 index 0000000000..6263198586 --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentIntentParser.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.integrations.maya.payments.parsers + +import org.bitcoinj.core.Coin +import org.bitcoinj.core.NetworkParameters +import org.bitcoinj.script.ScriptBuilder +import org.dash.wallet.common.data.PaymentIntent +import org.dash.wallet.common.payments.parsers.PaymentIntentParser + +abstract class MayaPaymentIntentParser(currency: String, val asset: String, params: NetworkParameters?) : + PaymentIntentParser( + currency, + params + ) { + fun createPaymentIntent(inputStr: String): PaymentIntent { + val metadata = "SWAP:$asset:${inputStr.substring(2)}" + return PaymentIntent( + null, "maya DASH pool", null, + arrayOf(PaymentIntent.Output(Coin.ZERO, ScriptBuilder.createOpReturnScript(metadata.toByteArray()))), + "maya swap to $currency", null, null, null, null + ) + } +} diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt new file mode 100644 index 0000000000..caa45aafd5 --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.maya.payments.parsers + +import org.dash.wallet.common.payments.parsers.AddressParser +import org.dash.wallet.common.payments.parsers.PaymentParsers + +class MayaPaymentParsers : PaymentParsers() { + init { + add("bitcoin", "btc", BitcoinPaymentIntentParser(), AddressParser.getBitcoinAddressParser()) + add("ethereum", "eth", EthereumPaymentIntentParser("ETH.ETH"), AddressParser.getEthereumAddressParser()) + add("usdc", "usdc", EthereumPaymentIntentParser("ETH.USDC"), AddressParser.getEthereumAddressParser()) + add("tether", "usdt", EthereumPaymentIntentParser("ETH.USDC"), AddressParser.getEthereumAddressParser()) + add( + "Wrapped stETH", + "wsteth", + EthereumPaymentIntentParser("ETH.WSTETH"), + AddressParser.getEthereumAddressParser() + ) + add("rune", "rune", RunePaymentIntentProcessor(), RuneAddressParser()) + add("kujira", "kuji", BEP2PaymentIntentParser("kuji", "kujira", "KIJI.KUJI"), BEP2AddressParser("kujira")) + add("usk", "usk", BEP2PaymentIntentParser("usk", "usk", "KUJI.USK"), BEP2AddressParser("usk")) + } +} diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/RunePaymentIntentProcessor.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/RunePaymentIntentProcessor.kt new file mode 100644 index 0000000000..2a85cdb5c1 --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/RunePaymentIntentProcessor.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.maya.payments.parsers + +class RunePaymentIntentProcessor : BEP2PaymentIntentParser("RUNE", "thor", "THOR.RUNE") diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/ThorAddressParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/ThorAddressParser.kt new file mode 100644 index 0000000000..744a60bd2b --- /dev/null +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/ThorAddressParser.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.integrations.maya.payments.parsers + +class RuneAddressParser : BEP2AddressParser("thor") diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt index 286088b83d..2a2b964fcf 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputFragment.kt @@ -17,10 +17,19 @@ package org.dash.wallet.integrations.maya.ui +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels import org.dash.wallet.common.ui.address_input.AddressInputFragment import org.dash.wallet.common.util.safeNavigate class MayaAddressInputFragment : AddressInputFragment() { + private val mayaViewModel by viewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.paymentParsers = mayaViewModel.paymentParsers + } + override fun continueAction() { safeNavigate( MayaAddressInputFragmentDirections.mayaAddressInputToEnterAmount( diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt index b9f8f305d9..7c15200c93 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaCryptoCurrencyPickerFragment.kt @@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import androidx.navigation.navGraphViewModels import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import org.dash.wallet.common.ui.decorators.ListDividerDecorator diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaViewModel.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaViewModel.kt index 1f06ce1450..c7c3eef2cc 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaViewModel.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaViewModel.kt @@ -34,6 +34,7 @@ import org.dash.wallet.common.util.isCurrencyFirst import org.dash.wallet.integrations.maya.api.FiatExchangeRateProvider import org.dash.wallet.integrations.maya.api.MayaApi import org.dash.wallet.integrations.maya.model.PoolInfo +import org.dash.wallet.integrations.maya.payments.parsers.MayaPaymentParsers import org.dash.wallet.integrations.maya.utils.MayaConfig import org.slf4j.LoggerFactory import java.util.Locale @@ -74,6 +75,7 @@ class MayaViewModel @Inject constructor( get() = globalConfig.format.noCode() val poolList = MutableStateFlow>(listOf()) + val paymentParsers = MayaPaymentParsers() init { // TODO: is this really needed? we don't support DASH swaps diff --git a/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt b/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt index ac0cdc9729..319fc2c7a2 100644 --- a/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/send/DashAddressInputFragment.kt @@ -20,29 +20,21 @@ package de.schildbach.wallet.ui.send import android.os.Bundle import android.view.View import com.google.common.base.Preconditions +import de.schildbach.wallet.Constants import de.schildbach.wallet_test.R -import org.dash.wallet.common.payments.parsers.AddressParser.Companion.getDashAddressParser -import org.dash.wallet.common.payments.parsers.DashPaymentIntentParser -import org.dash.wallet.common.payments.parsers.Parsers +import org.dash.wallet.common.payments.parsers.DashPaymentParsers import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.ui.address_input.AddressInputFragment -import org.dash.wallet.common.util.Constants - +import org.dash.wallet.common.util.Constants.DASH_CURRENCY class DashAddressInputFragment : AddressInputFragment() { - init { - if (Parsers.getAddressParser(Constants.DASH_CURRENCY) == null) { - Parsers.add( - "dash", - "DASH", - DashPaymentIntentParser(de.schildbach.wallet.Constants.NETWORK_PARAMETERS), - getDashAddressParser(de.schildbach.wallet.Constants.NETWORK_PARAMETERS) - ) - } + companion object { + private val paymentParsers = DashPaymentParsers(Constants.NETWORK_PARAMETERS) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - Preconditions.checkState(viewModel.currency == Constants.DASH_CURRENCY) + viewModel.paymentParsers = paymentParsers + Preconditions.checkState(viewModel.currency == DASH_CURRENCY) } override fun continueAction() { SendCoinsActivity.start(requireActivity(), viewModel.addressResult.paymentIntent!!) From a39466e6469836c3312339aae062834377dbeb5a Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Wed, 17 Jan 2024 13:41:24 -0800 Subject: [PATCH 15/60] fix: support bitcoin segwit --- .../common/payments/parsers/AddressParser.kt | 8 +- .../common/payments/parsers/Bech32.java | 190 ++++++++++ .../payments/parsers/Bech32AddressParser.kt | 27 ++ .../payments/parsers/BitcoinAddressParser.kt | 37 ++ .../payments/parsers/BitcoinMainNetParams.kt | 11 +- .../payments/parsers/SegwitAddress.java | 326 ++++++++++++++++++ .../parsers/BitcoinPaymentIntentParser.kt | 25 +- .../payments/parsers/MayaPaymentParsers.kt | 4 +- 8 files changed, 613 insertions(+), 15 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32.java create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32AddressParser.kt create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinAddressParser.kt create mode 100644 common/src/main/java/org/dash/wallet/common/payments/parsers/SegwitAddress.java diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt index b4607ed581..681cfa8eb6 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/AddressParser.kt @@ -23,14 +23,15 @@ import org.bitcoinj.core.NetworkParameters open class AddressParser(pattern: String, val params: NetworkParameters?) { companion object { - private val PATTERN_BITCOIN_ADDRESS = "[${Base58.ALPHABET.joinToString(separator = "")}]{20,40}" + val PATTERN_BITCOIN_ADDRESS = "[${Base58.ALPHABET.joinToString(separator = "")}]{20,40}" private const val PATTERN_ETHEREUM_ADDRESS = "0x[a-fA-F0-9]{40}" + const val PATTERN_BECH32_ADDRESS = "1[a-z0-9]{25,39}" fun getDashAddressParser(params: NetworkParameters): AddressParser { return AddressParser(PATTERN_BITCOIN_ADDRESS, params) } - fun getBitcoinAddressParser(): AddressParser { - return AddressParser(PATTERN_BITCOIN_ADDRESS, BitcoinMainNetParams()) + fun getBase58AddressParser(params: NetworkParameters? = null): AddressParser { + return AddressParser(PATTERN_BITCOIN_ADDRESS, params) } fun getEthereumAddressParser(): AddressParser { @@ -58,6 +59,7 @@ open class AddressParser(pattern: String, val params: NetworkParameters?) { validRanges.add(startIndex..endIndex) } catch (e: Exception) { // Invalid address, skipping + println(e) } } diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32.java b/common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32.java new file mode 100644 index 0000000000..2e9a7b7ccb --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32.java @@ -0,0 +1,190 @@ +/* + * Copyright 2018 Coinomi Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dash.wallet.common.payments.parsers; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkArgument; + +import org.bitcoinj.core.AddressFormatException; + +import java.util.Arrays; +import java.util.Locale; + +/** + *

Implementation of the Bech32 encoding.

+ * + *

See BIP350 and + * BIP173 for details.

+ */ +public class Bech32 { + /** The Bech32 character set for encoding. */ + private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + + /** The Bech32 character set for decoding. */ + private static final byte[] CHARSET_REV = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 + }; + + private static final int BECH32_CONST = 1; + private static final int BECH32M_CONST = 0x2bc830a3; + + public enum Encoding { BECH32, BECH32M } + + public static class Bech32Data { + public final Encoding encoding; + public final String hrp; + public final byte[] data; + + private Bech32Data(final Encoding encoding, final String hrp, final byte[] data) { + this.encoding = encoding; + this.hrp = hrp; + this.data = data; + } + } + + /** Find the polynomial with value coefficients mod the generator as 30-bit. */ + private static int polymod(final byte[] values) { + int c = 1; + for (byte v_i: values) { + int c0 = (c >>> 25) & 0xff; + c = ((c & 0x1ffffff) << 5) ^ (v_i & 0xff); + if ((c0 & 1) != 0) c ^= 0x3b6a57b2; + if ((c0 & 2) != 0) c ^= 0x26508e6d; + if ((c0 & 4) != 0) c ^= 0x1ea119fa; + if ((c0 & 8) != 0) c ^= 0x3d4233dd; + if ((c0 & 16) != 0) c ^= 0x2a1462b3; + } + return c; + } + + /** Expand a HRP for use in checksum computation. */ + private static byte[] expandHrp(final String hrp) { + int hrpLength = hrp.length(); + byte ret[] = new byte[hrpLength * 2 + 1]; + for (int i = 0; i < hrpLength; ++i) { + int c = hrp.charAt(i) & 0x7f; // Limit to standard 7-bit ASCII + ret[i] = (byte) ((c >>> 5) & 0x07); + ret[i + hrpLength + 1] = (byte) (c & 0x1f); + } + ret[hrpLength] = 0; + return ret; + } + + /** Verify a checksum. */ + private static @Nullable + Encoding verifyChecksum(final String hrp, final byte[] values) { + byte[] hrpExpanded = expandHrp(hrp); + byte[] combined = new byte[hrpExpanded.length + values.length]; + System.arraycopy(hrpExpanded, 0, combined, 0, hrpExpanded.length); + System.arraycopy(values, 0, combined, hrpExpanded.length, values.length); + final int check = polymod(combined); + if (check == BECH32_CONST) + return Encoding.BECH32; + else if (check == BECH32M_CONST) + return Encoding.BECH32M; + else + return null; + } + + /** Create a checksum. */ + private static byte[] createChecksum(final Encoding encoding, final String hrp, final byte[] values) { + byte[] hrpExpanded = expandHrp(hrp); + byte[] enc = new byte[hrpExpanded.length + values.length + 6]; + System.arraycopy(hrpExpanded, 0, enc, 0, hrpExpanded.length); + System.arraycopy(values, 0, enc, hrpExpanded.length, values.length); + int mod = polymod(enc) ^ (encoding == Encoding.BECH32 ? BECH32_CONST : BECH32M_CONST); + byte[] ret = new byte[6]; + for (int i = 0; i < 6; ++i) { + ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31); + } + return ret; + } + + /** Encode a Bech32 string. */ + public static String encode(final Bech32Data bech32) { + return encode(bech32.encoding, bech32.hrp, bech32.data); + } + + /** Encode a Bech32 string. */ + public static String encode(Encoding encoding, String hrp, final byte[] values) { + checkArgument(hrp.length() >= 1, "Human-readable part is too short"); + checkArgument(hrp.length() <= 83, "Human-readable part is too long"); + hrp = hrp.toLowerCase(Locale.ROOT); + byte[] checksum = createChecksum(encoding, hrp, values); + byte[] combined = new byte[values.length + checksum.length]; + System.arraycopy(values, 0, combined, 0, values.length); + System.arraycopy(checksum, 0, combined, values.length, checksum.length); + StringBuilder sb = new StringBuilder(hrp.length() + 1 + combined.length); + sb.append(hrp); + sb.append('1'); + for (byte b : combined) { + sb.append(CHARSET.charAt(b)); + } + return sb.toString(); + } + + /** @deprecated use {@link #encode(Encoding, String, byte[])} */ + @Deprecated + public static String encode(String hrp, byte[] values) { + return encode(Encoding.BECH32, hrp, values); + } + + /** Decode a Bech32 string. */ + public static Bech32Data decode(final String str) throws AddressFormatException { + boolean lower = false, upper = false; + if (str.length() < 8) + throw new AddressFormatException.InvalidDataLength("Input too short: " + str.length()); + if (str.length() > 90) + throw new AddressFormatException.InvalidDataLength("Input too long: " + str.length()); + for (int i = 0; i < str.length(); ++i) { + char c = str.charAt(i); + if (c < 33 || c > 126) throw new AddressFormatException.InvalidCharacter(c, i); + if (c >= 'a' && c <= 'z') { + if (upper) + throw new AddressFormatException.InvalidCharacter(c, i); + lower = true; + } + if (c >= 'A' && c <= 'Z') { + if (lower) + throw new AddressFormatException.InvalidCharacter(c, i); + upper = true; + } + } + final int pos = str.lastIndexOf('1'); + if (pos < 1) throw new AddressFormatException.InvalidPrefix("Missing human-readable part"); + final int dataPartLength = str.length() - 1 - pos; + if (dataPartLength < 6) throw new AddressFormatException.InvalidDataLength("Data part too short: " + dataPartLength); + byte[] values = new byte[dataPartLength]; + for (int i = 0; i < dataPartLength; ++i) { + char c = str.charAt(i + pos + 1); + if (CHARSET_REV[c] == -1) throw new AddressFormatException.InvalidCharacter(c, i + pos + 1); + values[i] = CHARSET_REV[c]; + } + String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT); + Encoding encoding = verifyChecksum(hrp, values); + if (encoding == null) throw new AddressFormatException.InvalidChecksum(); + return new Bech32Data(encoding, hrp, Arrays.copyOfRange(values, 0, values.length - 6)); + } +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32AddressParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32AddressParser.kt new file mode 100644 index 0000000000..75fad62881 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/Bech32AddressParser.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.bitcoinj.core.NetworkParameters + +class Bech32AddressParser(hrp: String, params: NetworkParameters?) : AddressParser( + "${hrp}$PATTERN_BECH32_ADDRESS", + params +) { + constructor(params: NetworkParameters) : this(params.segwitAddressHrp, params) +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinAddressParser.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinAddressParser.kt new file mode 100644 index 0000000000..1437df15cd --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinAddressParser.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers + +import org.bitcoinj.core.Address +import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.NetworkParameters + +class BitcoinAddressParser(params: NetworkParameters) : AddressParser( + "$PATTERN_BITCOIN_ADDRESS|${params.segwitAddressHrp}$PATTERN_BECH32_ADDRESS", + params +) { + override fun verifyAddress(addressCandidate: String) { + params?.let { + try { + Address.fromString(params, addressCandidate) + } catch (e: AddressFormatException) { + SegwitAddress.fromBech32(params, addressCandidate) + } + } + } +} diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt index aee11ada48..e9abdd74ab 100644 --- a/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/BitcoinMainNetParams.kt @@ -17,11 +17,18 @@ package org.dash.wallet.common.payments.parsers -import org.bitcoinj.params.MainNetParams +import org.bitcoinj.params.AbstractBitcoinNetParams -class BitcoinMainNetParams : MainNetParams() { +open class SegwitNetworkParams() : AbstractBitcoinNetParams() { + override fun getPaymentProtocolId(): String { + return PAYMENT_PROTOCOL_ID_MAINNET + } +} + +class BitcoinMainNetParams : SegwitNetworkParams() { init { addressHeader = 0 // addresses starting with 1 p2shHeader = 5 // addresses starting with 3 + segwitAddressHrp = "bc" } } diff --git a/common/src/main/java/org/dash/wallet/common/payments/parsers/SegwitAddress.java b/common/src/main/java/org/dash/wallet/common/payments/parsers/SegwitAddress.java new file mode 100644 index 0000000000..7f2f512f92 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/payments/parsers/SegwitAddress.java @@ -0,0 +1,326 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.payments.parsers; + +/* + * Copyright by the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +import java.io.ByteArrayOutputStream; + +import javax.annotation.Nullable; + +import com.google.common.primitives.UnsignedBytes; + +import org.bitcoinj.core.AbstractAddress; +import org.bitcoinj.core.Address; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.params.Networks; +import org.bitcoinj.script.Script; + +/** + *

Implementation of native segwit addresses. They are composed of two parts:

+ * + *
    + *
  • A human-readable part (HRP) which is a string the specifies the network. See + * {@link NetworkParameters#getSegwitAddressHrp()}.
  • + *
  • A data part, containing the witness version (encoded as an OP_N operator) and program (encoded by re-arranging + * bits into groups of 5).
  • + *
+ * + *

See BIP350 and + * BIP173 for details.

+ * + *

However, you don't need to care about the internals. Use {@link #fromBech32(NetworkParameters, String)}, + * {@link #fromHash(NetworkParameters, byte[])} or {@link #fromKey(NetworkParameters, ECKey)} to construct a native + * segwit address.

+ */ +public class SegwitAddress extends AbstractAddress { + public static final int WITNESS_PROGRAM_LENGTH_PKH = 20; + public static final int WITNESS_PROGRAM_LENGTH_SH = 32; + public static final int WITNESS_PROGRAM_LENGTH_TR = 32; + public static final int WITNESS_PROGRAM_MIN_LENGTH = 2; + public static final int WITNESS_PROGRAM_MAX_LENGTH = 40; + + /** + * Private constructor. Use {@link #fromBech32(NetworkParameters, String)}, + * {@link #fromHash(NetworkParameters, byte[])} or {@link #fromKey(NetworkParameters, ECKey)}. + * + * @param params + * network this address is valid for + * @param witnessVersion + * version number between 0 and 16 + * @param witnessProgram + * hash of pubkey, pubkey or script (depending on version) + */ + private SegwitAddress(NetworkParameters params, int witnessVersion, byte[] witnessProgram) + throws AddressFormatException { + this(params, encode(witnessVersion, witnessProgram)); + } + + /** + * Helper for the above constructor. + */ + private static byte[] encode(int witnessVersion, byte[] witnessProgram) throws AddressFormatException { + byte[] convertedProgram = convertBits(witnessProgram, 0, witnessProgram.length, 8, 5, true); + byte[] bytes = new byte[1 + convertedProgram.length]; + bytes[0] = (byte) (witnessVersion & 0xff); + System.arraycopy(convertedProgram, 0, bytes, 1, convertedProgram.length); + return bytes; + } + + /** + * Private constructor. Use {@link #fromBech32(NetworkParameters, String)}, + * {@link #fromHash(NetworkParameters, byte[])} or {@link #fromKey(NetworkParameters, ECKey)}. + * + * @param params + * network this address is valid for + * @param data + * in segwit address format, before bit re-arranging and bech32 encoding + * @throws AddressFormatException + * if any of the sanity checks fail + */ + private SegwitAddress(NetworkParameters params, byte[] data) throws AddressFormatException { + super(params, data); + if (data.length < 1) + throw new AddressFormatException.InvalidDataLength("Zero data found"); + final int witnessVersion = getWitnessVersion(); + if (witnessVersion < 0 || witnessVersion > 16) + throw new AddressFormatException("Invalid script version: " + witnessVersion); + byte[] witnessProgram = getWitnessProgram(); + if (witnessProgram.length < WITNESS_PROGRAM_MIN_LENGTH || witnessProgram.length > WITNESS_PROGRAM_MAX_LENGTH) + throw new AddressFormatException.InvalidDataLength("Invalid length: " + witnessProgram.length); + // Check script length for version 0 + if (witnessVersion == 0 && witnessProgram.length != WITNESS_PROGRAM_LENGTH_PKH + && witnessProgram.length != WITNESS_PROGRAM_LENGTH_SH) + throw new AddressFormatException.InvalidDataLength( + "Invalid length for address version 0: " + witnessProgram.length); + } + + /** + * Returns the witness version in decoded form. Only versions 0 and 1 are in use right now. + * + * @return witness version, between 0 and 16 + */ + public int getWitnessVersion() { + return bytes[0] & 0xff; + } + + /** + * Returns the witness program in decoded form. + * + * @return witness program + */ + public byte[] getWitnessProgram() { + // skip version byte + return convertBits(bytes, 1, bytes.length - 1, 5, 8, false); + } + + @Override + public byte[] getHash() { + return getWitnessProgram(); + } + + /** + * Get the type of output script that will be used for sending to the address. This is either + * {@link Script.ScriptType#P2WPKH} or {@link Script.ScriptType#P2WSH}. + * + * @return type of output script + */ + @Override + public Script.ScriptType getOutputScriptType() { + int version = getWitnessVersion(); + if (version == 0) { + int programLength = getWitnessProgram().length; + if (programLength == WITNESS_PROGRAM_LENGTH_PKH) + return Script.ScriptType.P2WPKH; + if (programLength == WITNESS_PROGRAM_LENGTH_SH) + return Script.ScriptType.P2WSH; + throw new IllegalStateException(); // cannot happen + } else if (version == 1) { + int programLength = getWitnessProgram().length; + //if (programLength == WITNESS_PROGRAM_LENGTH_TR) + // return Script.ScriptType.P2TR; + throw new IllegalStateException(); // cannot happen + } + throw new IllegalStateException("cannot handle: " + version); + } + + @Override + public String toString() { + return toBech32(); + } + + /** + * Construct a {@link SegwitAddress} from its textual form. + * + * @param params + * expected network this address is valid for, or null if the network should be derived from the bech32 + * @param bech32 + * bech32-encoded textual form of the address + * @return constructed address + * @throws AddressFormatException + * if something about the given bech32 address isn't right + */ + public static SegwitAddress fromBech32(@Nullable NetworkParameters params, String bech32) + throws AddressFormatException { + Bech32.Bech32Data bechData = Bech32.decode(bech32); + if (params == null) { + for (NetworkParameters p : Networks.get()) { + if (bechData.hrp.equals(p.getSegwitAddressHrp())) + return fromBechData(p, bechData); + } + throw new AddressFormatException.InvalidPrefix("No network found for " + bech32); + } else { + if (bechData.hrp.equals(params.getSegwitAddressHrp())) + return fromBechData(params, bechData); + throw new AddressFormatException("Wrong Network: ${bechData.hrp}"); + } + } + + private static SegwitAddress fromBechData(NetworkParameters params, Bech32.Bech32Data bechData) { + final SegwitAddress address = new SegwitAddress(params, bechData.data); + final int witnessVersion = address.getWitnessVersion(); + if ((witnessVersion == 0 && bechData.encoding != Bech32.Encoding.BECH32) || + (witnessVersion != 0 && bechData.encoding != Bech32.Encoding.BECH32M)) + throw new AddressFormatException("Unexpected witness version: " + witnessVersion); + return address; + } + + /** + * Construct a {@link SegwitAddress} that represents the given hash, which is either a pubkey hash or a script hash. + * The resulting address will be either a P2WPKH or a P2WSH type of address. + * + * @param params + * network this address is valid for + * @param hash + * 20-byte pubkey hash or 32-byte script hash + * @return constructed address + */ + public static SegwitAddress fromHash(NetworkParameters params, byte[] hash) { + return new SegwitAddress(params, 0, hash); + } + + /** + * Construct a {@link SegwitAddress} that represents the given program, which is either a pubkey, a pubkey hash + * or a script hash – depending on the script version. The resulting address will be either a P2WPKH, a P2WSH or + * a P2TR type of address. + * + * @param params + * network this address is valid for + * @param witnessVersion + * version number between 0 and 16 + * @param witnessProgram + * version dependent witness program + * @return constructed address + */ + public static SegwitAddress fromProgram(NetworkParameters params, int witnessVersion, byte[] witnessProgram) { + return new SegwitAddress(params, witnessVersion, witnessProgram); + } + + /** + * Construct a {@link SegwitAddress} that represents the public part of the given {@link ECKey}. Note that an + * address is derived from a hash of the public key and is not the public key itself. + * + * @param params + * network this address is valid for + * @param key + * only the public part is used + * @return constructed address + */ + public static SegwitAddress fromKey(NetworkParameters params, ECKey key) { + checkArgument(key.isCompressed(), "only compressed keys allowed"); + return fromHash(params, key.getPubKeyHash()); + } + + /** + * Returns the textual form of the address. + * + * @return textual form encoded in bech32 + */ + public String toBech32() { + if (getWitnessVersion() == 0) + return Bech32.encode(Bech32.Encoding.BECH32, params.getSegwitAddressHrp(), bytes); + else + return Bech32.encode(Bech32.Encoding.BECH32M, params.getSegwitAddressHrp(), bytes); + } + + /** + * Helper for re-arranging bits into groups. + */ + private static byte[] convertBits(final byte[] in, final int inStart, final int inLen, final int fromBits, + final int toBits, final boolean pad) throws AddressFormatException { + int acc = 0; + int bits = 0; + ByteArrayOutputStream out = new ByteArrayOutputStream(64); + final int maxv = (1 << toBits) - 1; + final int max_acc = (1 << (fromBits + toBits - 1)) - 1; + for (int i = 0; i < inLen; i++) { + int value = in[i + inStart] & 0xff; + if ((value >>> fromBits) != 0) { + throw new AddressFormatException( + String.format("Input value '%X' exceeds '%d' bit size", value, fromBits)); + } + acc = ((acc << fromBits) | value) & max_acc; + bits += fromBits; + while (bits >= toBits) { + bits -= toBits; + out.write((acc >>> bits) & maxv); + } + } + if (pad) { + if (bits > 0) + out.write((acc << (toBits - bits)) & maxv); + } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) { + throw new AddressFormatException("Could not convert bits, invalid padding"); + } + return out.toByteArray(); + } + +// /** +// * {@inheritDoc} +// * +// * @param o other {@code Address} object +// * @return comparison result +// */ +// @Override +// public int compareTo(Address o) { +// int result = compareAddressPartial(o); +// if (result != 0) return result; +// +// // Compare the bytes +// return UnsignedBytes.lexicographicalComparator().compare(this.bytes, o.bytes); +// } +} \ No newline at end of file diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt index 568de18b5a..61c1c79b48 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/BitcoinPaymentIntentParser.kt @@ -21,19 +21,21 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.bitcoinj.core.Address import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.NetworkParameters import org.bitcoinj.uri.BitcoinURI import org.bitcoinj.uri.BitcoinURIParseException import org.dash.wallet.common.R import org.dash.wallet.common.data.PaymentIntent -import org.dash.wallet.common.payments.parsers.AddressParser +import org.dash.wallet.common.payments.parsers.BitcoinAddressParser import org.dash.wallet.common.payments.parsers.BitcoinMainNetParams import org.dash.wallet.common.payments.parsers.PaymentIntentParserException +import org.dash.wallet.common.payments.parsers.SegwitAddress import org.dash.wallet.common.util.ResourceString import org.slf4j.LoggerFactory class BitcoinPaymentIntentParser : MayaPaymentIntentParser("BTC", "BTC.BTC", BitcoinMainNetParams()) { private val log = LoggerFactory.getLogger(BitcoinPaymentIntentParser::class.java) - private val addressParser = AddressParser.getBitcoinAddressParser() + private val addressParser = BitcoinAddressParser(params as NetworkParameters) override suspend fun parse(input: String): PaymentIntent = withContext(Dispatchers.Default) { if (input.startsWith("$currency:") || input.startsWith("${currency.uppercase()}:")) { @@ -61,14 +63,19 @@ class BitcoinPaymentIntentParser : MayaPaymentIntentParser("BTC", "BTC.BTC", Bit val address = Address.fromString(params, input) return@withContext createPaymentIntent(address.toString()) } catch (ex: AddressFormatException) { - log.info("got invalid address", ex) - throw PaymentIntentParserException( - ex, - ResourceString( - R.string.error, - listOf() + try { + val address = SegwitAddress.fromBech32(params, input) + return@withContext createPaymentIntent(address.toString()) + } catch (ex: AddressFormatException) { + log.info("got invalid address", ex) + throw PaymentIntentParserException( + ex, + ResourceString( + R.string.error, + listOf() + ) ) - ) + } } } diff --git a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt index caa45aafd5..d4ffedf7bd 100644 --- a/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt +++ b/integrations/maya/src/main/java/org/dash/wallet/integrations/maya/payments/parsers/MayaPaymentParsers.kt @@ -18,11 +18,13 @@ package org.dash.wallet.integrations.maya.payments.parsers import org.dash.wallet.common.payments.parsers.AddressParser +import org.dash.wallet.common.payments.parsers.BitcoinAddressParser +import org.dash.wallet.common.payments.parsers.BitcoinMainNetParams import org.dash.wallet.common.payments.parsers.PaymentParsers class MayaPaymentParsers : PaymentParsers() { init { - add("bitcoin", "btc", BitcoinPaymentIntentParser(), AddressParser.getBitcoinAddressParser()) + add("bitcoin", "btc", BitcoinPaymentIntentParser(), BitcoinAddressParser(BitcoinMainNetParams())) add("ethereum", "eth", EthereumPaymentIntentParser("ETH.ETH"), AddressParser.getEthereumAddressParser()) add("usdc", "usdc", EthereumPaymentIntentParser("ETH.USDC"), AddressParser.getEthereumAddressParser()) add("tether", "usdt", EthereumPaymentIntentParser("ETH.USDC"), AddressParser.getEthereumAddressParser()) From 46e0f0e53e8a7e1c92e48dd8e62816113f1a7052 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Sat, 20 Jan 2024 14:52:50 -0800 Subject: [PATCH 16/60] feat: add support for inputting addresses from other modules --- .../wallet/common/integrations/Integration.kt | 38 +++++++++++ .../ui/address_input/AddressInputFragment.kt | 22 +++++++ .../ui/address_input/AddressInputViewModel.kt | 7 +- .../common/ui/address_input/AddressSource.kt | 31 +++++++++ .../res/layout/fragment_address_input.xml | 35 ++++++++++ common/src/main/res/values/strings.xml | 1 + .../maya/ui/MayaAddressInputFragment.kt | 32 +++++++++ .../maya/ui/MayaAddressInputViewModel.kt | 62 ++++++++++++++++++ .../ui/MayaCryptoCurrencyPickerFragment.kt | 3 - .../wallet/di/DataProviderModule.kt | 8 +++ .../ExchangeIntegrationListProvider.kt | 65 +++++++++++++++++++ 11 files changed, 295 insertions(+), 9 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/integrations/Integration.kt create mode 100644 common/src/main/java/org/dash/wallet/common/ui/address_input/AddressSource.kt create mode 100644 integrations/maya/src/main/java/org/dash/wallet/integrations/maya/ui/MayaAddressInputViewModel.kt create mode 100644 wallet/src/de/schildbach/wallet/service/ExchangeIntegrationListProvider.kt diff --git a/common/src/main/java/org/dash/wallet/common/integrations/Integration.kt b/common/src/main/java/org/dash/wallet/common/integrations/Integration.kt new file mode 100644 index 0000000000..325793e0db --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/integrations/Integration.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.integrations + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import kotlinx.coroutines.flow.Flow + +data class ExchangeIntegration( + val id: String, + val isConnected: Boolean, + val address: String?, + val currency: String?, + @StringRes + val name: Int, + @DrawableRes + val iconId: Int +) + +interface ExchangeIntegrationProvider { + fun observeDepositAddresses(currency: String) : Flow> + fun connectToIntegration(name: String) +} diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt index e2dfa76326..ead00bc6a9 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputFragment.kt @@ -42,6 +42,8 @@ import kotlinx.coroutines.launch import org.dash.wallet.common.R import org.dash.wallet.common.databinding.FragmentAddressInputBinding import org.dash.wallet.common.services.analytics.AnalyticsConstants +import org.dash.wallet.common.ui.radio_group.IconifiedViewItem +import org.dash.wallet.common.ui.recyclerview.IconifiedListAdapter import org.dash.wallet.common.ui.scan.ScanActivity import org.dash.wallet.common.ui.viewBinding import org.dash.wallet.common.util.KeyboardUtil @@ -57,6 +59,7 @@ import org.dash.wallet.common.util.observe */ @AndroidEntryPoint abstract class AddressInputFragment : Fragment(R.layout.fragment_address_input) { + var adapter: IconifiedListAdapter? = null protected val binding by viewBinding(FragmentAddressInputBinding::bind) protected val viewModel by viewModels() @@ -90,6 +93,8 @@ abstract class AddressInputFragment : Fragment(R.layout.fragment_address_input) binding.inputWrapper.hint = it } + binding.addressSourceContainer.isVisible = viewModel.addressSources.isNotEmpty() + binding.addressInput.doOnTextChanged { text, _, _, _ -> viewModel.setInput(text.toString()) binding.continueBtn.isEnabled = !text.isNullOrEmpty() @@ -189,4 +194,21 @@ abstract class AddressInputFragment : Fragment(R.layout.fragment_address_input) } } } + + fun setAddressSources(source: List) { + viewModel.addressSources.addAll(source) + binding.addressSourceContainer.isVisible = source.isNotEmpty() + adapter?.run { + submitList( + source.map { + IconifiedViewItem( + getString(R.string.address_input_paste_from, getString(it.name)), + it.address ?: "", + it.icon + ) + } + ) + notifyItemRangeInserted(0, source.size) + } + } } diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt index 98177dad67..03ce4da3d1 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressInputViewModel.kt @@ -53,13 +53,8 @@ class AddressInputViewModel @Inject constructor( walletDataProvider: WalletDataProvider ): ViewModel() { lateinit var paymentParsers: PaymentParsers - //private lateinit var addressParser: AddressParser// = AddressParser.getDashAddressParser(walletDataProvider.networkParameters) - //private lateinit var paymentIntentParser: PaymentIntentParser var currency: String = Constants.DASH_CURRENCY - set(value) { - field = value - //addressParser = paymentParsers.getAddressParser(field)!! - } + val addressSources = arrayListOf() private val _uiState = MutableStateFlow(AddressInputUIState()) val uiState: StateFlow = _uiState.asStateFlow() diff --git a/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressSource.kt b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressSource.kt new file mode 100644 index 0000000000..2995aed08a --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/ui/address_input/AddressSource.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.ui.address_input + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes + +data class AddressSource( + val id: String, + @StringRes + val name: Int, + @DrawableRes + val icon: Int, + val address: String?, + val currency: String? +) diff --git a/common/src/main/res/layout/fragment_address_input.xml b/common/src/main/res/layout/fragment_address_input.xml index b1a971eed2..b899efe019 100644 --- a/common/src/main/res/layout/fragment_address_input.xml +++ b/common/src/main/res/layout/fragment_address_input.xml @@ -80,6 +80,41 @@ android:visibility="gone" tools:visibility="visible" /> + + + + + +