mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7cc2f6fafb | ||
![]() |
9a3ee0eda8 | ||
![]() |
6e898939a3 | ||
![]() |
40ae39d647 | ||
![]() |
ca81e652e5 | ||
![]() |
796048be4e | ||
![]() |
441bf995c8 | ||
![]() |
e8860ab8eb | ||
![]() |
525b38ff53 | ||
![]() |
ba79bf87aa | ||
![]() |
3fe6571e7d | ||
![]() |
364e6a8137 | ||
![]() |
cb69ce99d6 | ||
![]() |
1f976872fc | ||
![]() |
27f266b6f7 | ||
![]() |
168928d54a | ||
![]() |
b3f61072aa | ||
![]() |
ccb64aded0 | ||
![]() |
e98fa089f2 | ||
![]() |
884878b7a7 | ||
![]() |
4e23f0ef3a | ||
![]() |
6ea4e3d998 | ||
![]() |
971c90f35b | ||
![]() |
f0523c403c | ||
![]() |
966ed23b87 | ||
![]() |
95f2ca74a6 | ||
![]() |
81d94478f2 | ||
![]() |
16ff779ebc | ||
![]() |
6b7bb164f4 | ||
![]() |
da1d4ea1bf | ||
![]() |
d5a967f690 | ||
![]() |
de8de02f9f |
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 171
|
||||
versionName "1.11.1 'Chernushka'"
|
||||
versionCode 176
|
||||
versionName "1.11.6 'Chernushka'"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
@@ -115,6 +115,10 @@ dependencies {
|
||||
implementation 'org.slf4j:slf4j-nop:1.7.25'
|
||||
implementation 'com.github.brnunes:swipeablerecyclerview:1.0.2'
|
||||
|
||||
// https://mvnrepository.com/artifact/com.github.aelstad/keccakj
|
||||
implementation 'com.github.aelstad:keccakj:1.1.0'
|
||||
|
||||
|
||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -33,6 +34,7 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -45,6 +47,7 @@ import com.m2049r.xmrwallet.util.FingerprintHelper;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||
import com.m2049r.xmrwallet.util.RestoreHeight;
|
||||
import com.m2049r.xmrwallet.util.ledger.Monero;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
import com.nulabinc.zxcvbn.Strength;
|
||||
import com.nulabinc.zxcvbn.Zxcvbn;
|
||||
@@ -240,9 +243,7 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
etWalletAddress.setVisibility(View.VISIBLE);
|
||||
etWalletAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener()
|
||||
|
||||
{
|
||||
etWalletAddress.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
||||
@@ -274,9 +275,7 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
if (type.equals(TYPE_KEY)) {
|
||||
etWalletSpendKey.setVisibility(View.VISIBLE);
|
||||
etWalletSpendKey.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener()
|
||||
|
||||
{
|
||||
etWalletSpendKey.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
||||
@@ -303,9 +302,7 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
}
|
||||
bGenerate.setOnClickListener(new View.OnClickListener()
|
||||
|
||||
{
|
||||
bGenerate.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Helper.hideKeyboard(getActivity());
|
||||
@@ -616,4 +613,80 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
AlertDialog ledgerDialog = null;
|
||||
|
||||
public void convertLedgerSeed() {
|
||||
if (ledgerDialog != null) return;
|
||||
final Activity activity = getActivity();
|
||||
View promptsView = getLayoutInflater().inflate(R.layout.prompt_ledger_seed, null);
|
||||
android.app.AlertDialog.Builder alertDialogBuilder = new android.app.AlertDialog.Builder(activity);
|
||||
alertDialogBuilder.setView(promptsView);
|
||||
|
||||
final TextInputLayout etSeed = promptsView.findViewById(R.id.etSeed);
|
||||
final TextInputLayout etPassphrase = promptsView.findViewById(R.id.etPassphrase);
|
||||
|
||||
etSeed.getEditText().addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (etSeed.getError() != null) {
|
||||
etSeed.setError(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start,
|
||||
int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start,
|
||||
int before, int count) {
|
||||
}
|
||||
});
|
||||
|
||||
alertDialogBuilder
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(getString(R.string.label_ok), null)
|
||||
.setNegativeButton(getString(R.string.label_cancel),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Helper.hideKeyboardAlways(activity);
|
||||
etWalletMnemonic.getEditText().getText().clear();
|
||||
dialog.cancel();
|
||||
ledgerDialog = null;
|
||||
}
|
||||
});
|
||||
|
||||
ledgerDialog = alertDialogBuilder.create();
|
||||
|
||||
ledgerDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
String ledgerSeed = etSeed.getEditText().getText().toString();
|
||||
String ledgerPassphrase = etPassphrase.getEditText().getText().toString();
|
||||
String moneroSeed = Monero.convert(ledgerSeed, ledgerPassphrase);
|
||||
if (moneroSeed != null) {
|
||||
etWalletMnemonic.getEditText().setText(moneroSeed);
|
||||
ledgerDialog.dismiss();
|
||||
ledgerDialog = null;
|
||||
} else {
|
||||
etSeed.setError(getString(R.string.bad_ledger_seed));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
ledgerDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
ledgerDialog.show();
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
@@ -620,6 +621,11 @@ public class GenerateReviewFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
openDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
return openDialog;
|
||||
}
|
||||
|
||||
|
@@ -1229,6 +1229,12 @@ public class LoginActivity extends BaseActivity
|
||||
case R.id.action_language:
|
||||
onChangeLocale();
|
||||
return true;
|
||||
case R.id.action_ledger_seed:
|
||||
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
if (f instanceof GenerateFragment) {
|
||||
((GenerateFragment) f).convertLedgerSeed();
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@@ -1361,30 +1367,31 @@ public class LoginActivity extends BaseActivity
|
||||
};
|
||||
|
||||
private void connectLedger(UsbManager usbManager, final UsbDevice usbDevice) {
|
||||
try {
|
||||
Ledger.connect(usbManager, usbDevice);
|
||||
registerDetachReceiver();
|
||||
onLedgerAction();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.open_wallet_ledger_missing),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (Ledger.ENABLED)
|
||||
try {
|
||||
Ledger.connect(usbManager, usbDevice);
|
||||
registerDetachReceiver();
|
||||
onLedgerAction();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.toast_ledger_attached, usbDevice.getProductName()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(LoginActivity.this,
|
||||
getString(R.string.open_wallet_ledger_missing),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -32,6 +32,7 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
@@ -505,6 +506,10 @@ public class NodeFragment extends Fragment
|
||||
});
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
editDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
etNodePass.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
|
@@ -302,7 +302,7 @@ public class ReceiveFragment extends Fragment {
|
||||
File imagePath = new File(getActivity().getCacheDir(), "images");
|
||||
File png = new File(imagePath, "QR.png");
|
||||
Uri contentUri = FileProvider.getUriForFile(getActivity(),
|
||||
"com.m2049r.xmrwallet.fileprovider", png);
|
||||
BuildConfig.APPLICATION_ID + ".fileprovider", png);
|
||||
if (contentUri != null) {
|
||||
Intent shareIntent = new Intent();
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
@@ -574,6 +574,7 @@ public class ReceiveFragment extends Fragment {
|
||||
@Override
|
||||
public void onPause() {
|
||||
Timber.d("onPause()");
|
||||
Helper.hideKeyboard(getActivity());
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
|
@@ -71,7 +71,6 @@ public class TxFragment extends Fragment {
|
||||
private TextView tvTxFee;
|
||||
private TextView tvTxTransfers;
|
||||
private TextView etTxNotes;
|
||||
private Button bTxNotes;
|
||||
|
||||
// XMRTO stuff
|
||||
private View cvXmrTo;
|
||||
@@ -102,21 +101,9 @@ public class TxFragment extends Fragment {
|
||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||
tvTxTransfers = view.findViewById(R.id.tvTxTransfers);
|
||||
etTxNotes = view.findViewById(R.id.etTxNotes);
|
||||
bTxNotes = view.findViewById(R.id.bTxNotes);
|
||||
|
||||
etTxNotes.setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
bTxNotes.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
info.notes = null; // force reload on next view
|
||||
bTxNotes.setEnabled(false);
|
||||
etTxNotes.setEnabled(false);
|
||||
userNotes.setNote(etTxNotes.getText().toString());
|
||||
activityCallback.onSetNote(info.hash, userNotes.txNotes);
|
||||
}
|
||||
});
|
||||
|
||||
tvTxXmrToKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -131,14 +118,6 @@ public class TxFragment extends Fragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
public void onNotesSet(boolean reload) {
|
||||
bTxNotes.setEnabled(true);
|
||||
etTxNotes.setEnabled(true);
|
||||
if (reload) {
|
||||
loadNotes(this.info);
|
||||
}
|
||||
}
|
||||
|
||||
void shareTxInfo() {
|
||||
if (this.info == null) return;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
@@ -315,7 +294,6 @@ public class TxFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -337,9 +315,9 @@ public class TxFragment extends Fragment {
|
||||
|
||||
String getTxNotes(String hash);
|
||||
|
||||
String getTxAddress(int major, int minor);
|
||||
boolean setTxNotes(String txId, String txNotes);
|
||||
|
||||
void onSetNote(String txId, String notes);
|
||||
String getTxAddress(int major, int minor);
|
||||
|
||||
void setToolbarButton(int type);
|
||||
|
||||
@@ -357,4 +335,16 @@ public class TxFragment extends Fragment {
|
||||
+ " must implement Listener");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (!etTxNotes.getText().toString().equals(userNotes.note)) { // notes have changed
|
||||
// save them
|
||||
userNotes.setNote(etTxNotes.getText().toString());
|
||||
info.notes = userNotes.txNotes;
|
||||
activityCallback.setTxNotes(info.hash, info.notes);
|
||||
}
|
||||
Helper.hideKeyboard(getActivity());
|
||||
super.onPause();
|
||||
}
|
||||
}
|
@@ -150,8 +150,13 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
final WalletFragment walletFragment = (WalletFragment)
|
||||
getSupportFragmentManager().findFragmentByTag(WalletFragment.class.getName());
|
||||
if (walletFragment != null) walletFragment.resetDismissedTransactions();
|
||||
updateAccountsBalance();
|
||||
forceUpdate();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateAccountsBalance();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,6 +179,11 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
return getWallet().getUserNote(txId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setTxNotes(String txId, String txNotes) {
|
||||
return getWallet().setUserNote(txId, txNotes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTxAddress(int major, int minor) {
|
||||
return getWallet().getSubaddress(major, minor);
|
||||
@@ -713,26 +723,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetNotes(final boolean success) {
|
||||
try {
|
||||
final TxFragment txFragment = (TxFragment)
|
||||
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (!success) {
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.tx_notes_set_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
txFragment.onNotesSet(success);
|
||||
}
|
||||
});
|
||||
} catch (ClassCastException ex) {
|
||||
// not in tx fragment
|
||||
Timber.d(ex.getLocalizedMessage());
|
||||
// never mind
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(final String text) {
|
||||
try {
|
||||
@@ -794,21 +784,6 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetNote(String txId, String notes) {
|
||||
if (mIsBound) { // no point in talking to unbound service
|
||||
Intent intent = new Intent(getApplicationContext(), WalletService.class);
|
||||
intent.putExtra(WalletService.REQUEST, WalletService.REQUEST_CMD_SETNOTE);
|
||||
intent.putExtra(WalletService.REQUEST_CMD_SETNOTE_TX, txId);
|
||||
intent.putExtra(WalletService.REQUEST_CMD_SETNOTE_NOTES, notes);
|
||||
startService(intent);
|
||||
Timber.d("SET NOTE request sent");
|
||||
} else {
|
||||
Timber.e("Service not bound");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareSend(final String tag, final TxData txData) {
|
||||
if (mIsBound) { // no point in talking to unbound service
|
||||
@@ -1062,6 +1037,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
} else {
|
||||
tvBalance.setText(null);
|
||||
}
|
||||
updateAccountsList();
|
||||
}
|
||||
|
||||
void updateAccountsHeader() {
|
||||
@@ -1075,8 +1051,11 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
|
||||
Menu menu = accountsView.getMenu();
|
||||
menu.removeGroup(R.id.accounts_list);
|
||||
final int n = wallet.getNumAccounts();
|
||||
final boolean showBalances = (n > 1) && !isStreetMode();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final String label = wallet.getAccountLabel(i);
|
||||
final String label = (showBalances ?
|
||||
getString(R.string.label_account, wallet.getAccountLabel(i), Helper.getDisplayAmount(wallet.getBalance(i), 2))
|
||||
: wallet.getAccountLabel(i));
|
||||
final MenuItem item = menu.add(R.id.accounts_list, getAccountId(i), 2 * i, label);
|
||||
item.setIcon(R.drawable.ic_account_balance_wallet_black_24dp);
|
||||
if (i == wallet.getAccountIndex())
|
||||
|
@@ -219,7 +219,7 @@ public class WalletFragment extends Fragment
|
||||
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
|
||||
// at this point selection is XMR in case of error
|
||||
String displayB;
|
||||
double amountA = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // crash if this fails!
|
||||
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
|
||||
double amountB = amountA * balanceRate;
|
||||
displayB = Helper.getFormattedAmount(amountB, false);
|
||||
@@ -235,10 +235,10 @@ public class WalletFragment extends Fragment
|
||||
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
||||
|
||||
void refreshBalance() {
|
||||
double unconfirmedXmr = Double.parseDouble(Helper.getDisplayAmount(balance - unlockedBalance));
|
||||
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();
|
||||
showUnconfirmed(unconfirmedXmr);
|
||||
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
||||
double amountXmr = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
|
||||
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
||||
} else { // not XMR
|
||||
String currency = (String) sCurrency.getSelectedItem();
|
||||
@@ -294,7 +294,7 @@ public class WalletFragment extends Fragment
|
||||
|
||||
public void exchangeFailed() {
|
||||
sCurrency.setSelection(0, true); // default to XMR
|
||||
double amountXmr = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
|
||||
double amountXmr = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||
showBalance(Helper.getFormattedAmount(amountXmr, true));
|
||||
hideExchanging();
|
||||
}
|
||||
|
@@ -46,7 +46,8 @@ import okhttp3.ResponseBody;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NodeInfo extends Node {
|
||||
final static public int MIN_MAJOR_VERSION = 9;
|
||||
final static public int MIN_MAJOR_VERSION = 11;
|
||||
final static public String RPC_VERSION = "2.0";
|
||||
|
||||
private long height = 0;
|
||||
private long timestamp = 0;
|
||||
@@ -228,9 +229,12 @@ public class NodeInfo extends Node {
|
||||
responseCode = response.code();
|
||||
if (response.isSuccessful()) {
|
||||
ResponseBody respBody = response.body(); // closed through Response object
|
||||
if ((respBody != null) && (respBody.contentLength() < 1000)) { // sanity check
|
||||
if ((respBody != null) && (respBody.contentLength() < 2000)) { // sanity check
|
||||
final JSONObject json = new JSONObject(
|
||||
respBody.string());
|
||||
String rpcVersion = json.getString("jsonrpc");
|
||||
if (!RPC_VERSION.equals(rpcVersion))
|
||||
return false;
|
||||
final JSONObject header = json.getJSONObject(
|
||||
"result").getJSONObject("block_header");
|
||||
height = header.getLong("height");
|
||||
|
@@ -62,9 +62,6 @@ public class ProgressDialog extends AlertDialog {
|
||||
pbBar = view.findViewById(R.id.pbBar);
|
||||
tvProgress = view.findViewById(R.id.tvProgress);
|
||||
setView(view);
|
||||
//setTitle("blabla");
|
||||
//super.setMessage("bubbu");
|
||||
// view.invalidate();
|
||||
setIndeterminate(indeterminate);
|
||||
if (maxValue > 0) {
|
||||
setMax(maxValue);
|
||||
|
@@ -27,11 +27,13 @@ import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.TxData;
|
||||
import com.m2049r.xmrwallet.data.TxDataBtc;
|
||||
@@ -435,6 +437,11 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
passwordDialog.show();
|
||||
}
|
||||
|
||||
|
@@ -27,10 +27,12 @@ import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.data.TxData;
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
@@ -322,6 +324,11 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// set FLAG_SECURE to prevent screenshots in Release Mode
|
||||
if (!(BuildConfig.DEBUG && BuildConfig.FLAVOR_type.equals("alpha"))) {
|
||||
passwordDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
passwordDialog.show();
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,7 @@ import com.m2049r.xmrwallet.model.TransactionInfo;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.data.UserNotes;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
@@ -154,7 +155,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
}
|
||||
|
||||
if ((infoItem.fee > 0)) {
|
||||
String fee = Helper.getDisplayAmount(infoItem.fee, 5);
|
||||
String fee = Helper.getDisplayAmount(infoItem.fee, Helper.DISPLAY_DIGITS_INFO);
|
||||
tvFee.setText(context.getString(R.string.tx_list_fee, fee));
|
||||
tvFee.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@@ -34,6 +34,7 @@ import java.io.IOException;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Ledger {
|
||||
static final public boolean ENABLED = true;
|
||||
// 5:20 is same as wallet2.cpp::restore()
|
||||
static public final int LOOKAHEAD_ACCOUNTS = 5;
|
||||
static public final int LOOKAHEAD_SUBADDRESSES = 20;
|
||||
@@ -44,6 +45,7 @@ public class Ledger {
|
||||
public static final int OK[] = {SW_OK};
|
||||
|
||||
public static UsbDevice findDevice(UsbManager usbManager) {
|
||||
if (!ENABLED) return null;
|
||||
return BTChipTransportAndroidHID.getDevice(usbManager);
|
||||
}
|
||||
|
||||
|
@@ -173,5 +173,4 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
||||
return this.hash.compareTo(another.hash);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -353,9 +353,10 @@ public class Wallet {
|
||||
if (label.equals(NEW_ACCOUNT_NAME)) {
|
||||
String address = getAddress(accountIndex);
|
||||
int len = address.length();
|
||||
return address.substring(0, 6) +
|
||||
label = address.substring(0, 6) +
|
||||
"\u2026" + address.substring(len - 6, len);
|
||||
} else return label;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getSubaddressLabel(int addressIndex) {
|
||||
|
@@ -69,10 +69,6 @@ public class WalletService extends Service {
|
||||
public static final String REQUEST_CMD_SEND = "send";
|
||||
public static final String REQUEST_CMD_SEND_NOTES = "notes";
|
||||
|
||||
public static final String REQUEST_CMD_SETNOTE = "setnote";
|
||||
public static final String REQUEST_CMD_SETNOTE_TX = "tx";
|
||||
public static final String REQUEST_CMD_SETNOTE_NOTES = "notes";
|
||||
|
||||
public static final int START_SERVICE = 1;
|
||||
public static final int STOP_SERVICE = 2;
|
||||
|
||||
@@ -224,8 +220,6 @@ public class WalletService extends Service {
|
||||
|
||||
void onSendTransactionFailed(String error);
|
||||
|
||||
void onSetNotes(boolean success);
|
||||
|
||||
void onWalletStarted(Wallet.ConnectionStatus walletStatus);
|
||||
|
||||
void onWalletOpen(Wallet.Device device);
|
||||
@@ -378,26 +372,6 @@ public class WalletService extends Service {
|
||||
if (observer != null) observer.onSendTransactionFailed(error);
|
||||
return;
|
||||
}
|
||||
} else if (cmd.equals(REQUEST_CMD_SETNOTE)) {
|
||||
Wallet myWallet = getWallet();
|
||||
Timber.d("SET NOTE for wallet: %s", myWallet.getName());
|
||||
String txId = extras.getString(REQUEST_CMD_SETNOTE_TX);
|
||||
String notes = extras.getString(REQUEST_CMD_SETNOTE_NOTES);
|
||||
if ((txId != null) && (notes != null)) {
|
||||
boolean success = myWallet.setUserNote(txId, notes);
|
||||
if (!success) {
|
||||
Timber.e(myWallet.getErrorString());
|
||||
}
|
||||
if (observer != null) observer.onSetNotes(success);
|
||||
if (success) {
|
||||
boolean rc = myWallet.store();
|
||||
Timber.d("wallet stored: %s with rc=%b", myWallet.getName(), rc);
|
||||
if (!rc) {
|
||||
Timber.w("Wallet store failed: %s", myWallet.getErrorString());
|
||||
}
|
||||
if (observer != null) observer.onWalletStored(rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@@ -64,6 +64,7 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketTimeoutException;
|
||||
@@ -186,28 +187,23 @@ public class Helper {
|
||||
act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
}
|
||||
|
||||
static public BigDecimal getDecimalAmount(long amount) {
|
||||
return new BigDecimal(amount).scaleByPowerOfTen(-12);
|
||||
}
|
||||
|
||||
static public String getDisplayAmount(long amount) {
|
||||
return getDisplayAmount(amount, 12);
|
||||
}
|
||||
|
||||
static public String getDisplayAmount(long amount, int maxDecimals) {
|
||||
return getDisplayAmount(Wallet.getDisplayAmount(amount), maxDecimals);
|
||||
}
|
||||
|
||||
// amountString must have '.' as decimal point
|
||||
private static String getDisplayAmount(String amountString, int maxDecimals) {
|
||||
int lastZero = 0;
|
||||
int decimal = 0;
|
||||
for (int i = amountString.length() - 1; i >= 0; i--) {
|
||||
if ((lastZero == 0) && (amountString.charAt(i) != '0')) lastZero = i + 1;
|
||||
// TODO i18n
|
||||
if (amountString.charAt(i) == '.') {
|
||||
decimal = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int cutoff = Math.min(Math.max(lastZero, decimal + 2), decimal + maxDecimals);
|
||||
return amountString.substring(0, cutoff);
|
||||
// a Java bug does not strip zeros properly if the value is 0
|
||||
if (amount == 0) return "0.00";
|
||||
BigDecimal d = getDecimalAmount(amount)
|
||||
.setScale(maxDecimals, BigDecimal.ROUND_HALF_UP)
|
||||
.stripTrailingZeros();
|
||||
if (d.scale() < 2)
|
||||
d = d.setScale(2, BigDecimal.ROUND_UNNECESSARY);
|
||||
return d.toPlainString();
|
||||
}
|
||||
|
||||
static public String getFormattedAmount(double amount, boolean isXmr) {
|
||||
@@ -556,6 +552,8 @@ public class Helper {
|
||||
tvOpenPrompt.setText(context.getText(R.string.prompt_fingerprint_auth));
|
||||
tvOpenPrompt.setVisibility(View.VISIBLE);
|
||||
FingerprintHelper.authenticate(context, cancelSignal, fingerprintAuthCallback);
|
||||
} else {
|
||||
etPassword.requestFocus();
|
||||
}
|
||||
Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
|
@@ -101,6 +101,8 @@ public class RestoreHeight {
|
||||
blockheight.put("2019-01-01", 1738923L);
|
||||
blockheight.put("2019-02-01", 1761435L);
|
||||
blockheight.put("2019-03-01", 1781681L);
|
||||
blockheight.put("2019-04-01", 1803081L);
|
||||
blockheight.put("2019-05-01", 1824671L);
|
||||
}
|
||||
|
||||
public long getHeight(String date) {
|
||||
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Based on
|
||||
* https://stackoverflow.com/a/19943894
|
||||
*
|
||||
* Curve parameters from
|
||||
* https://en.bitcoin.it/wiki/Secp256k1
|
||||
*
|
||||
* Copyright (c) 2019 m2049r
|
||||
* Copyright (c) 2013 ChiaraHsieh
|
||||
*
|
||||
* 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 com.m2049r.xmrwallet.util.ledger;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.spec.ECPoint;
|
||||
|
||||
public class ECsecp256k1 {
|
||||
static private final BigInteger TWO = new BigInteger("2");
|
||||
static private final BigInteger THREE = new BigInteger("3");
|
||||
static public final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
|
||||
static public final BigInteger a = new BigInteger("0000000000000000000000000000000000000000000000000000000000000000", 16);
|
||||
static public final BigInteger b = new BigInteger("0000000000000000000000000000000000000000000000000000000000000007", 16);
|
||||
static public final BigInteger n = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
|
||||
static public final ECPoint G = new ECPoint(
|
||||
new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16),
|
||||
new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16));
|
||||
|
||||
public static ECPoint scalmult(BigInteger kin, ECPoint P) {
|
||||
ECPoint R = ECPoint.POINT_INFINITY, S = P;
|
||||
BigInteger k = kin.mod(n); // not necessary b/c that's how curves work
|
||||
int length = k.bitLength();
|
||||
byte[] binarray = new byte[length];
|
||||
for (int i = 0; i <= length - 1; i++) {
|
||||
binarray[i] = k.mod(TWO).byteValue();
|
||||
k = k.divide(TWO);
|
||||
}
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
// i should start at length-1 not -2 because the MSB of binary may not be 1
|
||||
R = doublePoint(R);
|
||||
if (binarray[i] == 1)
|
||||
R = addPoint(R, S);
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
public static ECPoint addPoint(ECPoint r, ECPoint s) {
|
||||
if (r.equals(s))
|
||||
return doublePoint(r);
|
||||
else if (r.equals(ECPoint.POINT_INFINITY))
|
||||
return s;
|
||||
else if (s.equals(ECPoint.POINT_INFINITY))
|
||||
return r;
|
||||
BigInteger slope = (r.getAffineY().subtract(s.getAffineY()))
|
||||
.multiply(r.getAffineX().subtract(s.getAffineX()).modInverse(p));
|
||||
BigInteger Xout = (slope.modPow(TWO, p).subtract(r.getAffineX())).subtract(s.getAffineX()).mod(p);
|
||||
BigInteger Yout = s.getAffineY().negate().add(slope.multiply(s.getAffineX().subtract(Xout))).mod(p);
|
||||
return new ECPoint(Xout, Yout);
|
||||
}
|
||||
|
||||
public static ECPoint doublePoint(ECPoint r) {
|
||||
if (r.equals(ECPoint.POINT_INFINITY))
|
||||
return r;
|
||||
BigInteger slope = (r.getAffineX().pow(2)).multiply(THREE).add(a)
|
||||
.multiply((r.getAffineY().multiply(TWO)).modInverse(p));
|
||||
BigInteger Xout = slope.pow(2).subtract(r.getAffineX().multiply(TWO)).mod(p);
|
||||
BigInteger Yout = (r.getAffineY().negate()).add(slope.multiply(r.getAffineX().subtract(Xout))).mod(p);
|
||||
return new ECPoint(Xout, Yout);
|
||||
}
|
||||
}
|
1866
app/src/main/java/com/m2049r/xmrwallet/util/ledger/Monero.java
Normal file
1866
app/src/main/java/com/m2049r/xmrwallet/util/ledger/Monero.java
Normal file
File diff suppressed because it is too large
Load Diff
170
app/src/main/java/com/theromus/sha/Keccak.java
Normal file
170
app/src/main/java/com/theromus/sha/Keccak.java
Normal file
@@ -0,0 +1,170 @@
|
||||
package com.theromus.sha;
|
||||
|
||||
import static com.theromus.utils.HexUtils.leftRotate64;
|
||||
import static com.theromus.utils.HexUtils.convertToUint;
|
||||
import static com.theromus.utils.HexUtils.convertFromLittleEndianTo64;
|
||||
import static com.theromus.utils.HexUtils.convertFrom64ToLittleEndian;
|
||||
import static java.lang.Math.min;
|
||||
import static java.lang.System.arraycopy;
|
||||
import static java.util.Arrays.fill;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
|
||||
/**
|
||||
* Keccak implementation.
|
||||
*
|
||||
* @author romus
|
||||
*/
|
||||
public class Keccak {
|
||||
|
||||
private static BigInteger BIT_64 = new BigInteger("18446744073709551615");
|
||||
|
||||
/**
|
||||
* Do hash.
|
||||
*
|
||||
* @param message input data
|
||||
* @param parameter keccak param
|
||||
* @return byte-array result
|
||||
*/
|
||||
public byte[] getHash(final byte[] message, final Parameters parameter) {
|
||||
int[] uState = new int[200];
|
||||
int[] uMessage = convertToUint(message);
|
||||
|
||||
|
||||
int rateInBytes = parameter.getRate() / 8;
|
||||
int blockSize = 0;
|
||||
int inputOffset = 0;
|
||||
|
||||
// Absorbing phase
|
||||
while (inputOffset < uMessage.length) {
|
||||
blockSize = min(uMessage.length - inputOffset, rateInBytes);
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
uState[i] = uState[i] ^ uMessage[i + inputOffset];
|
||||
}
|
||||
|
||||
inputOffset = inputOffset + blockSize;
|
||||
if (blockSize == rateInBytes) {
|
||||
doKeccakf(uState);
|
||||
blockSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Padding phase
|
||||
uState[blockSize] = uState[blockSize] ^ parameter.getD();
|
||||
if ((parameter.getD() & 0x80) != 0 && blockSize == (rateInBytes - 1)) {
|
||||
doKeccakf(uState);
|
||||
}
|
||||
|
||||
uState[rateInBytes - 1] = uState[rateInBytes - 1] ^ 0x80;
|
||||
doKeccakf(uState);
|
||||
|
||||
// Squeezing phase
|
||||
ByteArrayOutputStream byteResults = new ByteArrayOutputStream();
|
||||
int tOutputLen = parameter.getOutputLen() / 8;
|
||||
while (tOutputLen > 0) {
|
||||
blockSize = min(tOutputLen, rateInBytes);
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
byteResults.write((byte) uState[i]);
|
||||
}
|
||||
|
||||
tOutputLen -= blockSize;
|
||||
if (tOutputLen > 0) {
|
||||
doKeccakf(uState);
|
||||
}
|
||||
}
|
||||
|
||||
return byteResults.toByteArray();
|
||||
}
|
||||
|
||||
private void doKeccakf(final int[] uState) {
|
||||
BigInteger[][] lState = new BigInteger[5][5];
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
int[] data = new int[8];
|
||||
arraycopy(uState, 8 * (i + 5 * j), data, 0, data.length);
|
||||
lState[i][j] = convertFromLittleEndianTo64(data);
|
||||
}
|
||||
}
|
||||
roundB(lState);
|
||||
|
||||
fill(uState, 0);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
int[] data = convertFrom64ToLittleEndian(lState[i][j]);
|
||||
arraycopy(data, 0, uState, 8 * (i + 5 * j), data.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Permutation on the given state.
|
||||
*
|
||||
* @param state state
|
||||
*/
|
||||
private void roundB(final BigInteger[][] state) {
|
||||
int LFSRstate = 1;
|
||||
for (int round = 0; round < 24; round++) {
|
||||
BigInteger[] C = new BigInteger[5];
|
||||
BigInteger[] D = new BigInteger[5];
|
||||
|
||||
// θ step
|
||||
for (int i = 0; i < 5; i++) {
|
||||
C[i] = state[i][0].xor(state[i][1]).xor(state[i][2]).xor(state[i][3]).xor(state[i][4]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
D[i] = C[(i + 4) % 5].xor(leftRotate64(C[(i + 1) % 5], 1));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
state[i][j] = state[i][j].xor(D[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//ρ and π steps
|
||||
int x = 1, y = 0;
|
||||
BigInteger current = state[x][y];
|
||||
for (int i = 0; i < 24; i++) {
|
||||
int tX = x;
|
||||
x = y;
|
||||
y = (2 * tX + 3 * y) % 5;
|
||||
|
||||
BigInteger shiftValue = current;
|
||||
current = state[x][y];
|
||||
|
||||
state[x][y] = leftRotate64(shiftValue, (i + 1) * (i + 2) / 2);
|
||||
}
|
||||
|
||||
//χ step
|
||||
for (int j = 0; j < 5; j++) {
|
||||
BigInteger[] t = new BigInteger[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
t[i] = state[i][j];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
// ~t[(i + 1) % 5]
|
||||
BigInteger invertVal = t[(i + 1) % 5].xor(BIT_64);
|
||||
// t[i] ^ ((~t[(i + 1) % 5]) & t[(i + 2) % 5])
|
||||
state[i][j] = t[i].xor(invertVal.and(t[(i + 2) % 5]));
|
||||
}
|
||||
}
|
||||
|
||||
//ι step
|
||||
for (int i = 0; i < 7; i++) {
|
||||
LFSRstate = ((LFSRstate << 1) ^ ((LFSRstate >> 7) * 0x71)) % 256;
|
||||
// pow(2, i) - 1
|
||||
int bitPosition = (1 << i) - 1;
|
||||
if ((LFSRstate & 2) != 0) {
|
||||
state[0][0] = state[0][0].xor(new BigInteger("1").shiftLeft(bitPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
app/src/main/java/com/theromus/sha/Parameters.java
Normal file
51
app/src/main/java/com/theromus/sha/Parameters.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.theromus.sha;
|
||||
|
||||
/**
|
||||
* The parameters defining the standard FIPS 202.
|
||||
*
|
||||
* @author romus
|
||||
*/
|
||||
public enum Parameters {
|
||||
KECCAK_224 (1152, 0x01, 224),
|
||||
KECCAK_256 (1088, 0x01, 256),
|
||||
KECCAK_384 (832, 0x01, 384),
|
||||
KECCAK_512 (576, 0x01, 512),
|
||||
|
||||
SHA3_224 (1152, 0x06, 224),
|
||||
SHA3_256 (1088, 0x06, 256),
|
||||
SHA3_384 (832, 0x06, 384),
|
||||
SHA3_512 (576, 0x06, 512),
|
||||
|
||||
SHAKE128 (1344, 0x1F, 256),
|
||||
SHAKE256 (1088, 0x1F, 512);
|
||||
|
||||
private final int rate;
|
||||
|
||||
/**
|
||||
* Delimited suffix.
|
||||
*/
|
||||
public final int d;
|
||||
|
||||
/**
|
||||
* Output length (bits).
|
||||
*/
|
||||
public final int outputLen;
|
||||
|
||||
Parameters(int rate, int d, int outputLen) {
|
||||
this.rate = rate;
|
||||
this.d = d;
|
||||
this.outputLen = outputLen;
|
||||
}
|
||||
|
||||
public int getRate() {
|
||||
return rate;
|
||||
}
|
||||
|
||||
public int getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
public int getOutputLen() {
|
||||
return outputLen;
|
||||
}
|
||||
}
|
97
app/src/main/java/com/theromus/utils/HexUtils.java
Normal file
97
app/src/main/java/com/theromus/utils/HexUtils.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package com.theromus.utils;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Hex-utils.
|
||||
*
|
||||
* @author romus
|
||||
*/
|
||||
public class HexUtils {
|
||||
|
||||
private static final byte[] ENCODE_BYTE_TABLE = {
|
||||
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert byte array to unsigned array.
|
||||
*
|
||||
* @param data byte array
|
||||
* @return unsigned array
|
||||
*/
|
||||
public static int[] convertToUint(final byte[] data) {
|
||||
int[] converted = new int[data.length];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
converted[i] = data[i] & 0xFF;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert LE to 64-bit value (unsigned long).
|
||||
*
|
||||
* @param data data
|
||||
* @return 64-bit value (unsigned long)
|
||||
*/
|
||||
public static BigInteger convertFromLittleEndianTo64(final int[] data) {
|
||||
BigInteger uLong = new BigInteger("0");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uLong = uLong.add(new BigInteger(Integer.toString(data[i])).shiftLeft(8 * i));
|
||||
}
|
||||
|
||||
return uLong;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert 64-bit (unsigned long) value to LE.
|
||||
*
|
||||
* @param uLong 64-bit value (unsigned long)
|
||||
* @return LE
|
||||
*/
|
||||
public static int[] convertFrom64ToLittleEndian(final BigInteger uLong) {
|
||||
int[] data = new int[8];
|
||||
BigInteger mod256 = new BigInteger("256");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
data[i] = uLong.shiftRight((8 * i)).mod(mod256).intValue();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise rotate left.
|
||||
*
|
||||
* @param value unsigned long value
|
||||
* @param rotate rotate left
|
||||
* @return result
|
||||
*/
|
||||
public static BigInteger leftRotate64(final BigInteger value, final int rotate) {
|
||||
BigInteger lp = value.shiftRight(64 - (rotate % 64));
|
||||
BigInteger rp = value.shiftLeft(rotate % 64);
|
||||
|
||||
return lp.add(rp).mod(new BigInteger("18446744073709551616"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes to string.
|
||||
*
|
||||
* @param data bytes array
|
||||
* @return string
|
||||
*/
|
||||
public static String convertBytesToString(final byte[] data) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
int uVal = data[i] & 0xFF;
|
||||
|
||||
buffer.write(ENCODE_BYTE_TABLE[(uVal >>> 4)]);
|
||||
buffer.write(ENCODE_BYTE_TABLE[uVal & 0xF]);
|
||||
}
|
||||
|
||||
return new String(buffer.toByteArray());
|
||||
}
|
||||
|
||||
}
|
@@ -37,8 +37,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_toStartOf="@+id/ibOption"
|
||||
android:layout_toEndOf="@id/ibBookmark">
|
||||
android:layout_toStartOf="@+id/ibOption">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pbNode"
|
||||
|
@@ -225,34 +225,14 @@
|
||||
android:padding="8sp"
|
||||
android:text="@string/tx_notes" />
|
||||
|
||||
<RelativeLayout
|
||||
<EditText
|
||||
android:id="@+id/etTxNotes"
|
||||
style="@style/MoneroEdit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etTxNotes"
|
||||
style="@style/MoneroEdit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toStartOf="@+id/bTxNotes"
|
||||
android:backgroundTint="@color/moneroGray"
|
||||
android:hint="@string/tx_notes_hint"
|
||||
android:inputType="textMultiLine" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bTxNotes"
|
||||
style="@style/MoneroButton.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerInParent="true"
|
||||
android:enabled="true"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/tx_button_notes" />
|
||||
</RelativeLayout>
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/moneroGray"
|
||||
android:hint="@string/tx_notes_hint"
|
||||
android:inputType="textMultiLine" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
66
app/src/main/res/layout/prompt_ledger_seed.xml
Normal file
66
app/src/main/res/layout/prompt_ledger_seed.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:labelFor="@+id/etSeed"
|
||||
android:text="@string/menu_ledger_seed"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
|
||||
<TextView
|
||||
style="@style/MoneroLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:labelFor="@+id/etSeed"
|
||||
android:text="@string/prompt_ledger_seed_warn" />
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/etSeed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<android.support.design.widget.TextInputEditText
|
||||
style="@style/MoneroEdit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_ledger_seed"
|
||||
android:imeOptions="normal"
|
||||
android:inputType="textMultiLine|textVisiblePassword" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/etPassphrase"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<android.support.design.widget.TextInputEditText
|
||||
style="@style/MoneroEdit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_ledger_phrase"
|
||||
android:imeOptions="normal"
|
||||
android:inputType="textVisiblePassword" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOpenPrompt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
@@ -7,6 +7,13 @@
|
||||
android:icon="@drawable/ic_help_white_24dp"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/menu_help"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_ledger_seed"
|
||||
android:icon="@drawable/ic_ledger_restore"
|
||||
android:orderInCategory="200"
|
||||
android:title="@string/menu_ledger_seed"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
@@ -17,7 +17,7 @@
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p>Diese Seite informiert dich über unsere Richtlinien bezüglich der Sammlung,
|
||||
Nutzung und Weitergabe persönlicher Informationen, die wir von Nutzern unserer
|
||||
App erhalten (monerujo: Monero Wallet).
|
||||
App erhalten (monerujo: Monero-Wallet).
|
||||
</p>
|
||||
<p>Mit der Nutzung der App stimmst du der Erfassung und Nutzung von Informationen in
|
||||
Übereinstimmung mit dieser Erklärung zu.
|
||||
@@ -29,21 +29,21 @@
|
||||
von Transaktionen lokal gesammelt und verarbeitet und verschlüsselt in das Monero-Netzwerk übertragen.
|
||||
</p>
|
||||
<p>Andere persönliche Daten werden von der App nicht gesammelt.</p>
|
||||
<p>Wenn du den Umrechner (optional) nutzt fragt Monerujo
|
||||
<p>Wenn du den Umrechner (optional) nutzt, fragt Monerujo
|
||||
den aktuellen Kurs über die öffentliche Schnittstelle von coinmarketcap.com ab.
|
||||
Siehe dir ihre Datenschutzerklärung unter https://coinmarketcap.com/privacy für
|
||||
Details darüber an, wie Daten in deinen Anfragen gesammelt werden.</p>
|
||||
<p>Wenn du die App zum Bezahlen an BTC-Adressen verwendest, verwendest du den Dienst XMR.TO.
|
||||
Weitere Informationen findest du in den Datenschutzerklärung unter https://xmr.to/.
|
||||
Monerujo schickt dem Anbieter die BTC Zieladresse und den Betrag.
|
||||
Weitere Informationen findest du in der Datenschutzerklärung unter https://xmr.to/.
|
||||
Monerujo schickt dem Anbieter die BTC-Zieladresse und den Betrag.
|
||||
Deine IP-Adresse kann dabei aufgezeichnet werden.</p>
|
||||
<h2>App-Berechtigungen</h2>
|
||||
<ul>
|
||||
<li>INTERNET : Verbinde zum Monero Netzwerk über eine Monero Daemon Node</li>
|
||||
<li>READ_EXTERNAL_STORAGE : Liest die auf dem Gerät gespeicherten Wallet Dateien aus</li>
|
||||
<li>WRITE_EXTERNAL_STORAGE : Schreibt Wallet Dateien auf dem Gerät</li>
|
||||
<li>INTERNET : Verbinde zum Monero-Netzwerk über einen Monero-Hintergrunddienst-Node</li>
|
||||
<li>READ_EXTERNAL_STORAGE : Lese die auf dem Gerät gespeicherten Wallet-Dateien aus</li>
|
||||
<li>WRITE_EXTERNAL_STORAGE : Schreibe Wallet-Dateien auf das Gerät</li>
|
||||
<li>WAKE_LOCK : Halte das Gerät während der Synchronisation wach</li>
|
||||
<li>CAMERA : Scanne QR-Codes um Monero zu erhalten</li>
|
||||
<li>CAMERA : Scanne QR-Codes, um Monero zu erhalten</li>
|
||||
</ul>
|
||||
<h2>Änderungen an dieser Datenschutzerklärung</h2>
|
||||
<p>Wir können diese Datenschutzerklärung von Zeit zu Zeit aktualisieren.
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user