/*
* Copyright 2014 http://Bither.net
*
* 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 net.bither.bitherj.core;
import net.bither.bitherj.AbstractApp;
import net.bither.bitherj.BitherjSettings;
import net.bither.bitherj.db.AbstractDb;
import net.bither.bitherj.db.imp.AbstractTxProvider;
import net.bither.bitherj.script.Script;
import net.bither.bitherj.utils.Sha256Hash;
import net.bither.bitherj.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class AddressManager implements HDMKeychain.HDMAddressChangeDelegate,
EnterpriseHDMKeychain.EnterpriseHDMKeychainAddressChangeDelegate {
private static final Logger log = LoggerFactory.getLogger(AddressManager.class);
private final byte[] lock = new byte[0];
private static AddressManager uniqueInstance = new AddressManager();
protected List<Address> privKeyAddresses = new ArrayList<Address>();
protected List<Address> watchOnlyAddresses = new ArrayList<Address>();
protected List<Address> trashAddresses = new ArrayList<Address>();
protected HashSet<String> addressHashSet = new HashSet<String>();
protected HDMKeychain hdmKeychain;
protected EnterpriseHDMKeychain enterpriseHDMKeychain;
protected HDAccount hdAccountHot;
protected HDAccount hdAccountMonitored;
protected List<DesktopHDMKeychain> desktopHDMKeychains;
private AddressManager() {
synchronized (lock) {
initAddress();
initHDMKeychain();
initEnterpriseHDMKeychain();
initHDAccounts();
initDesktopHDMKeychain();
initAliasAndVanityLen();
AbstractApp.addressIsReady = true;
AbstractApp.notificationService.sendBroadcastAddressLoadCompleteState();
}
}
public static AddressManager getInstance() {
return uniqueInstance;
}
private void initAliasAndVanityLen() {
Map<String, String> addressAlias = AbstractDb.addressProvider.getAliases();
Map<String, Integer> vanityAddresses = AbstractDb.addressProvider.getVanitylens();
if (addressAlias.size() == 0 && vanityAddresses.size() == 0) {
return;
}
for (Address address : privKeyAddresses) {
String addressStr = address.getAddress();
if (addressAlias.containsKey(addressStr)) {
String alias = addressAlias.get(addressStr);
address.setAlias(alias);
}
if (vanityAddresses.containsKey(addressStr)) {
int vanityLen = vanityAddresses.get(addressStr);
address.setVanityLen(vanityLen);
}
}
for (Address address : watchOnlyAddresses) {
String addressStr = address.getAddress();
if (addressAlias.containsKey(addressStr)) {
String alias = addressAlias.get(addressStr);
address.setAlias(alias);
}
if (vanityAddresses.containsKey(addressStr)) {
int vanityLen = vanityAddresses.get(addressStr);
address.setVanityLen(vanityLen);
}
}
if (hdmKeychain != null) {
for (HDMAddress address : hdmKeychain.getAllCompletedAddresses()) {
if (addressAlias.containsKey(address.getAddress())) {
String alias = addressAlias.get(address.getAddress());
address.setAlias(alias);
}
}
}
if (enterpriseHDMKeychain != null) {
for (EnterpriseHDMAddress address : enterpriseHDMKeychain.getAddresses()) {
if (addressAlias.containsKey(address.getAddress())) {
String alias = addressAlias.get(address.getAddress());
address.setAlias(alias);
}
}
}
}
private void initAddress() {
List<Address> addressList = AbstractDb.addressProvider.getAddresses();
for (Address address : addressList) {
if (address.hasPrivKey()) {
if (address.isTrashed()) {
this.trashAddresses.add(address);
} else {
this.privKeyAddresses.add(address);
this.addressHashSet.add(address.getAddress());
}
} else {
this.watchOnlyAddresses.add(address);
this.addressHashSet.add(address.getAddress());
}
}
}
private void initHDAccounts() {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.HOT) {
List<Integer> seeds = AbstractDb.hdAccountProvider.getHDAccountSeeds();
for (int seedId : seeds) {
if (hdAccountHot == null && AbstractDb.hdAccountProvider.hasMnemonicSeed(seedId)) {
hdAccountHot = new HDAccount(seedId);
} else if (hdAccountMonitored == null && !AbstractDb.hdAccountProvider.hasMnemonicSeed(seedId)) {
hdAccountMonitored = new HDAccount(seedId);
}
}
}
}
private void initDesktopHDMKeychain() {
if (AbstractDb.desktopAddressProvider != null) {
List<Integer> seeds = AbstractDb.desktopAddressProvider.getDesktopKeyChainSeed();
if (seeds.size() > 0) {
desktopHDMKeychains = new ArrayList<DesktopHDMKeychain>();
for (int i = 0;
i < seeds.size();
i++) {
desktopHDMKeychains.add(new DesktopHDMKeychain(seeds.get(i)));
}
}
}
}
public boolean hasDesktopHDMKeychain() {
return desktopHDMKeychains != null && desktopHDMKeychains.size() > 0;
}
public void setDesktopHDMKeychains(List<DesktopHDMKeychain> desktopHDMKeychains) {
this.desktopHDMKeychains = desktopHDMKeychains;
}
public List<DesktopHDMKeychain> getDesktopHDMKeychains() {
return this.desktopHDMKeychains;
}
public boolean registerTx(Tx tx, Tx.TxNotificationType txNotificationType, boolean isConfirmed) {
if (isConfirmed) {
byte[] existTx = AbstractDb.txProvider.isIdentify(tx);
if (existTx.length > 0) {
AbstractDb.txProvider.remove(existTx);
}
} else {
byte[] existTx = AbstractDb.txProvider.isIdentify(tx);
if (existTx.length > 0) {
return false;
}
}
if (AbstractDb.txProvider.isTxDoubleSpendWithConfirmedTx(tx)) {
// double spend with confirmed tx
return false;
}
// long begin = System.currentTimeMillis();
List<String> inAddresses = tx.getInAddresses();
// log.info("getInAddresses time : {} ,ins:{}", (System.currentTimeMillis() - begin), tx
// .getIns().size());
boolean isRegister = false;
Tx compressedTx;
tx = AbstractDb.hdAccountAddressProvider.updateOutHDAccountId(tx);
if (txNotificationType != Tx.TxNotificationType.txSend) {
compressedTx = compressTx(tx, inAddresses);
} else {
compressedTx = tx;
}
HashSet<String> needNotifyAddressHashSet = new HashSet<String>();
// HashSet<String> needNotifyHDAccountHS = new HashSet<String>();
// HashSet<String> needNotifyHDAccountMonitoredHS = new HashSet<String>();
HashSet<String> needNotifyDesktopHDMHS = new HashSet<String>();
HashSet<Integer> needNotifyHDAccountIdHS = new HashSet<Integer>();
// List<HDAccount.HDAccountAddress> relatedAddresses = new ArrayList<HDAccount
// .HDAccountAddress>();
// List<HDAccount.HDAccountAddress> relatedHDMonitoredAddresses = new ArrayList<HDAccount
// .HDAccountAddress>();
List<DesktopHDMAddress> relatedDesktopHDMAddresses = new ArrayList<DesktopHDMAddress>();
// HashSet<String> relatedAddressesHS = new HashSet<String>();
// HashSet<String> relatedHDMonitoredAddressesHS = new HashSet<String>();
HashSet<String> relatedDesktopHDMAddressesHS = new HashSet<String>();
// if (hdAccountHot != null) {
// relatedAddresses = hdAccountHot.getRelatedAddressesForTx(compressedTx, inAddresses);
// }
// if (hdAccountMonitored != null) {
// relatedHDMonitoredAddresses = hdAccountMonitored.getRelatedAddressesForTx
// (compressedTx, inAddresses);
// }
if (hasDesktopHDMKeychain()) {
DesktopHDMKeychain desktopHDMKeychain = desktopHDMKeychains.get(0);
relatedDesktopHDMAddresses = desktopHDMKeychain.getRelatedAddressesForTx
(compressedTx, inAddresses);
}
// for (HDAccount.HDAccountAddress hdAccountAddress : relatedAddresses) {
// relatedAddressesHS.add(hdAccountAddress.getAddress());
// }
// for (HDAccount.HDAccountAddress hdAccountAddress : relatedHDMonitoredAddresses) {
// relatedHDMonitoredAddressesHS.add(hdAccountAddress.getAddress());
// }
for (DesktopHDMAddress desktopHDMAddress : relatedDesktopHDMAddresses) {
relatedDesktopHDMAddressesHS.add(desktopHDMAddress.getAddress());
}
for (Out out : compressedTx.getOuts()) {
String outAddress = out.getOutAddress();
if (addressHashSet.contains(outAddress)) {
needNotifyAddressHashSet.add(outAddress);
}
// if (relatedAddressesHS.contains(outAddress)) {
// needNotifyHDAccountHS.add(outAddress);
// }
//
// if (relatedHDMonitoredAddressesHS.contains(outAddress)) {
// needNotifyHDAccountMonitoredHS.add(outAddress);
// }
if (relatedDesktopHDMAddressesHS.contains(outAddress)) {
needNotifyDesktopHDMHS.add(outAddress);
}
if (out.getHDAccountId() > 0) {
needNotifyHDAccountIdHS.add(out.getHDAccountId());
}
}
Tx txInDb = AbstractDb.txProvider.getTxDetailByTxHash(tx.getTxHash());
if (txInDb != null) {
for (Out out : txInDb.getOuts()) {
String outAddress = out.getOutAddress();
if (needNotifyAddressHashSet.contains(outAddress)) {
needNotifyAddressHashSet.remove(outAddress);
}
// if (needNotifyHDAccountHS.contains(outAddress)) {
// needNotifyHDAccountHS.remove(outAddress);
// }
// if (needNotifyHDAccountMonitoredHS.contains(outAddress)) {
// needNotifyHDAccountMonitoredHS.remove(outAddress);
// }
if (needNotifyDesktopHDMHS.contains(outAddress)) {
needNotifyDesktopHDMHS.remove(outAddress);
}
if (out.getHDAccountId() > 0 && needNotifyHDAccountIdHS.contains(out.getHDAccountId())) {
needNotifyHDAccountIdHS.remove(out.getHDAccountId());
}
}
isRegister = true;
} else {
for (String address : inAddresses) {
if (addressHashSet.contains(address)) {
needNotifyAddressHashSet.add(address);
}
// if (relatedAddressesHS.contains(address)) {
// needNotifyHDAccountHS.add(address);
// }
// if (relatedHDMonitoredAddressesHS.contains(address)) {
// needNotifyHDAccountMonitoredHS.add(address);
// }
if (relatedDesktopHDMAddressesHS.contains(address)) {
needNotifyDesktopHDMHS.add(address);
}
}
needNotifyHDAccountIdHS.addAll(AbstractDb.hdAccountAddressProvider.getRelatedHDAccountIdList(inAddresses));
isRegister = needNotifyAddressHashSet.size() > 0
|| needNotifyDesktopHDMHS.size() > 0 || needNotifyHDAccountIdHS.size() > 0;
}
if (needNotifyAddressHashSet.size() > 0 || needNotifyHDAccountIdHS.size() > 0
|| needNotifyDesktopHDMHS.size() > 0) {
AbstractDb.txProvider.add(compressedTx);
log.info("add tx {} into db", Utils.hashToString(tx.getTxHash()));
}
for (Address addr : AddressManager.getInstance().getAllAddresses()) {
if (needNotifyAddressHashSet.contains(addr.getAddress())) {
addr.notificatTx(tx, txNotificationType);
}
}
// List<HDAccount.HDAccountAddress> needNotifityAddressList = new ArrayList<HDAccount
// .HDAccountAddress>();
// for (HDAccount.HDAccountAddress hdAccountAddress : relatedAddresses) {
// if (needNotifyHDAccountHS.contains(hdAccountAddress.getAddress())) {
// needNotifityAddressList.add(hdAccountAddress);
// }
// }
//
// List<HDAccount.HDAccountAddress> needNotifyHDMonitoredAddressList = new
// ArrayList<HDAccount.HDAccountAddress>();
// for (HDAccount.HDAccountAddress hdAccountAddress : relatedHDMonitoredAddresses) {
// if (needNotifyHDAccountMonitoredHS.contains(hdAccountAddress.getAddress())) {
// needNotifyHDMonitoredAddressList.add(hdAccountAddress);
// }
// }
List<DesktopHDMAddress> needNotifityDesktopHDMAddressList = new
ArrayList<DesktopHDMAddress>();
for (DesktopHDMAddress desktopHDMAddress : relatedDesktopHDMAddresses) {
if (needNotifyDesktopHDMHS.contains(desktopHDMAddress.getAddress())) {
needNotifityDesktopHDMAddressList.add(desktopHDMAddress);
}
}
// if (needNotifityAddressList.size() > 0) {
// getHDAccountHot().onNewTx(tx, needNotifityAddressList, txNotificationType);
// }
// if (needNotifyHDAccountMonitoredHS.size() > 0) {
// getHDAccountMonitored().onNewTx(tx, needNotifyHDMonitoredAddressList,
// txNotificationType);
// }
if (needNotifityDesktopHDMAddressList.size() > 0) {
DesktopHDMKeychain desktopHDMKeychain = desktopHDMKeychains.get(0);
desktopHDMKeychain.onNewTx(tx, needNotifityDesktopHDMAddressList, txNotificationType);
}
this.onNewTx(tx, needNotifyHDAccountIdHS, txNotificationType);
return isRegister;
}
private void onNewTx(Tx tx, HashSet<Integer> relatedHDAccountIdList, Tx.TxNotificationType txNotificationType) {
for (Integer i : relatedHDAccountIdList) {
if (hasHDAccountHot() && getHDAccountHot().getHdSeedId() == i) {
getHDAccountHot().onNewTx(tx, txNotificationType);
}
if (hasHDAccountMonitored() && getHDAccountMonitored().getHdSeedId() == i) {
getHDAccountMonitored().onNewTx(tx, txNotificationType);
}
}
}
public boolean isTxRelated(Tx tx, List<String> inAddresses) {
for (Address address : this.getAllAddresses()) {
// todo: may be do not need query in db, just ^ with in and out 's address
if (isAddressContainsTx(address.getAddress(), tx)) {
return true;
}
}
tx = AbstractDb.hdAccountAddressProvider.updateOutHDAccountId(tx);
for (Out out : tx.getOuts()) {
if (out.getHDAccountId() > 0) {
return true;
}
}
List<String> addressList = tx.getOutAddressList();
addressList.addAll(inAddresses);
if (AbstractDb.hdAccountAddressProvider.getRelatedAddressCnt(addressList) > 0){
return true;
}
// if (hasHDAccountHot()) {
// if (getHDAccountHot().isTxRelated(tx, inAddresses)) {
// return true;
// }
// }
// if (hasHDAccountMonitored()) {
// if (getHDAccountMonitored().isTxRelated(tx, inAddresses)) {
// return true;
// }
// }
if (hasDesktopHDMKeychain()) {
if (getDesktopHDMKeychains().get(0).isTxRelated(tx, inAddresses)) {
return true;
}
}
return false;
}
private boolean isAddressContainsTx(String address, Tx tx) {
Set<String> outAddress = new HashSet<String>();
for (Out out : tx.getOuts()) {
outAddress.add(out.getOutAddress());
}
if (outAddress.contains(address)) {
return true;
} else {
return AbstractDb.txProvider.isAddressContainsTx(address, tx);
}
}
public boolean addAddress(Address address) {
synchronized (lock) {
if (getAllAddresses().contains(address)) {
return false;
}
if (address.hasPrivKey()) {
long sortTime = getPrivKeySortTime();
address.setSortTime(sortTime);
if (!this.getTrashAddresses().contains(address)) {
AbstractDb.addressProvider.addAddress(address);
privKeyAddresses.add(0, address);
addressHashSet.add(address.address);
} else {
address.setSyncComplete(false);
AbstractDb.addressProvider.restorePrivKeyAddress(address);
trashAddresses.remove(address);
privKeyAddresses.add(0, address);
addressHashSet.add(address.address);
}
} else {
long sortTime = getWatchOnlySortTime();
address.setSortTime(sortTime);
AbstractDb.addressProvider.addAddress(address);
watchOnlyAddresses.add(0, address);
addressHashSet.add(address.address);
}
return true;
}
}
public long getSortTime(boolean hasPrivateKey) {
if (hasPrivateKey) {
return getPrivKeySortTime();
} else {
return getWatchOnlySortTime();
}
}
private long getWatchOnlySortTime() {
long sortTime = new Date().getTime();
if (getWatchOnlyAddresses().size() > 0) {
long firstSortTime = getWatchOnlyAddresses().get(0).getSortTime()
+ getWatchOnlyAddresses().size();
if (sortTime < firstSortTime) {
sortTime = firstSortTime;
}
}
return sortTime;
}
private long getPrivKeySortTime() {
long sortTime = new Date().getTime();
if (getPrivKeyAddresses().size() > 0) {
long firstSortTime = getPrivKeyAddresses().get(0).getSortTime()
+ getPrivKeyAddresses().size();
if (sortTime < firstSortTime) {
sortTime = firstSortTime;
}
}
return sortTime;
}
public boolean stopMonitor(Address address) {
synchronized (lock) {
if (!address.hasPrivKey()) {
AbstractDb.addressProvider.removeWatchOnlyAddress(address);
watchOnlyAddresses.remove(address);
addressHashSet.remove(address.address);
} else {
return false;
}
return true;
}
}
public boolean trashPrivKey(Address address) {
synchronized (lock) {
if ((address.hasPrivKey() || address.isHDM()) && address.getBalance() == 0) {
if (address.isHDM() && hdmKeychain.getAddresses().size() <= 1) {
return false;
}
address.setTrashed(true);
AbstractDb.addressProvider.trashPrivKeyAddress(address);
trashAddresses.add(address);
privKeyAddresses.remove(address);
addressHashSet.remove(address.address);
} else {
return false;
}
return true;
}
}
public boolean restorePrivKey(Address address) {
synchronized (lock) {
if (address.hasPrivKey() || address.isHDM()) {
long sortTime = getPrivKeySortTime();
address.setSortTime(sortTime);
address.setSyncComplete(false);
address.setTrashed(false);
AbstractDb.addressProvider.restorePrivKeyAddress(address);
if (address.hasPrivKey() && !address.isHDM()) {
privKeyAddresses.add(0, address);
}
trashAddresses.remove(address);
addressHashSet.add(address.address);
} else {
return false;
}
return true;
}
}
public List<Address> getPrivKeyAddresses() {
synchronized (lock) {
return this.privKeyAddresses;
}
}
public List<Address> getWatchOnlyAddresses() {
synchronized (lock) {
return this.watchOnlyAddresses;
}
}
public List<Address> getTrashAddresses() {
synchronized (lock) {
return this.trashAddresses;
}
}
public List<Address> getAllAddresses() {
synchronized (lock) {
ArrayList<Address> result = new ArrayList<Address>();
if (hasHDMKeychain()) {
result.addAll(getHdmKeychain().getAddresses());
}
result.addAll(this.privKeyAddresses);
result.addAll(this.watchOnlyAddresses);
if (hasEnterpriseHDMKeychain()) {
result.addAll(getEnterpriseHDMKeychain().getAddresses());
}
return result;
}
}
public HashSet<String> getAddressHashSet() {
synchronized (lock) {
return this.addressHashSet;
}
}
public boolean addressIsSyncComplete() {
for (Address address : AddressManager.getInstance().getAllAddresses()) {
if (!address.isSyncComplete()) {
return false;
}
}
if (hdAccountHot != null && !hdAccountHot.isSyncComplete()) {
return false;
}
if (hdAccountMonitored != null && !hdAccountMonitored.isSyncComplete()) {
return false;
}
if (hasDesktopHDMKeychain() && !desktopHDMKeychains.get(0).isSyncComplete()) {
return false;
}
if (hasHDAccountMonitored() && !getHDAccountMonitored().isSyncComplete()) {
return false;
}
return true;
}
private void initHDMKeychain() {
List<Integer> seeds = AbstractDb.addressProvider.getHDSeeds();
if (seeds.size() > 0) {
hdmKeychain = new HDMKeychain(seeds.get(0));
hdmKeychain.setAddressChangeDelegate(this);
List<HDMAddress> addresses = hdmKeychain.getAddresses();
for (HDMAddress a : addresses) {
addressHashSet.add(a.getAddress());
}
}
}
private void initEnterpriseHDMKeychain() {
if (AbstractDb.enterpriseHDMProvider != null) {
List<Integer> ids = AbstractDb.enterpriseHDMProvider.getEnterpriseHDMKeychainIds();
if (ids != null && ids.size() > 0) {
enterpriseHDMKeychain = new EnterpriseHDMKeychain(ids.get(0));
enterpriseHDMKeychain.setAddressChangeDelegate(this);
List<EnterpriseHDMAddress> addresses = enterpriseHDMKeychain.getAddresses();
for (EnterpriseHDMAddress a : addresses) {
addressHashSet.add(a.getAddress());
}
}
}
}
public void setHdAccountHot(HDAccount hdAccountHot) {
this.hdAccountHot = hdAccountHot;
}
public void setHDMKeychain(HDMKeychain keychain) {
synchronized (lock) {
if (hdmKeychain != null && hdmKeychain != keychain) {
throw new RuntimeException("can not add a different hdm keychain to address " +
"manager");
}
if (hdmKeychain == keychain) {
return;
}
hdmKeychain = keychain;
hdmKeychain.setAddressChangeDelegate(this);
List<HDMAddress> addresses = hdmKeychain.getAddresses();
for (HDMAddress a : addresses) {
addressHashSet.add(a.getAddress());
}
}
}
public boolean hasHDMKeychain() {
synchronized (lock) {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
return hdmKeychain != null;
} else {
return hdmKeychain != null && hdmKeychain.getAddresses().size() > 0;
}
}
}
public HDMKeychain getHdmKeychain() {
synchronized (lock) {
return hdmKeychain;
}
}
public void setEnterpriseHDMKeychain(EnterpriseHDMKeychain keychain) {
synchronized (lock) {
if (enterpriseHDMKeychain != null && enterpriseHDMKeychain != keychain) {
throw new RuntimeException("can not add a different enterprise hdm keychain to "
+ "address manager");
}
if (keychain == enterpriseHDMKeychain) {
return;
}
enterpriseHDMKeychain = keychain;
enterpriseHDMKeychain.setAddressChangeDelegate(this);
List<EnterpriseHDMAddress> addresses = enterpriseHDMKeychain.getAddresses();
for (EnterpriseHDMAddress a : addresses) {
addressHashSet.add(a.getAddress());
}
}
}
public boolean hasEnterpriseHDMKeychain() {
synchronized (lock) {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
return false;
} else {
return enterpriseHDMKeychain != null;
}
}
}
public EnterpriseHDMKeychain getEnterpriseHDMKeychain() {
synchronized (lock) {
return enterpriseHDMKeychain;
}
}
public boolean hasHDAccountHot() {
synchronized (lock) {
return hdAccountHot != null;
}
}
public HDAccount getHDAccountHot() {
synchronized (lock) {
return hdAccountHot;
}
}
public void setHDAccountMonitored(HDAccount account) {
synchronized (lock) {
hdAccountMonitored = account;
}
}
public boolean hasHDAccountMonitored() {
synchronized (lock) {
return hdAccountMonitored != null;
}
}
public HDAccount getHDAccountMonitored() {
synchronized (lock) {
return hdAccountMonitored;
}
}
public boolean hasHDAccountCold() {
synchronized (lock) {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
List<Integer> seeds = AbstractDb.hdAccountProvider.getHDAccountSeeds();
for (int seedId : seeds) {
if (AbstractDb.hdAccountProvider.hasMnemonicSeed(seedId)) {
return true;
}
}
}
return false;
}
}
public HDAccountCold getHDAccountCold() {
synchronized (lock) {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
List<Integer> seeds = AbstractDb.hdAccountProvider.getHDAccountSeeds();
for (int seedId : seeds) {
if (AbstractDb.hdAccountProvider.hasMnemonicSeed(seedId)) {
return new HDAccountCold(seedId);
}
}
}
return null;
}
}
// @Override
public void hdmAddressAdded(HDMAddress address) {
addressHashSet.add(address.getAddress());
}
// @Override
public void enterpriseHDMKeychainAddedAddress(EnterpriseHDMAddress address) {
if (address != null) {
addressHashSet.add(address.getAddress());
}
}
public List<Tx> compressTxsForApi(List<Tx> txList, Address address) {
Map<Sha256Hash, Tx> txHashList = new HashMap<Sha256Hash, Tx>();
for (Tx tx : txList) {
txHashList.put(new Sha256Hash(tx.getTxHash()), tx);
}
for (Tx tx : txList) {
if (!isSendFromMe(tx, txHashList, address) && tx.getOuts().size() > BitherjSettings
.COMPRESS_OUT_NUM) {
List<Out> outList = new ArrayList<Out>();
for (Out out : tx.getOuts()) {
if (Utils.compareString(address.getAddress(), out.getOutAddress())) {
outList.add(out);
}
}
tx.setOuts(outList);
}
}
return txList;
}
public List<Tx> compressTxsForHDAccount(List<Tx> txList) {
Map<Sha256Hash, Tx> txHashList = new HashMap<Sha256Hash, Tx>();
for (Tx tx : txList) {
txHashList.put(new Sha256Hash(tx.getTxHash()), tx);
AbstractDb.hdAccountAddressProvider.updateOutHDAccountId(tx);
}
for (Tx tx : txList) {
if (!isSendFromHDAccount(tx, txHashList) && tx.getOuts().size() > BitherjSettings
.COMPRESS_OUT_NUM) {
List<Out> outList = new ArrayList<Out>();
HashSet<String> addressHashSet = AbstractDb.hdAccountAddressProvider.
getBelongAccountAddresses(tx.getOutAddressList());
for (Out out : tx.getOuts()) {
if (addressHashSet.contains(out.getOutAddress())) {
outList.add(out);
}
}
tx.setOuts(outList);
}
}
return txList;
}
// public List<Tx> compressTxsForHDAccountMoitored(List<Tx> txList) {
// Map<Sha256Hash, Tx> txHashList = new HashMap<Sha256Hash, Tx>();
// for (Tx tx : txList) {
// txHashList.put(new Sha256Hash(tx.getTxHash()), tx);
// }
// for (Tx tx : txList) {
// if (!isSendFromHDAccount(tx, txHashList) && tx.getOuts().size() > BitherjSettings
// .COMPRESS_OUT_NUM) {
// List<Out> outList = new ArrayList<Out>();
// HashSet<String> addressHashSet = AbstractDb.hdAccountAddressProvider.
// getBelongAccountAddresses(tx.getOutAddressList());
// for (Out out : tx.getOuts()) {
// if (addressHashSet.contains(out.getOutAddress())) {
// outList.add(out);
// }
// }
// tx.setOuts(outList);
// }
// }
//
// return txList;
// }
// public List<Tx> compressTxsForHDAccountMonitored(List<Tx> txList) {
// Map<Sha256Hash, Tx> txHashList = new HashMap<Sha256Hash, Tx>();
// for (Tx tx : txList) {
// txHashList.put(new Sha256Hash(tx.getTxHash()), tx);
// }
// for (Tx tx : txList) {
// if (!isSendFromHDAccountMonitored(tx, txHashList) && tx.getOuts().size() >
// BitherjSettings.COMPRESS_OUT_NUM) {
// List<Out> outList = new ArrayList<Out>();
// HashSet<String> addressHashSet = AbstractDb.hdAccountAddressProvider
// .getBelongAccountAddresses(tx.getOutAddressList());
// for (Out out : tx.getOuts()) {
// if (addressHashSet.contains(out.getOutAddress())) {
// outList.add(out);
// }
// }
// tx.setOuts(outList);
// }
// }
//
// return txList;
// }
public List<Tx> compressTxsForDesktopHDM(List<Tx> txList) {
Map<Sha256Hash, Tx> txHashList = new HashMap<Sha256Hash, Tx>();
for (Tx tx : txList) {
txHashList.put(new Sha256Hash(tx.getTxHash()), tx);
}
for (Tx tx : txList) {
if (!isSendFromHDAccount(tx, txHashList) && tx.getOuts().size() > BitherjSettings
.COMPRESS_OUT_NUM) {
List<Out> outList = new ArrayList<Out>();
HashSet<String> addressHashSet = AbstractDb.desktopTxProvider.
getBelongAccountAddresses(tx.getOutAddressList());
for (Out out : tx.getOuts()) {
if (addressHashSet.contains(out.getOutAddress())) {
outList.add(out);
}
}
tx.setOuts(outList);
}
}
return txList;
}
private boolean isSendFromMe(Tx tx, Map<Sha256Hash, Tx> txHashList, Address address) {
for (In in : tx.getIns()) {
Sha256Hash prevTxHahs = new Sha256Hash(in.getPrevTxHash());
if (txHashList.containsKey(prevTxHahs)) {
Tx preTx = txHashList.get(prevTxHahs);
for (Out out : preTx.getOuts()) {
if (out.getOutSn() == in.getPrevOutSn()) {
if (Utils.compareString(out.getOutAddress(), address.getAddress())) {
return true;
}
}
}
}
}
return false;
}
private boolean isSendFromHDAccount(Tx tx, Map<Sha256Hash, Tx> txHashList) {
List<String> inAddressList = new ArrayList<String>();
for (In in : tx.getIns()) {
Sha256Hash prevTxHahs = new Sha256Hash(in.getPrevTxHash());
if (txHashList.containsKey(prevTxHahs)) {
Tx preTx = txHashList.get(prevTxHahs);
for (Out out : preTx.getOuts()) {
if (out.getOutSn() == in.getPrevOutSn()) {
inAddressList.add(out.getOutAddress());
}
}
}
}
return AbstractDb.hdAccountAddressProvider.getRelatedAddressCnt(inAddressList) > 0;
}
// private boolean isSendFromHDAccountMonitored(Tx tx, Map<Sha256Hash, Tx> txHashList) {
// List<String> inAddressList = new ArrayList<String>();
// for (In in : tx.getIns()) {
// Sha256Hash prevTxHash = new Sha256Hash(in.getPrevTxHash());
// if (txHashList.containsKey(prevTxHash)) {
// Tx preTx = txHashList.get(prevTxHash);
// for (Out out : preTx.getOuts()) {
// if (out.getOutSn() == in.getPrevOutSn()) {
// inAddressList.add(out.getOutAddress());
// }
// }
// }
// }
// List<HDAccount.HDAccountAddress> hdAccountAddressList = AbstractDb.hdAccountAddressProvider
// .belongAccount(this.hdAccountHot.hdSeedId, inAddressList);
// return hdAccountAddressList != null && hdAccountAddressList.size() > 0;
// }
public Tx compressTx(Tx tx, List<String> inAddresses) {
if (tx.getOuts().size() > BitherjSettings.COMPRESS_OUT_NUM
&& !isSendFromMe(tx, inAddresses)) {
List<Out> outList = new ArrayList<Out>();
for (Out out : tx.getOuts()) {
String outAddress = out.getOutAddress();
if (addressHashSet.contains(outAddress) || out.getHDAccountId() > 0) {
outList.add(out);
}
}
tx.setOuts(outList);
}
return tx;
}
private boolean isSendFromMe(Tx tx, List<String> addresses) {
return this.addressHashSet.containsAll(addresses) || AbstractDb.hdAccountAddressProvider.getRelatedAddressCnt(addresses) > 0;
}
public static boolean isPrivateLimit() {
int maxPrivateKey = AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode
.COLD ?
AbstractApp.bitherjSetting.watchOnlyAddressCountLimit()
: AbstractApp.bitherjSetting.privateKeyOfHotCountLimit();
return AddressManager.getInstance().getPrivKeyAddresses() != null
&& AddressManager.getInstance().getPrivKeyAddresses().size() >= maxPrivateKey;
}
public static boolean isWatchOnlyLimit() {
return AddressManager.getInstance().getWatchOnlyAddresses() != null
&& AddressManager.getInstance().getWatchOnlyAddresses().size() >= AbstractApp
.bitherjSetting.watchOnlyAddressCountLimit();
}
public static int canAddPrivateKeyCount() {
int max;
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
max = AbstractApp.bitherjSetting.watchOnlyAddressCountLimit() - AddressManager
.getInstance()
.getAllAddresses().size();
} else {
max = AbstractApp.bitherjSetting.privateKeyOfHotCountLimit() - AddressManager
.getInstance()
.getPrivKeyAddresses().size();
}
return max;
}
public static boolean isHDMKeychainLimit() {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
return AddressManager.getInstance().getHdmKeychain() != null;
} else {
if (AddressManager.getInstance().getHdmKeychain() == null) {
return false;
}
return AddressManager.getInstance().getHdmKeychain().getAllCompletedAddresses().size
() > 0;
}
}
public static boolean isHDMAddressLimit() {
if (AbstractApp.bitherjSetting.getAppMode() == BitherjSettings.AppMode.COLD) {
return true;
}
if (AddressManager.getInstance().getHdmKeychain() == null) {
return false;
}
return AddressManager.getInstance().getHdmKeychain().getAllCompletedAddresses().size()
>= AbstractApp.bitherjSetting.hdmAddressPerSeedCount();
}
public HashMap<String, Address> getNeededPrivKeyAddresses(Tx tx) {
HashMap<String, Address> result = new HashMap<String, Address>();
for (In in : tx.getIns()) {
Script pubKeyScript = new Script(in.getPrevOutScript());
String address = pubKeyScript.getToAddress();
for (Address privKey : this.getPrivKeyAddresses()) {
if (Utils.compareString(address, privKey.address)) {
result.put(address, privKey);
break;
}
}
}
return result;
}
}