/* * * 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.db; import net.bither.ApplicationInstanceManager; import net.bither.bitherj.core.Address; import net.bither.bitherj.core.HDMAddress; import net.bither.bitherj.core.HDMBId; import net.bither.bitherj.core.HDMKeychain; import net.bither.bitherj.crypto.EncryptedData; import net.bither.bitherj.crypto.PasswordSeed; import net.bither.bitherj.db.AbstractDb; import net.bither.bitherj.db.IAddressProvider; import net.bither.bitherj.exception.AddressFormatException; import net.bither.bitherj.utils.Base58; import net.bither.bitherj.utils.Utils; import javax.annotation.Nullable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class AddressProvider implements IAddressProvider { private static AddressProvider addressProvider = new AddressProvider(ApplicationInstanceManager.addressDBHelper); private static final String insertHDSeedSql = "insert into hd_seeds " + "(encrypt_seed,encrypt_hd_seed,is_xrandom,hdm_address)" + " values (?,?,?,?) "; private static final String insertAddressSql = "insert into addresses " + "(address,encrypt_private_key,pub_key,is_xrandom,is_trash,is_synced,sort_time)" + " values (?,?,?,?,?,?,?) "; private static final String insertHDMBidSql = "insert into hdm_bid " + "(hdm_bid,encrypt_bither_password)" + " values (?,?) "; private static final String updateHDMBidSql = "update hdm_bid set " + " encrypt_bither_password=? where hdm_bid=?"; public static AddressProvider getInstance() { return addressProvider; } private AddressDBHelper mDb; private AddressProvider(AddressDBHelper db) { this.mDb = db; } @Override public boolean changePassword(CharSequence oldPassword, CharSequence newPassword) { final HashMap<String, String> addressesPrivKeyHashMap = new HashMap<String, String>(); String hdmEncryptPassword = null; PasswordSeed passwordSeed = null; final HashMap<Integer, String> encryptMnemonicSeedHashMap = new HashMap<Integer, String>(); final HashMap<Integer, String> encryptHDSeedHashMap = new HashMap<Integer, String>(); HashMap<Integer, String> hdEncryptSeedHashMap = new HashMap<Integer, String>(); HashMap<Integer, String> hdEncryptMnemonicSeedHashMap = new HashMap<Integer, String>(); HashMap<Integer, String> singularModeBackupHashMap = new HashMap<Integer, String>(); try { String sql = "select address,encrypt_private_key,pub_key,is_xrandom from addresses where encrypt_private_key is not null"; PreparedStatement statement = this.mDb.getPreparedStatement(sql, null); ResultSet c = statement.executeQuery(); while (c.next()) { String address = null; String encryptPrivKey = null; String pubKey = null; boolean isXRandom = false; int idColumn = c.findColumn(AbstractDb.AddressesColumns.ADDRESS); if (idColumn != -1) { address = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.AddressesColumns.ENCRYPT_PRIVATE_KEY); if (idColumn != -1) { encryptPrivKey = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.AddressesColumns.PUB_KEY); if (idColumn != -1) { pubKey = c.getString(idColumn); } boolean isCompressed = true; try { isCompressed = Base58.decode(pubKey).length == 33; } catch (AddressFormatException e) { e.printStackTrace(); } idColumn = c.findColumn(AbstractDb.AddressesColumns.PUB_KEY); if (idColumn != -1) { isXRandom = c.getBoolean(idColumn); } addressesPrivKeyHashMap.put(address, new EncryptedData(encryptPrivKey).toEncryptedStringForQRCode(isCompressed, isXRandom)); } c.close(); statement.close(); sql = "select encrypt_bither_password from hdm_bid limit 1"; statement = this.mDb.getPreparedStatement(sql, null); c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDMBIdColumns.ENCRYPT_BITHER_PASSWORD); if (idColumn != -1) { hdmEncryptPassword = c.getString(idColumn); } } else { hdmEncryptPassword = null; } c.close(); statement.close(); sql = "select hd_seed_id,encrypt_seed,encrypt_hd_seed,singular_mode_backup from hd_seeds where encrypt_seed!='RECOVER'"; statement = this.mDb.getPreparedStatement(sql, null); c = statement.executeQuery(); while (c.next()) { int idColumn = c.findColumn(AbstractDb.HDSeedsColumns.HD_SEED_ID); Integer hdSeedId = 0; if (idColumn != -1) { hdSeedId = c.getInt(idColumn); } String encryptMnemonicSeed = null; idColumn = c.findColumn(AbstractDb.HDSeedsColumns.ENCRYPT_MNEMONIC_SEED); if (idColumn != -1) { encryptMnemonicSeed = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.HDSeedsColumns.ENCRYPT_HD_SEED); if (idColumn != -1) { String encryptHDSeed = c.getString(idColumn); if (!Utils.isEmpty(encryptHDSeed)) { encryptHDSeedHashMap.put(hdSeedId, encryptHDSeed); } } idColumn = c.findColumn(AbstractDb.HDSeedsColumns.SINGULAR_MODE_BACKUP); if (idColumn != -1) { String singularModeBackup = c.getString(idColumn); if (!Utils.isEmpty(singularModeBackup)) { singularModeBackupHashMap.put(hdSeedId, singularModeBackup); } } encryptMnemonicSeedHashMap.put(hdSeedId, encryptMnemonicSeed); } c.close(); statement.close(); statement = this.mDb.getPreparedStatement("select hd_account_id,encrypt_seed,encrypt_mnemonic_seed from hd_account ", null); c = statement.executeQuery(); while (c.next()) { int idColumn = c.findColumn(AbstractDb.HDAccountColumns.HD_ACCOUNT_ID); Integer hdAccountId = 0; if (idColumn != -1) { hdAccountId = c.getInt(idColumn); } idColumn = c.findColumn(AbstractDb.HDAccountColumns.ENCRYPT_SEED); if (idColumn != -1) { String encryptSeed = c.getString(idColumn); hdEncryptSeedHashMap.put(hdAccountId, encryptSeed); } idColumn = c.findColumn(AbstractDb.HDAccountColumns.ENCRYPT_MNMONIC_SEED); if (idColumn != -1) { String encryptHDSeed = c.getString(idColumn); hdEncryptMnemonicSeedHashMap.put(hdAccountId, encryptHDSeed); } } c.close(); statement.close(); sql = "select password_seed from password_seed limit 1"; statement = this.mDb.getPreparedStatement(sql, null); c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.PasswordSeedColumns.PASSWORD_SEED); if (idColumn != -1) { passwordSeed = new PasswordSeed(c.getString(idColumn)); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); return false; } for (Map.Entry<String, String> kv : addressesPrivKeyHashMap.entrySet()) { kv.setValue(EncryptedData.changePwdKeepFlag(kv.getValue(), oldPassword, newPassword)); } if (hdmEncryptPassword != null) { hdmEncryptPassword = EncryptedData.changePwd(hdmEncryptPassword, oldPassword, newPassword); } for (Map.Entry<Integer, String> kv : encryptMnemonicSeedHashMap.entrySet()) { kv.setValue(EncryptedData.changePwd(kv.getValue(), oldPassword, newPassword)); } for (Map.Entry<Integer, String> kv : encryptHDSeedHashMap.entrySet()) { kv.setValue(EncryptedData.changePwd(kv.getValue(), oldPassword, newPassword)); } for (Map.Entry<Integer, String> kv : singularModeBackupHashMap.entrySet()) { kv.setValue(EncryptedData.changePwd(kv.getValue(), oldPassword, newPassword)); } for (Map.Entry<Integer, String> kv : hdEncryptSeedHashMap.entrySet()) { kv.setValue(EncryptedData.changePwd(kv.getValue(), oldPassword, newPassword)); } for (Map.Entry<Integer, String> kv : hdEncryptMnemonicSeedHashMap.entrySet()) { kv.setValue(EncryptedData.changePwd(kv.getValue(), oldPassword, newPassword)); } if (passwordSeed != null) { boolean result = passwordSeed.changePassword(oldPassword, newPassword); if (!result) { return false; } } final String finalHdmEncryptPassword = hdmEncryptPassword; final PasswordSeed finalPasswordSeed = passwordSeed; try { this.mDb.getConn().setAutoCommit(false); String sql = "update addresses set encrypt_private_key=? where address=? "; for (Map.Entry<String, String> kv : addressesPrivKeyHashMap.entrySet()) { PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, kv.getValue()); stmt.setString(2, kv.getKey()); stmt.executeUpdate(); stmt.close(); } sql = "update hdm_bid set encrypt_bither_password=? "; if (finalHdmEncryptPassword != null) { PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, finalHdmEncryptPassword); stmt.executeUpdate(); stmt.close(); } sql = "update hd_seeds set encrypt_seed=? %s %s where hd_seed_id=? "; for (Map.Entry<Integer, String> kv : encryptMnemonicSeedHashMap.entrySet()) { String singularModeBackupStr = ""; if (singularModeBackupHashMap.containsKey(kv.getKey())) { singularModeBackupStr = ",singular_mode_backup='" + singularModeBackupHashMap.get(kv.getKey()) + "'"; } if (encryptHDSeedHashMap.containsKey(kv.getKey())) { sql = Utils.format(sql, ",encrypt_hd_seed='" + encryptHDSeedHashMap.get(kv.getKey()) + "'", singularModeBackupStr); } PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, kv.getValue()); stmt.setString(2, kv.getKey().toString()); stmt.executeUpdate(); stmt.close(); } sql = "update hd_account set encrypt_mnemonic_seed=?,encrypt_seed=? where hd_account_id=? "; for (Map.Entry<Integer, String> kv : hdEncryptMnemonicSeedHashMap.entrySet()) { PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, kv.getValue()); stmt.setString(2, hdEncryptSeedHashMap.get(kv.getKey())); stmt.setString(3, kv.getKey().toString()); stmt.executeUpdate(); stmt.close(); } if (finalPasswordSeed != null) { sql = "update password_seed set password_seed=? "; PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, finalPasswordSeed.toPasswordSeedString()); stmt.executeUpdate(); stmt.close(); } this.mDb.getConn().commit(); } catch (SQLException e) { e.printStackTrace(); return false; } return true; } @Override public PasswordSeed getPasswordSeed() { PasswordSeed passwordSeed = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select password_seed from password_seed limit 1", null); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.PasswordSeedColumns.PASSWORD_SEED); if (idColumn != -1) { passwordSeed = new PasswordSeed(c.getString(idColumn)); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return passwordSeed; } @Override public boolean hasPasswordSeed() { boolean result = false; try { result = hasPasswordSeed(this.mDb.getConn()); } catch (SQLException e) { e.printStackTrace(); } return result; } private boolean hasPasswordSeed(Connection conn) throws SQLException { PreparedStatement stmt = conn.prepareStatement("select count(0) cnt from password_seed where password_seed is not null "); ResultSet c = stmt.executeQuery(); int count = 0; try { if (c.next()) { int idColumn = c.findColumn("cnt"); if (idColumn != -1) { count = c.getInt(idColumn); } } c.close(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } return count > 0; } @Override public List<Integer> getHDSeeds() { List<Integer> hdSeedIds = new ArrayList<Integer>(); try { String sql = "select hd_seed_id from hd_seeds"; PreparedStatement statement = this.mDb.getPreparedStatement(sql, null); ResultSet c = statement.executeQuery(); while (c.next()) { int idColumn = c.findColumn(AbstractDb.HDSeedsColumns.HD_SEED_ID); if (idColumn != 0) { hdSeedIds.add(c.getInt(idColumn)); } } c.close(); statement.close(); } catch (Exception ex) { ex.printStackTrace(); } finally { } return hdSeedIds; } @Override public String getEncryptMnemonicSeed(int hdSeedId) { String encryptSeed = null; try { String sql = "select encrypt_seed from hd_seeds where hd_seed_id=?"; PreparedStatement statement = this.mDb.getPreparedStatement(sql, new String[]{Integer.toString(hdSeedId)}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDSeedsColumns.ENCRYPT_MNEMONIC_SEED); if (idColumn != -1) { encryptSeed = c.getString(idColumn); } } c.close(); statement.close(); } catch (Exception ex) { ex.printStackTrace(); } return encryptSeed; } @Override public String getEncryptHDSeed(int hdSeedId) { String encryptHDSeed = null; try { String sql = "select encrypt_hd_seed from hd_seeds where hd_seed_id=?"; PreparedStatement statement = this.mDb.getPreparedStatement(sql, new String[]{Integer.toString(hdSeedId)}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDSeedsColumns.ENCRYPT_HD_SEED); if (idColumn != -1) { encryptHDSeed = c.getString(idColumn); } } c.close(); statement.close(); } catch (SQLException ex) { ex.printStackTrace(); } return encryptHDSeed; } @Override public void updateEncrypttMnmonicSeed(int hdSeedId, String encryptMnmonicSeed) { this.mDb.executeUpdate("update hd_seeds set encrypt_seed=? where hd_seed_id=?", new String[]{encryptMnmonicSeed, Integer.toString(hdSeedId)}); } @Override public boolean isHDSeedFromXRandom(int hdSeedId) { boolean isXRandom = false; try { PreparedStatement statement = this.mDb.getPreparedStatement("select is_xrandom from hd_seeds where hd_seed_id=?" , new String[]{Integer.toString(hdSeedId)}); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDSeedsColumns.IS_XRANDOM); if (idColumn != -1) { isXRandom = cursor.getInt(idColumn) == 1; } } cursor.close(); statement.close(); } catch (SQLException ex) { ex.printStackTrace(); } return isXRandom; } @Override public String getHDMFristAddress(int hdSeedId) { String address = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select hdm_address from hd_seeds where hd_seed_id=?" , new String[]{Integer.toString(hdSeedId)}); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDSeedsColumns.HDM_ADDRESS); if (idColumn != -1) { address = cursor.getString(idColumn); } } cursor.close(); statement.close(); } catch (SQLException ex) { ex.printStackTrace(); } return address; } @Override public int addHDKey(final String encryptedMnemonicSeed, final String encryptHdSeed, final String firstAddress, final boolean isXrandom, final String addressOfPS) { int result = 0; try { this.mDb.getConn().setAutoCommit(false); String[] params = new String[]{encryptedMnemonicSeed, encryptHdSeed, Integer.toString(isXrandom ? 1 : 0), firstAddress}; PreparedStatement stmt = this.mDb.getConn().prepareStatement(insertHDSeedSql); if (params != null) { for (int i = 0; i < params.length; i++) { stmt.setString(i + 1, params[i]); } } stmt.executeUpdate(); stmt.close(); if (!hasPasswordSeed(this.mDb.getConn()) && !Utils.isEmpty(addressOfPS)) { addPasswordSeed(this.mDb.getConn(), new PasswordSeed(addressOfPS, encryptedMnemonicSeed)); } this.mDb.getConn().commit(); PreparedStatement statement = this.mDb.getPreparedStatement("select hd_seed_id from hd_seeds where encrypt_seed=? and encrypt_hd_seed=? and is_xrandom=? and hdm_address=?" , new String[]{encryptedMnemonicSeed, encryptHdSeed, Integer.toString(isXrandom ? 1 : 0), firstAddress}); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDSeedsColumns.HD_SEED_ID); if (idColumn != -1) { result = cursor.getInt(idColumn); } } cursor.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return result; } @Override public HDMBId getHDMBId() { HDMBId hdmbId = null; ResultSet c = null; String address = null; String encryptBitherPassword = null; try { String sql = "select " + AbstractDb.HDMBIdColumns.HDM_BID + "," + AbstractDb.HDMBIdColumns.ENCRYPT_BITHER_PASSWORD + " from " + AbstractDb.Tables.HDM_BID; PreparedStatement statement = this.mDb.getPreparedStatement(sql, null); c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDMBIdColumns.HDM_BID); if (idColumn != -1) { address = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.HDMBIdColumns.ENCRYPT_BITHER_PASSWORD); if (idColumn != -1) { encryptBitherPassword = c.getString(idColumn); } } if (!Utils.isEmpty(address) && !Utils.isEmpty(encryptBitherPassword)) { hdmbId = new HDMBId(address, encryptBitherPassword); } c.close(); statement.close(); } catch (Exception ex) { ex.printStackTrace(); } return hdmbId; } @Override public void addAndUpdateHDMBId(final HDMBId bitherId, final String addressOfPS) { boolean isExist = true; try { String sql = "select count(0) cnt from " + AbstractDb.Tables.HDM_BID; PreparedStatement statement = this.mDb.getPreparedStatement(sql, null); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn("cnt"); isExist = c.getInt(idColumn) > 0; } c.close(); statement.close(); } catch (Exception ex) { ex.printStackTrace(); } if (!isExist) { try { this.mDb.getConn().setAutoCommit(false); String encryptedBitherPasswordString = bitherId.getEncryptedBitherPasswordString(); PreparedStatement stmt = this.mDb.getConn().prepareStatement(insertHDMBidSql); stmt.setString(1, bitherId.getAddress()); stmt.setString(2, encryptedBitherPasswordString); stmt.executeUpdate(); if (!hasPasswordSeed(this.mDb.getConn()) && !Utils.isEmpty(addressOfPS)) { addPasswordSeed(this.mDb.getConn(), new PasswordSeed(addressOfPS, encryptedBitherPasswordString)); } this.mDb.getConn().commit(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } else { try { this.mDb.getConn().setAutoCommit(false); String encryptedBitherPasswordString = bitherId.getEncryptedBitherPasswordString(); PreparedStatement stmt = this.mDb.getConn().prepareStatement(updateHDMBidSql); stmt.setString(1, encryptedBitherPasswordString); stmt.setString(2, bitherId.getAddress()); stmt.executeUpdate(); if (!hasPasswordSeed(this.mDb.getConn()) && !Utils.isEmpty(addressOfPS)) { addPasswordSeed(this.mDb.getConn(), new PasswordSeed(addressOfPS, encryptedBitherPasswordString)); } this.mDb.getConn().commit(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } @Override public List<HDMAddress> getHDMAddressInUse(HDMKeychain keychain) { List<HDMAddress> addresses = new ArrayList<HDMAddress>(); try { ResultSet c = null; String sql = "select hd_seed_index,pub_key_hot,pub_key_cold,pub_key_remote,address,is_synced " + "from hdm_addresses " + "where hd_seed_id=? and address is not null order by hd_seed_index"; PreparedStatement statement = this.mDb.getPreparedStatement(sql, new String[]{Integer.toString(keychain.getHdSeedId())}); c = statement.executeQuery(); while (c.next()) { HDMAddress hdmAddress = applyHDMAddress(c, keychain); if (hdmAddress != null) { addresses.add(hdmAddress); } } c.close(); statement.close(); } catch (Exception ex) { ex.printStackTrace(); } return addresses; } @Override public void prepareHDMAddresses(final int hdSeedId, final List<HDMAddress.Pubs> pubsList) { boolean isExist = false; try { for (HDMAddress.Pubs pubs : pubsList) { String sql = "select count(0) cnt from hdm_addresses where hd_seed_id=? and hd_seed_index=?"; PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, Integer.toString(hdSeedId)); stmt.setString(2, Integer.toString(pubs.index)); ResultSet rs = stmt.executeQuery(); if (rs.next()) { int idColumn = rs.findColumn("cnt"); if (idColumn != -1) { isExist |= rs.getInt(idColumn) > 0; } } rs.close(); stmt.close(); } } catch (Exception ex) { ex.printStackTrace(); isExist = true; } try { if (!isExist) { this.mDb.getConn().setAutoCommit(false); for (int i = 0; i < pubsList.size(); i++) { HDMAddress.Pubs pubs = pubsList.get(i); applyHDMAddressContentValues(this.mDb.getConn(), null, hdSeedId, pubs.index, pubs.hot, pubs.cold, null, false); } this.mDb.getConn().commit(); } } catch (SQLException e) { e.printStackTrace(); } } @Override public List<HDMAddress.Pubs> getUncompletedHDMAddressPubs(int hdSeedId, int count) { List<HDMAddress.Pubs> pubsList = new ArrayList<HDMAddress.Pubs>(); try { PreparedStatement statement = this.mDb.getPreparedStatement("select * from hdm_addresses where hd_seed_id=? and pub_key_remote is null limit ? ", new String[]{ Integer.toString(hdSeedId), Integer.toString(count) }); ResultSet cursor = statement.executeQuery(); try { while (cursor.next()) { HDMAddress.Pubs pubs = applyPubs(cursor); if (pubs != null) { pubsList.add(pubs); } } } catch (AddressFormatException e) { e.printStackTrace(); } cursor.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return pubsList; } @Override public int maxHDMAddressPubIndex(int hdSeedId) { int maxIndex = -1; try { PreparedStatement statement = this.mDb.getPreparedStatement("select ifnull(max(hd_seed_index),-1) hd_seed_index from hdm_addresses where hd_seed_id=? ", new String[]{ Integer.toString(hdSeedId) }); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDMAddressesColumns.HD_SEED_INDEX); if (idColumn != -1) { maxIndex = cursor.getInt(idColumn); } } cursor.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return maxIndex; } @Override public int uncompletedHDMAddressCount(int hdSeedId) { int count = 0; try { PreparedStatement statement = this.mDb.getPreparedStatement("select count(0) cnt from hdm_addresses where hd_seed_id=? and pub_key_remote is null " , new String[]{ Integer.toString(hdSeedId) }); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn("cnt"); if (idColumn != -1) { count = cursor.getInt(idColumn); } } cursor.close(); statement.close(); } catch (Exception e) { e.printStackTrace(); } return count; } @Override public void completeHDMAddresses(final int hdSeedId, final List<HDMAddress> addresses) { try { boolean isExist = true; ResultSet c = null; try { for (HDMAddress address : addresses) { String sql = "select count(0) cnt from hdm_addresses " + "where hd_seed_id=? and hd_seed_index=? and address is null"; PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, Integer.toString(hdSeedId)); stmt.setString(2, Integer.toString(address.getIndex())); c = stmt.executeQuery(); if (c.next()) { int idColumn = c.findColumn("cnt"); if (idColumn != -1) { isExist &= c.getInt(idColumn) > 0; } } c.close(); stmt.close(); } } catch (Exception ex) { ex.printStackTrace(); isExist = false; } finally { if (c != null && !c.isClosed()) c.close(); } if (isExist) { this.mDb.getConn().setAutoCommit(false); for (int i = 0; i < addresses.size(); i++) { HDMAddress address = addresses.get(i); String sql = "update hdm_addresses set pub_key_remote=?,address=? where hd_seed_id=? and hd_seed_index=?"; PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, Base58.encode(address.getPubRemote())); stmt.setString(2, address.getAddress()); stmt.setString(3, Integer.toString(hdSeedId)); stmt.setString(4, Integer.toString(address.getIndex())); stmt.executeUpdate(); stmt.close(); } this.mDb.getConn().commit(); } } catch (SQLException e) { e.printStackTrace(); } } public void setHDMPubsRemote(final int hdSeedId, final int index, final byte[] remote) { try { boolean isExist = true; ResultSet c = null; try { String sql = "select count(0) cnt from hdm_addresses " + "where hd_seed_id=? and hd_seed_index=? and address is null"; PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, Integer.toString(hdSeedId)); c = stmt.executeQuery(); if (c.next()) { int idColumn = c.findColumn("cnt"); if (idColumn != -1) { isExist &= c.getInt(0) > 0; } } c.close(); stmt.close(); } catch (Exception ex) { ex.printStackTrace(); isExist = false; } finally { if (c != null && !c.isClosed()) c.close(); } if (isExist) { String sql = "update hdm_addresses set pub_key_remote=? where hd_seed_id=? and hd_seed_index=?"; PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); stmt.setString(1, Base58.encode(remote)); stmt.setString(2, Integer.toString(hdSeedId)); stmt.setString(3, Integer.toString(index)); stmt.executeUpdate(); stmt.close(); } this.mDb.getConn().commit(); } catch (SQLException e) { e.printStackTrace(); } } private static final String insertHDMAddressSql = "insert into hdm_addresses " + "(hd_seed_id,hd_seed_index,pub_key_hot,pub_key_cold,pub_key_remote,address,is_synced)" + " values (?,?,?,?,?,?,?) "; @Override public void recoverHDMAddresses(final int hdSeedId, final List<HDMAddress> addresses) { try { this.mDb.getConn().setAutoCommit(false); for (int i = 0; i < addresses.size(); i++) { HDMAddress address = addresses.get(i); applyHDMAddressContentValues(this.mDb.getConn(), address.getAddress(), hdSeedId, address.getIndex(), address.getPubHot(), address.getPubCold(), address.getPubRemote(), false); } this.mDb.getConn().commit(); } catch (SQLException e) { e.printStackTrace(); } } @Override public void syncComplete(int hdSeedId, int hdSeedIndex) { this.mDb.executeUpdate("update hdm_addresses set is_synced=? where hd_seed_id=? and hd_seed_index=?" , new String[]{Integer.toString(1), Integer.toString(hdSeedId), Integer.toString(hdSeedIndex)}); } //normal @Override public List<Address> getAddresses() { List<Address> addressList = new ArrayList<Address>(); try { PreparedStatement statement = this.mDb.getPreparedStatement("select address,encrypt_private_key,pub_key,is_xrandom,is_trash,is_synced,sort_time " + "from addresses order by sort_time desc", null); ResultSet c = statement.executeQuery(); while (c.next()) { Address address = null; try { address = applyAddressCursor(c); } catch (AddressFormatException e) { e.printStackTrace(); } if (address != null) { addressList.add(address); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return addressList; } @Override public String getEncryptPrivateKey(String address) { String encryptPrivateKey = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select encrypt_private_key from addresses where address=?", new String[]{address}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.AddressesColumns.ENCRYPT_PRIVATE_KEY); if (idColumn != -1) { encryptPrivateKey = c.getString(idColumn); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return encryptPrivateKey; } @Override public void addAddress(final Address address) { try { this.mDb.getConn().setAutoCommit(false); String[] params = new String[]{address.getAddress(), address.hasPrivKey() ? address.getEncryptPrivKeyOfDb() : null, Base58.encode(address.getPubKey()), Integer.toString(address.isFromXRandom() ? 1 : 0), Integer.toString(address.isSyncComplete() ? 1 : 0), Integer.toString(address.isTrashed() ? 1 : 0), Long.toString(address.getSortTime())}; PreparedStatement stmt = this.mDb.getConn().prepareStatement(insertAddressSql); if (params != null) { for (int i = 0; i < params.length; i++) { stmt.setString(i + 1, params[i]); } } stmt.executeUpdate(); if (address.hasPrivKey()) { if (!hasPasswordSeed(this.mDb.getConn())) { PasswordSeed passwordSeed = new PasswordSeed(address.getAddress(), address.getFullEncryptPrivKeyOfDb()); addPasswordSeed(this.mDb.getConn(), passwordSeed); } } this.mDb.getConn().commit(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } @Override public void updatePrivateKey(String address, String encryptPriv) { this.mDb.executeUpdate("update addresses set encrypt_private_key=? where address=?" , new String[]{encryptPriv, address}); } @Override public void removeWatchOnlyAddress(Address address) { this.mDb.executeUpdate("delete from addresses where address=? and encrypt_private_key is null ", new String[]{address.getAddress()}); } @Override public void trashPrivKeyAddress(Address address) { this.mDb.executeUpdate("update addresses set is_trash=1 where address=?" , new String[]{address.getAddress()}); } @Override public void restorePrivKeyAddress(Address address) { this.mDb.executeUpdate("update addresses set is_trash=0 ,is_synced=0,sort_time=? where address=?" , new String[]{Long.toString(address.getSortTime()), address.getAddress()}); } @Override public void updateSyncComplete(Address address) { this.mDb.executeUpdate("update addresses set is_synced=? where address=?" , new String[]{Integer.toString(address.isSyncComplete() ? 1 : 0), address.getAddress()}); } @Override public String getAlias(String address) { String alias = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select alias from aliases where address=?", new String[]{address}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.AliasColumns.ALIAS); if (idColumn != -1) { alias = c.getString(idColumn); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return alias; } @Override public Map<String, String> getAliases() { Map<String, String> stringMap = new HashMap<String, String>(); try { PreparedStatement statement = this.mDb.getPreparedStatement("select * from aliases", null); ResultSet c = statement.executeQuery(); while (c.next()) { int idColumn = c.findColumn(AbstractDb.AliasColumns.ADDRESS); String address = null; String alias = null; if (idColumn > -1) { address = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.AliasColumns.ALIAS); if (idColumn > -1) { alias = c.getString(idColumn); } stringMap.put(address, alias); } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return stringMap; } @Override public void updateAlias(String address, @Nullable String alias) { if (alias == null) { this.mDb.executeUpdate("delete from aliases where address=?", new String[]{ address }); } else { this.mDb.executeUpdate("insert or replace into aliases(address,alias) values(?,?)", new String[]{address, alias}); } } @Override public int getVanityLen(String address) { int vanityLen = Address.VANITY_LEN_NO_EXSITS; try { PreparedStatement statement = this.mDb.getPreparedStatement("select vanity_len from vanity_address where address=?", new String[]{address}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.VanityAddressColumns.VANITY_LEN); if (idColumn != -1) { vanityLen = c.getInt(idColumn); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return vanityLen; } @Override public Map<String, Integer> getVanitylens() { Map<String, Integer> stringMap = new HashMap<String, Integer>(); try { PreparedStatement statement = this.mDb.getPreparedStatement("select * from vanity_address", null); ResultSet c = statement.executeQuery(); while (c.next()) { int idColumn = c.findColumn(AbstractDb.VanityAddressColumns.ADDRESS); String address = null; int vanityLen = Address.VANITY_LEN_NO_EXSITS; if (idColumn > -1) { address = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.VanityAddressColumns.VANITY_LEN); if (idColumn > -1) { vanityLen = c.getInt(idColumn); } stringMap.put(address, vanityLen); } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return stringMap; } @Override public void updateVaitylen(String address, int vanitylen) { if (vanitylen == Address.VANITY_LEN_NO_EXSITS) { this.mDb.executeUpdate("delete from vanity_address where address=?", new String[]{ address }); } else { this.mDb.executeUpdate("insert or replace into vanity_address(address,vanity_len) values(?,?)", new String[]{address, Integer.toString(vanitylen)}); } } @Override public void setSingularModeBackup(int hdSeedId, String singularModeBackup) { this.mDb.executeUpdate("update hd_seeds set singular_mode_backup=? where hd_seed_id=?", new String[]{singularModeBackup, Integer.toString(hdSeedId)}); } @Override public String getSingularModeBackup(int hdSeedId) { String singularModeBackup = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select singular_mode_backup from hd_seeds where hd_seed_id=?" , new String[]{Integer.toString(hdSeedId)}); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDSeedsColumns.SINGULAR_MODE_BACKUP); if (idColumn > 1) { singularModeBackup = cursor.getString(idColumn); } } cursor.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return singularModeBackup; } @Override public int addHDAccount(String encryptedMnemonicSeed, String encryptSeed, String firstAddress, boolean isXrandom, String addressOfPS, byte[] externalPub, byte[] internalPub) { int result = 0; try { String[] params = new String[]{encryptedMnemonicSeed, encryptSeed, firstAddress, Base58.encode(externalPub) , Base58.encode(internalPub), Integer.toString(isXrandom ? 1 : 0),}; String sql = "insert into hd_account(encrypt_mnemonic_seed,encrypt_seed" + ",hd_address,external_pub,internal_pub,is_xrandom) " + " values(?,?,?,?,?,?)"; this.mDb.getConn().setAutoCommit(false); PreparedStatement stmt = this.mDb.getConn().prepareStatement(sql); if (params != null) { for (int i = 0; i < params.length; i++) { stmt.setString(i + 1, params[i]); } } stmt.executeUpdate(); stmt.close(); if (!hasPasswordSeed(this.mDb.getConn()) && !Utils.isEmpty(addressOfPS)) { addPasswordSeed(this.mDb.getConn(), new PasswordSeed(addressOfPS, encryptedMnemonicSeed)); } this.mDb.getConn().commit(); stmt = this.mDb.getPreparedStatement("select hd_account_id from hd_account where encrypt_mnemonic_seed=? and encrypt_seed=? and is_xrandom=? and hd_address=?" , new String[]{encryptedMnemonicSeed, encryptSeed, Integer.toString(isXrandom ? 1 : 0), firstAddress}); ResultSet cursor = stmt.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDAccountColumns.HD_ACCOUNT_ID); if (idColumn != -1) { result = cursor.getInt(idColumn); } } cursor.close(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } return result; } @Override public String getHDFristAddress(int hdSeedId) { String address = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select hd_address from hd_account where hd_account_id=?" , new String[]{Integer.toString(hdSeedId)}); ResultSet cursor = statement.executeQuery(); if (cursor.next()) { int idColumn = cursor.findColumn(AbstractDb.HDAccountColumns.HD_ADDRESS); if (idColumn != -1) { address = cursor.getString(idColumn); } } cursor.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return address; } @Override public byte[] getExternalPub(int hdSeedId) { byte[] pub = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select external_pub from hd_account where hd_account_id=? " , new String[]{Integer.toString(hdSeedId)}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDAccountColumns.EXTERNAL_PUB); if (idColumn != -1) { String pubStr = c.getString(idColumn); pub = Base58.decode(pubStr); } } c.close(); statement.close(); } catch (AddressFormatException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return pub; } @Override public byte[] getInternalPub(int hdSeedId) { byte[] pub = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select internal_pub from hd_account where hd_account_id=? " , new String[]{Integer.toString(hdSeedId)}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDAccountColumns.INTERNAL_PUB); if (idColumn != -1) { String pubStr = c.getString(idColumn); pub = Base58.decode(pubStr); } } c.close(); statement.close(); } catch (AddressFormatException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return pub; } @Override public String getHDAccountEncryptSeed(int hdSeedId) { String hdAccountEncryptSeed = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select " + AbstractDb.HDAccountColumns.ENCRYPT_SEED + " from hd_account where hd_account_id=? " , new String[]{Integer.toString(hdSeedId)}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDAccountColumns.ENCRYPT_SEED); if (idColumn != -1) { hdAccountEncryptSeed = c.getString(idColumn); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return hdAccountEncryptSeed; } @Override public String getHDAccountEncryptMnmonicSeed(int hdSeedId) { String hdAccountMnmonicEncryptSeed = null; try { PreparedStatement statement = this.mDb.getPreparedStatement("select " + AbstractDb.HDAccountColumns.ENCRYPT_MNMONIC_SEED + " from hd_account where hd_account_id=? " , new String[]{Integer.toString(hdSeedId)}); ResultSet c = statement.executeQuery(); if (c.next()) { int idColumn = c.findColumn(AbstractDb.HDAccountColumns.ENCRYPT_MNMONIC_SEED); if (idColumn != -1) { hdAccountMnmonicEncryptSeed = c.getString(idColumn); } } c.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return hdAccountMnmonicEncryptSeed; } @Override public List<Integer> getHDAccountSeeds() { List<Integer> hdSeedIds = new ArrayList<Integer>(); try { String sql = "select " + AbstractDb.HDAccountColumns.HD_ACCOUNT_ID + " from " + AbstractDb.Tables.HD_ACCOUNT; PreparedStatement statement = this.mDb.getPreparedStatement(sql, null); ResultSet c = statement.executeQuery(); while (c.next()) { int idColumn = c.findColumn(AbstractDb.HDAccountColumns.HD_ACCOUNT_ID); if (idColumn != -1) { hdSeedIds.add(c.getInt(idColumn)); } } c.close(); statement.close(); } catch (Exception ex) { ex.printStackTrace(); } return hdSeedIds; } @Override public boolean hdAccountIsXRandom(int seedId) { boolean result = false; String sql = "select is_xrandom from hd_account where hd_account_id=?"; try { PreparedStatement statement = this.mDb.getPreparedStatement(sql, new String[]{Integer.toString(seedId)}); ResultSet rs = statement.executeQuery(); if (rs.next()) { int idColumn = rs.findColumn(AbstractDb.HDAccountColumns.IS_XRANDOM); if (idColumn != -1) { result = rs.getBoolean(idColumn); } } rs.close(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } return result; } public void addPasswordSeed(Connection conn, PasswordSeed passwordSeed) throws SQLException { PreparedStatement stmt = conn.prepareStatement("insert into password_seed (password_seed) values (?)"); stmt.setString(1, passwordSeed.toPasswordSeedString()); stmt.executeUpdate(); stmt.close(); } private HDMAddress applyHDMAddress(ResultSet c, HDMKeychain keychain) throws AddressFormatException, SQLException { HDMAddress hdmAddress; String address = null; boolean isSynced = false; int idColumn = c.findColumn(AbstractDb.HDMAddressesColumns.ADDRESS); if (idColumn != -1) { address = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.HDMAddressesColumns.IS_SYNCED); if (idColumn != -1) { isSynced = c.getInt(idColumn) == 1; } HDMAddress.Pubs pubs = applyPubs(c); hdmAddress = new HDMAddress(pubs, address, isSynced, keychain); return hdmAddress; } private HDMAddress.Pubs applyPubs(ResultSet c) throws AddressFormatException, SQLException { int hdSeedIndex = 0; byte[] hot = null; byte[] cold = null; byte[] remote = null; int idColumn = c.findColumn(AbstractDb.HDMAddressesColumns.HD_SEED_INDEX); if (idColumn != -1) { hdSeedIndex = c.getInt(idColumn); } idColumn = c.findColumn(AbstractDb.HDMAddressesColumns.PUB_KEY_HOT); if (idColumn != -1) { String str = c.getString(idColumn); if (!Utils.isEmpty(str)) { hot = Base58.decode(str); } } idColumn = c.findColumn(AbstractDb.HDMAddressesColumns.PUB_KEY_COLD); if (idColumn != -1) { String str = c.getString(idColumn); if (!Utils.isEmpty(str)) { cold = Base58.decode(str); } } idColumn = c.findColumn(AbstractDb.HDMAddressesColumns.PUB_KEY_REMOTE); if (idColumn != -1) { String str = c.getString(idColumn); if (!Utils.isEmpty(str)) { remote = Base58.decode(str); } } HDMAddress.Pubs pubs = new HDMAddress.Pubs(hot, cold, remote, hdSeedIndex); return pubs; } private Address applyAddressCursor(ResultSet c) throws AddressFormatException, SQLException { Address address; int idColumn = c.findColumn(AbstractDb.AddressesColumns.ADDRESS); String addressStr = null; String encryptPrivateKey = null; byte[] pubKey = null; boolean isXRandom = false; boolean isSynced = false; boolean isTrash = false; long sortTime = 0; if (idColumn != -1) { addressStr = c.getString(idColumn); if (!Utils.validBicoinAddress(addressStr)) { return null; } } idColumn = c.findColumn(AbstractDb.AddressesColumns.ENCRYPT_PRIVATE_KEY); if (idColumn != -1) { encryptPrivateKey = c.getString(idColumn); } idColumn = c.findColumn(AbstractDb.AddressesColumns.PUB_KEY); if (idColumn != -1) { pubKey = Base58.decode(c.getString(idColumn)); } idColumn = c.findColumn(AbstractDb.AddressesColumns.IS_XRANDOM); if (idColumn != -1) { isXRandom = c.getInt(idColumn) == 1; } idColumn = c.findColumn(AbstractDb.AddressesColumns.IS_SYNCED); if (idColumn != -1) { isSynced = c.getInt(idColumn) == 1; } idColumn = c.findColumn(AbstractDb.AddressesColumns.IS_TRASH); if (idColumn != -1) { isTrash = c.getInt(idColumn) == 1; } idColumn = c.findColumn(AbstractDb.AddressesColumns.SORT_TIME); if (idColumn != -1) { sortTime = c.getLong(idColumn); } address = new Address(addressStr, pubKey, sortTime, isSynced, isXRandom, isTrash, encryptPrivateKey); return address; } private void applyHDMAddressContentValues(Connection conn, String address, int hdSeedId, int index, byte[] pubKeysHot, byte[] pubKeysCold, byte[] pubKeysRemote, boolean isSynced) throws SQLException { PreparedStatement stmt = conn.prepareStatement(insertHDMAddressSql); stmt.setString(1, Integer.toString(hdSeedId)); stmt.setString(2, Integer.toString(index)); stmt.setString(3, Base58.encode(pubKeysHot)); stmt.setString(4, Base58.encode(pubKeysCold)); stmt.setString(5, pubKeysRemote == null ? null : Base58.encode(pubKeysRemote)); stmt.setString(6, Utils.isEmpty(address) ? null : address); stmt.setString(7, Integer.toString(isSynced ? 1 : 0)); stmt.executeUpdate(); stmt.close(); } }