You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Wonerujo/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java

508 lines
16 KiB

/*
* Copyright (c) 2017 m2049r
*
* 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.model;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.m2049r.xmrwallet.data.Subaddress;
import com.m2049r.xmrwallet.data.TxData;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import timber.log.Timber;
public class Wallet {
final static public long SWEEP_ALL = Long.MAX_VALUE;
static {
System.loadLibrary("monerujo");
}
static public class Status {
Status(int status, String errorString) {
this.status = StatusEnum.values()[status];
this.errorString = errorString;
}
final private StatusEnum status;
final private String errorString;
@Nullable
private ConnectionStatus connectionStatus; // optional
public StatusEnum getStatus() {
return status;
}
public String getErrorString() {
return errorString;
}
public void setConnectionStatus(@Nullable ConnectionStatus connectionStatus) {
this.connectionStatus = connectionStatus;
}
@Nullable
public ConnectionStatus getConnectionStatus() {
return connectionStatus;
}
public boolean isOk() {
return (getStatus() == StatusEnum.Status_Ok)
&& ((getConnectionStatus() == null) ||
(getConnectionStatus() == ConnectionStatus.ConnectionStatus_Connected));
}
@Override
@NonNull
public String toString() {
return "Wallet.Status: (" + status + "/" + errorString + ", " + connectionStatus;
}
}
private int accountIndex = 0;
public int getAccountIndex() {
return accountIndex;
}
public void setAccountIndex(int accountIndex) {
Timber.d("setAccountIndex(%d)", accountIndex);
this.accountIndex = accountIndex;
getHistory().setAccountFor(this);
}
public String getName() {
return new File(getPath()).getName();
}
private long handle = 0;
private long listenerHandle = 0;
Wallet(long handle) {
this.handle = handle;
}
Wallet(long handle, int accountIndex) {
this.handle = handle;
this.accountIndex = accountIndex;
}
@RequiredArgsConstructor
@Getter
public enum Device {
Device_Undefined(0, 0),
Device_Software(50, 200),
Device_Ledger(5, 20);
private final int accountLookahead;
private final int subaddressLookahead;
}
public enum StatusEnum {
Status_Ok,
Status_Error,
Status_Critical
}
public enum ConnectionStatus {
ConnectionStatus_Disconnected,
ConnectionStatus_Connected,
ConnectionStatus_WrongVersion
}
public native String getSeed();
public native String getSeedLanguage();
public native void setSeedLanguage(String language);
public Status getStatus() {
return statusWithErrorString();
}
public Status getFullStatus() {
Wallet.Status walletStatus = statusWithErrorString();
walletStatus.setConnectionStatus(getConnectionStatus());
return walletStatus;
}
private native Status statusWithErrorString();
public native boolean setPassword(String password);
public String getAddress() {
return getAddress(accountIndex);
}
public String getAddress(int accountIndex) {
return getAddressJ(accountIndex, 0);
}
public String getSubaddress(int addressIndex) {
return getAddressJ(accountIndex, addressIndex);
}
public String getSubaddress(int accountIndex, int addressIndex) {
return getAddressJ(accountIndex, addressIndex);
}
private native String getAddressJ(int accountIndex, int addressIndex);
public Subaddress getSubaddressObject(int accountIndex, int subAddressIndex) {
return new Subaddress(accountIndex, subAddressIndex,
getSubaddress(subAddressIndex), getSubaddressLabel(subAddressIndex));
}
public Subaddress getSubaddressObject(int subAddressIndex) {
Subaddress subaddress = getSubaddressObject(accountIndex, subAddressIndex);
long amount = 0;
for (TransactionInfo info : getHistory().getAll()) {
if ((info.addressIndex == subAddressIndex)
&& (info.direction == TransactionInfo.Direction.Direction_In)) {
amount += info.amount;
}
}
subaddress.setAmount(amount);
return subaddress;
}
public native String getPath();
public NetworkType getNetworkType() {
return NetworkType.fromInteger(nettype());
}
public native int nettype();
//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
public native String getIntegratedAddress(String payment_id);
public native String getSecretViewKey();
public native String getSecretSpendKey();
public boolean store() {
final boolean ok = store("");
Timber.d("stored");
return ok;
}
public native boolean store(String path);
public boolean close() {
disposePendingTransaction();
return WalletManager.getInstance().close(this);
}
public native String getFilename();
// virtual std::string keysFilename() const = 0;
public boolean init(long upper_transaction_size_limit) {
return initJ(WalletManager.getInstance().getDaemonAddress(), upper_transaction_size_limit,
WalletManager.getInstance().getDaemonUsername(),
WalletManager.getInstance().getDaemonPassword());
}
private native boolean initJ(String daemon_address, long upper_transaction_size_limit,
String daemon_username, String daemon_password);
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
// virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0;
public native void setRestoreHeight(long height);
public native long getRestoreHeight();
// virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
// virtual bool connectToDaemon() = 0;
public ConnectionStatus getConnectionStatus() {
int s = getConnectionStatusJ();
return Wallet.ConnectionStatus.values()[s];
}
private native int getConnectionStatusJ();
//TODO virtual void setTrustedDaemon(bool arg) = 0;
//TODO virtual bool trustedDaemon() const = 0;
public long getBalance() {
return getBalance(accountIndex);
}
public native long getBalance(int accountIndex);
public native long getBalanceAll();
public long getUnlockedBalance() {
return getUnlockedBalance(accountIndex);
}
public native long getUnlockedBalanceAll();
public native long getUnlockedBalance(int accountIndex);
public native boolean isWatchOnly();
public native long getBlockChainHeight();
public native long getApproximateBlockChainHeight();
public native long getDaemonBlockChainHeight();
public native long getDaemonBlockChainTargetHeight();
boolean synced = false;
public boolean isSynchronized() {
return synced;
}
public void setSynchronized() {
this.synced = true;
}
public static native String getDisplayAmount(long amount);
public static native long getAmountFromString(String amount);
public static native long getAmountFromDouble(double amount);
public static native String generatePaymentId();
public static native boolean isPaymentIdValid(String payment_id);
public static boolean isAddressValid(String address) {
return isAddressValid(address, WalletManager.getInstance().getNetworkType().getValue());
}
public static native boolean isAddressValid(String address, int networkType);
public static native String getPaymentIdFromAddress(String address, int networkType);
public static native long getMaximumAllowedAmount();
public native void startRefresh();
public native void pauseRefresh();
public native boolean refresh();
public native void refreshAsync();
public native void rescanBlockchainAsyncJ();
public void rescanBlockchainAsync() {
synced = false;
rescanBlockchainAsyncJ();
}
//TODO virtual void setAutoRefreshInterval(int millis) = 0;
//TODO virtual int autoRefreshInterval() const = 0;
private PendingTransaction pendingTransaction = null;
public PendingTransaction getPendingTransaction() {
return pendingTransaction;
}
public void disposePendingTransaction() {
if (pendingTransaction != null) {
disposeTransaction(pendingTransaction);
pendingTransaction = null;
}
}
public PendingTransaction createTransaction(TxData txData) {
return createTransaction(
txData.getDestinationAddress(),
txData.getAmount(),
txData.getMixin(),
txData.getPriority());
}
public PendingTransaction createTransaction(String dst_addr,
long amount, int mixin_count,
PendingTransaction.Priority priority) {
disposePendingTransaction();
int _priority = priority.getValue();
long txHandle =
(amount == SWEEP_ALL ?
createSweepTransaction(dst_addr, "", mixin_count, _priority,
accountIndex) :
createTransactionJ(dst_addr, "", amount, mixin_count, _priority,
accountIndex));
pendingTransaction = new PendingTransaction(txHandle);
return pendingTransaction;
}
private native long createTransactionJ(String dst_addr, String payment_id,
long amount, int mixin_count,
int priority, int accountIndex);
private native long createSweepTransaction(String dst_addr, String payment_id,
int mixin_count,
int priority, int accountIndex);
public PendingTransaction createSweepUnmixableTransaction() {
disposePendingTransaction();
long txHandle = createSweepUnmixableTransactionJ();
pendingTransaction = new PendingTransaction(txHandle);
return pendingTransaction;
}
private native long createSweepUnmixableTransactionJ();
//virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0;
//virtual bool submitTransaction(const std::string &fileName) = 0;
public native void disposeTransaction(PendingTransaction pendingTransaction);
//virtual bool exportKeyImages(const std::string &filename) = 0;
//virtual bool importKeyImages(const std::string &filename) = 0;
//virtual TransactionHistory * history() const = 0;
private TransactionHistory history = null;
public TransactionHistory getHistory() {
if (history == null) {
history = new TransactionHistory(getHistoryJ(), accountIndex);
}
return history;
}
private native long getHistoryJ();
public void refreshHistory() {
getHistory().refreshWithNotes(this);
}
//virtual AddressBook * addressBook() const = 0;
//virtual void setListener(WalletListener *) = 0;
private native long setListenerJ(WalletListener listener);
public void setListener(WalletListener listener) {
this.listenerHandle = setListenerJ(listener);
}
public native int getDefaultMixin();
public native void setDefaultMixin(int mixin);
public native boolean setUserNote(String txid, String note);
public native String getUserNote(String txid);
public native String getTxKey(String txid);
//virtual std::string signMessage(const std::string &message) = 0;
//virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
//virtual bool rescanSpent() = 0;
private static final String NEW_ACCOUNT_NAME = "Untitled account"; // src/wallet/wallet2.cpp:941
public void addAccount() {
addAccount(NEW_ACCOUNT_NAME);
}
public native void addAccount(String label);
public String getAccountLabel() {
return getAccountLabel(accountIndex);
}
public String getAccountLabel(int accountIndex) {
String label = getSubaddressLabel(accountIndex, 0);
if (label.equals(NEW_ACCOUNT_NAME)) {
String address = getAddress(accountIndex);
int len = address.length();
label = address.substring(0, 6) +
"\u2026" + address.substring(len - 6, len);
}
return label;
}
public String getSubaddressLabel(int addressIndex) {
return getSubaddressLabel(accountIndex, addressIndex);
}
public native String getSubaddressLabel(int accountIndex, int addressIndex);
public void setAccountLabel(String label) {
setAccountLabel(accountIndex, label);
}
public void setAccountLabel(int accountIndex, String label) {
setSubaddressLabel(accountIndex, 0, label);
}
public void setSubaddressLabel(int addressIndex, String label) {
setSubaddressLabel(accountIndex, addressIndex, label);
refreshHistory();
}
public native void setSubaddressLabel(int accountIndex, int addressIndex, String label);
public native int getNumAccounts();
public int getNumSubaddresses() {
return getNumSubaddresses(accountIndex);
}
public native int getNumSubaddresses(int accountIndex);
public String getNewSubaddress() {
return getNewSubaddress(accountIndex);
}
public String getNewSubaddress(int accountIndex) {
String timeStamp = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).format(new Date());
addSubaddress(accountIndex, timeStamp);
String subaddress = getLastSubaddress(accountIndex);
Timber.d("%d: %s", getNumSubaddresses(accountIndex) - 1, subaddress);
return subaddress;
}
public native void addSubaddress(int accountIndex, String label);
public String getLastSubaddress(int accountIndex) {
return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1);
}
public Wallet.Device getDeviceType() {
int device = getDeviceTypeJ();
return Wallet.Device.values()[device + 1]; // mapping is monero+1=android
}
private native int getDeviceTypeJ();
}