package cy.agorise.crystalwallet.manager; import android.annotation.SuppressLint; import android.content.Context; import com.google.common.primitives.UnsignedLong; import org.bitcoinj.core.ECKey; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.TimeZone; import cy.agorise.crystalwallet.apigenerator.ApiRequest; import cy.agorise.crystalwallet.apigenerator.ApiRequestListener; import cy.agorise.crystalwallet.apigenerator.BitsharesFaucetApiGenerator; import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; import cy.agorise.crystalwallet.apigenerator.grapheneoperation.AccountUpgradeOperationBuilder; import cy.agorise.crystalwallet.application.constant.BitsharesConstant; import cy.agorise.crystalwallet.dao.AccountSeedDao; import cy.agorise.crystalwallet.models.BitsharesAccountNameCache; import cy.agorise.crystalwallet.models.seed.BIP39; import cy.agorise.crystalwallet.requestmanagers.CryptoNetEquivalentRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestsListener; import cy.agorise.crystalwallet.requestmanagers.GetBitsharesAccountNameCacheRequest; import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesLTMUpgradeRequest; import cy.agorise.crystalwallet.requestmanagers.ValidateBitsharesSendRequest; import cy.agorise.crystalwallet.requestmanagers.ValidateCreateBitsharesAccountRequest; import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest; import cy.agorise.crystalwallet.requestmanagers.ValidateImportBitsharesAccountRequest; import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.dao.TransactionDao; import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.enums.SeedType; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.BitsharesAsset; import cy.agorise.crystalwallet.models.BitsharesAssetInfo; import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCurrency; import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.models.GrapheneAccount; import cy.agorise.crystalwallet.models.GrapheneAccountInfo; import cy.agorise.crystalwallet.network.CryptoNetManager; import cy.agorise.graphenej.Address; import cy.agorise.graphenej.Asset; import cy.agorise.graphenej.AssetAmount; import cy.agorise.graphenej.BaseOperation; import cy.agorise.graphenej.BrainKey; import cy.agorise.graphenej.PublicKey; import cy.agorise.graphenej.Transaction; import cy.agorise.graphenej.UserAccount; import cy.agorise.graphenej.models.AccountProperties; import cy.agorise.graphenej.models.BlockHeader; import cy.agorise.graphenej.models.HistoricalTransfer; import cy.agorise.graphenej.operations.TransferOperationBuilder; /** * The manager for the Bitshare CryptoCoin * * Created by henry on 26/9/2017. */ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { //private final static String BITSHARES_TESTNET_CHAIN_ID= "9cf6f255a208100d2bb275a3c52f4b1589b7ec9c9bfc2cb2a5fe6411295106d8"; private final static String SIMPLE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; private final static String DEFAULT_TIME_ZONE = "GMT"; @Override public void createAccountFromSeed(CryptoNetAccount account, final ManagerRequest request, final Context context) { if(account instanceof GrapheneAccount) { final GrapheneAccount grapheneAccount = (GrapheneAccount) account; ApiRequest creationRequest = new ApiRequest(1, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount fetch = (GrapheneAccount) answer; fetch.setSeedId(grapheneAccount.getSeedId()); fetch.setCryptoNet(grapheneAccount.getCryptoNet()); fetch.setAccountIndex(grapheneAccount.getAccountIndex()); CrystalDatabase db = CrystalDatabase.getAppDatabase(context); long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(fetch)[0]; fetch.setId(idAccount); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(fetch)); subscribeBitsharesAccount(fetch.getId(),fetch.getAccountId(),context); request.success(fetch); } @Override public void fail() { request.fail(); } }); } @Override public void fail(int idPetition) { request.fail(); } }); BitsharesFaucetApiGenerator.registerBitsharesAccount(grapheneAccount.getName(), new Address(ECKey.fromPublicOnly(grapheneAccount.getOwnerKey(context).getPubKey())).toString(), new Address(ECKey.fromPublicOnly(grapheneAccount.getActiveKey(context).getPubKey())).toString(), new Address(ECKey.fromPublicOnly(grapheneAccount.getMemoKey(context).getPubKey())).toString(), BitsharesConstant.FAUCET_URL, creationRequest); } } @Override public void importAccountFromSeed(CryptoNetAccount account, final Context context) { if(account instanceof GrapheneAccount) { final GrapheneAccount grapheneAccount = (GrapheneAccount) account; if(grapheneAccount.getAccountId() == null){ this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount fetch = (GrapheneAccount) answer; grapheneAccount.setAccountId(fetch.getAccountId()); CrystalDatabase db = CrystalDatabase.getAppDatabase(context); long[] idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); grapheneAccount.setId(idAccount[0]); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); } @Override public void fail() { //TODO get account data fail } }); }else if(grapheneAccount.getName() == null){ this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount fetch = (GrapheneAccount) answer; grapheneAccount.setName(fetch.getName()); CrystalDatabase db = CrystalDatabase.getAppDatabase(context); db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); } @Override public void fail() { //TODO get account data fail } }); }else { CrystalDatabase db = CrystalDatabase.getAppDatabase(context); db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context); } } } @Override public void loadAccountFromDB(CryptoNetAccount account, final Context context) { if(account instanceof GrapheneAccount){ final GrapheneAccount grapheneAccount = (GrapheneAccount) account; final CrystalDatabase db = CrystalDatabase.getAppDatabase(context); final GrapheneAccountInfo info = db.grapheneAccountInfoDao().getByAccountId(account.getId()); grapheneAccount.loadInfo(info); if(grapheneAccount.getAccountId() == null){ this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount fetch = (GrapheneAccount) answer; info.setAccountId(fetch.getAccountId()); grapheneAccount.setAccountId(fetch.getAccountId()); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info); subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); } @Override public void fail() { //TODO account data retrieve failed } }); }else if(grapheneAccount.getName() == null){ this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount fetch = (GrapheneAccount) answer; info.setName(fetch.getName()); grapheneAccount.setName(fetch.getName()); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info); subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); } @Override public void fail() { //TODO account data retrieve failed } }); }else{ subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context); } } } private void subscribeBitsharesAccount(long accountId, String accountBitsharesID, Context context){ GrapheneApiGenerator.subscribeBitsharesAccount(accountId,accountBitsharesID,context); BitsharesAccountManager.refreshAccountTransactions(accountId,context); GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesID,context); } /** * Process the bitshares manager request * @param request The request Object */ @Override public void onNewRequest(CryptoNetInfoRequest request) { if (request instanceof ValidateImportBitsharesAccountRequest){ this.validateImportAccount((ValidateImportBitsharesAccountRequest) request); } else if (request instanceof ValidateExistBitsharesAccountRequest){ this.validateExistAcccount((ValidateExistBitsharesAccountRequest) request); } else if (request instanceof ValidateBitsharesSendRequest){ this.validateSendRequest((ValidateBitsharesSendRequest) request); }else if (request instanceof CryptoNetEquivalentRequest){ this.getEquivalentValue((CryptoNetEquivalentRequest) request); }else if (request instanceof ValidateCreateBitsharesAccountRequest){ this.validateCreateAccount((ValidateCreateBitsharesAccountRequest) request); }else if (request instanceof ValidateBitsharesLTMUpgradeRequest){ this.validateLTMAccountUpgrade((ValidateBitsharesLTMUpgradeRequest) request); }else if (request instanceof GetBitsharesAccountNameCacheRequest){ this.getBitsharesAccountNameCacheRequest((GetBitsharesAccountNameCacheRequest) request); }else{ //TODO not implemented System.out.println("Error request not implemented " + request.getClass().getName()); } } /** * Process the import account request */ private void validateImportAccount(final ValidateImportBitsharesAccountRequest importRequest){ //TODO check internet and server status final CrystalDatabase db = CrystalDatabase.getAppDatabase(importRequest.getContext()); final AccountSeedDao accountSeedDao = db.accountSeedDao(); ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { ApiRequest getAccountInfo = new ApiRequest(1,new ApiRequestListener(){ @Override public void success(Object answer, int idPetition) { if(answer != null && answer instanceof AccountProperties) { AccountProperties prop = (AccountProperties) answer; BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0); for(PublicKey activeKey : prop.owner.getKeyAuthList()){ if((new Address(activeKey.getKey(),"BTS")).toString().equals(bk.getPublicAddress("BTS").toString())){ importRequest.setSeedType(SeedType.BRAINKEY); importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED); break; } } BIP39 bip39 = new BIP39(-1, importRequest.getMnemonic()); for(PublicKey activeKey : prop.active.getKeyAuthList()){ if((new Address(activeKey.getKey(),"BTS")).toString().equals(new Address(ECKey.fromPublicOnly(bip39.getBitsharesActiveKey(0).getPubKey())).toString())){ importRequest.setSeedType(SeedType.BIP39); importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED); break; } } if ((importRequest.getStatus() == ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED)){ if (importRequest.addAccountIfValid()) { AccountSeed seed = new AccountSeed(); seed.setName(importRequest.getAccountName()); seed.setType(importRequest.getSeedType()); seed.setMasterSeed(importRequest.getMnemonic()); long idSeed = accountSeedDao.insertAccountSeed(seed); if (idSeed >= 0) { GrapheneAccount account = new GrapheneAccount(); account.setCryptoNet(CryptoNet.BITSHARES); account.setAccountIndex(0); account.setSeedId(idSeed); account.setName(importRequest.getAccountName()); importAccountFromSeed(account, importRequest.getContext()); } } return; } importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.BAD_SEED); } importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.PETITION_FAILED); } @Override public void fail(int idPetition) { // importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.NO_ACCOUNT_DATA); } }); GrapheneApiGenerator.getAccountById((String)answer,getAccountInfo); } @Override public void fail(int idPetition) { // importRequest.setStatus(ValidateImportBitsharesAccountRequest.StatusCode.ACCOUNT_DOESNT_EXIST); } }); GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName); } private void validateCreateAccount(final ValidateCreateBitsharesAccountRequest createRequest){ //TODO test for internet and server connection // Generate seed or find key Context context = createRequest.getContext(); AccountSeed seed = AccountSeed.getAccountSeed(SeedType.BIP39, context); CrystalDatabase db = CrystalDatabase.getAppDatabase(context); long idSeed = db.accountSeedDao().insertAccountSeed(seed); assert seed != null; seed.setId(idSeed); seed.setName(createRequest.getAccountName()); GrapheneAccount account = new GrapheneAccount(); account.setName(createRequest.getAccountName()); account.setSeedId(idSeed); account.setAccountIndex(0); account.setCryptoNet(CryptoNet.BITSHARES); this.createAccountFromSeed(account, new ManagerRequest() { @Override public void success(Object answer) { createRequest.setAccount((GrapheneAccount) answer); createRequest.setStatus(ValidateCreateBitsharesAccountRequest.StatusCode.SUCCEEDED); } @Override public void fail() { createRequest.setStatus(ValidateCreateBitsharesAccountRequest.StatusCode.ACCOUNT_EXIST); } }, context); } /** * Process the account exist request, it consults the bitshares api for the account name. * * This can be used to know if the name is avaible, or the account to be send fund exists */ private void validateExistAcccount(final ValidateExistBitsharesAccountRequest validateRequest){ ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { if (answer != null) { validateRequest.setAccountExists(true); } else { validateRequest.setAccountExists(false); } } @Override public void fail(int idPetition) { //TODO verified validateRequest.setAccountExists(false); } }); GrapheneApiGenerator.getAccountIdByName(validateRequest.getAccountName(),checkAccountName); } /** * Broadcast a transaction request */ private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest) { //TODO check internet, server connection //TODO feeAsset CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext()); CryptoCurrency currency = db.cryptoCurrencyDao().getByNameAndCryptoNet(sendRequest.getAsset(), CryptoNet.BITSHARES.name()); if (currency == null){ getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() { @Override public void success(Object answer) { validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId()); } @Override public void fail() { sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_ASSET_INFO_DB); } }); }else{ BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfo(currency.getId()); if (info == null || info.getBitsharesId() == null || info.getBitsharesId().isEmpty()){ getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() { @Override public void success(Object answer) { validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId()); } @Override public void fail() { sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_ASSET_INFO); } }); }else { this.validateSendRequest(sendRequest, info.getBitsharesId()); } } } /** * Broadcast a send asset request, the idAsset is already fetched * @param sendRequest The petition for transfer * @param idAsset The Bitshares Asset's id */ private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest , final String idAsset){ final Asset feeAsset = new Asset(idAsset); final UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId()); final CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext()); BitsharesAccountNameCache cacheAccount = db.bitsharesAccountNameCacheDao().getByAccountName(sendRequest.getToAccount()); if(cacheAccount == null) { this.getAccountInfoByName(sendRequest.getToAccount(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount toUserGrapheneAccount = (GrapheneAccount) answer; UserAccount toUserAccount = new UserAccount(toUserGrapheneAccount.getAccountId()); try { BitsharesAccountNameCache cacheAccount = new BitsharesAccountNameCache(); cacheAccount.setName(sendRequest.getToAccount()); cacheAccount.setAccountId(toUserAccount.getObjectId()); db.bitsharesAccountNameCacheDao().insertBitsharesAccountNameCache(cacheAccount); }catch(Exception e){ e.printStackTrace(); } validateSendRequest(sendRequest,fromUserAccount,toUserAccount,feeAsset,idAsset); } @Override public void fail() { sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.NO_TO_USER_INFO); } }); }else { UserAccount toUserAccount = new UserAccount(cacheAccount.getAccountId()); this.validateSendRequest(sendRequest,fromUserAccount,toUserAccount,feeAsset,idAsset); } } /** * Broadcast a transaction request * @param sendRequest The petition for transfer operation * @param fromUserAccount The source account * @param toUserAccount The receiver account * @param feeAsset The Fee Asset * @param idAsset The id of the asset to be used on the operation */ private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest, UserAccount fromUserAccount, UserAccount toUserAccount, Asset feeAsset, String idAsset){ TransferOperationBuilder builder = new TransferOperationBuilder() .setSource(fromUserAccount) .setDestination(toUserAccount) .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(sendRequest.getAmount()), new Asset(idAsset))) .setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset)); if (sendRequest.getMemo() != null) { //builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes())); //TODO memo System.out.println("transaction has memo"); } ArrayList operationList = new ArrayList<>(); operationList.add(builder.build()); ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext()); Transaction transaction = new Transaction(privateKey, null, operationList); transaction.setChainId(CryptoNetManager.getChaindId(CryptoNet.BITSHARES)); ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.SUCCEEDED); } @Override public void fail(int idPetition) { sendRequest.setStatus(ValidateBitsharesSendRequest.StatusCode.PETITION_FAILED); } }); GrapheneApiGenerator.broadcastTransaction(transaction, feeAsset, transactionRequest); } /** * Broadcast a transaction request */ private void validateLTMAccountUpgrade(final ValidateBitsharesLTMUpgradeRequest sendRequest) { //TODO check internet, server connection Asset feeAsset = new Asset(sendRequest.getIdAsset()); UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId()); CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext()); AccountUpgradeOperationBuilder builder = new AccountUpgradeOperationBuilder() .setAccountToUpgrade(fromUserAccount) .setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset)); ArrayList operationList = new ArrayList<>(); operationList.add(builder.build()); ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext()); Transaction transaction = new Transaction(privateKey, null, operationList); transaction.setChainId(CryptoNetManager.getChaindId(CryptoNet.BITSHARES)); ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { sendRequest.setStatus(ValidateBitsharesLTMUpgradeRequest.StatusCode.SUCCEEDED); } @Override public void fail(int idPetition) { sendRequest.setStatus(ValidateBitsharesLTMUpgradeRequest.StatusCode.PETITION_FAILED); } }); GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest); } private void getBitsharesAccountNameCacheRequest(final GetBitsharesAccountNameCacheRequest request){ final CrystalDatabase db = CrystalDatabase.getAppDatabase(request.getContext()); BitsharesAccountNameCache cacheAccount = db.bitsharesAccountNameCacheDao().getByAccountId(request.getAccountId()); if(cacheAccount == null) { this.getAccountInfoById(request.getAccountId(), new ManagerRequest() { @Override public void success(Object answer) { GrapheneAccount userGrapheneAccount = (GrapheneAccount) answer; BitsharesAccountNameCache cacheAccount = new BitsharesAccountNameCache(); cacheAccount.setName(userGrapheneAccount.getName()); cacheAccount.setAccountId(request.getAccountId()); db.bitsharesAccountNameCacheDao().insertBitsharesAccountNameCache(cacheAccount); request.setAccountName(userGrapheneAccount.getName()); } @Override public void fail() { //TODO error } }); }else { request.setAccountName(cacheAccount.getName()); } } /** * Returns the account info from a graphene id * @param grapheneId The graphene id of the account */ private void getAccountInfoById(String grapheneId, ManagerRequest request){ AccountIdOrNameListener listener = new AccountIdOrNameListener(request); ApiRequest accountRequest = new ApiRequest(0, listener); GrapheneApiGenerator.getAccountById(grapheneId,accountRequest); } /** * Gets account info by its name * @param grapheneName The name of the account to retrieve */ private void getAccountInfoByName(String grapheneName, ManagerRequest request){ AccountIdOrNameListener listener = new AccountIdOrNameListener(request); ApiRequest accountRequest = new ApiRequest(0, listener); GrapheneApiGenerator.getAccountByName(grapheneName,accountRequest); } //TODO expand function to be more generic private void getAssetInfoByName(String assetName, ManagerRequest request){ AssetIdOrNameListener nameListener = new AssetIdOrNameListener(request); ApiRequest assetRequest = new ApiRequest(0, nameListener); ArrayList assetNames = new ArrayList<>(); assetNames.add(assetName); GrapheneApiGenerator.getAssetByName(assetNames, assetRequest); } /** * Refresh the transactions of an account, important to notice, it return nothing, to get the changes tuse the LiveData * @param idAccount database id of the account * @param context The android context of this application */ private static void refreshAccountTransactions(long idAccount, Context context){ CrystalDatabase db = CrystalDatabase.getAppDatabase(context); List transactions = db.transactionDao().getByIdAccount(idAccount); CryptoNetAccount account = db.cryptoNetAccountDao().getById(idAccount); if(account.getCryptoNet() == CryptoNet.BITSHARES) { GrapheneAccount grapheneAccount = new GrapheneAccount(account); grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(idAccount)); int start = transactions.size(); int limit = 50; int stop = start + limit; ApiRequest transactionRequest = new ApiRequest(0, new TransactionRequestListener(start, stop, limit, grapheneAccount, db)); GrapheneApiGenerator.getAccountTransaction(grapheneAccount.getAccountId(), start, stop, limit, transactionRequest); } } /** * Class that handles the transactions request */ private static class TransactionRequestListener implements ApiRequestListener{ /** * Start index */ int start; /** * End index */ int stop; /** * Limit of transasction to fetch */ int limit; /** * The grapheneaccount with all data CryptoCurrnecy + info */ GrapheneAccount account; /** * The database */ CrystalDatabase db; /** * Basic consturctor */ TransactionRequestListener(int start, int stop, int limit, GrapheneAccount account, CrystalDatabase db) { this.start = start; this.stop = stop; this.limit = limit; this.account = account; this.db = db; } /** * Handles the success request of the transaction, if the amount of transaction is equal to the limit, ask for more transaction * @param answer The answer, this object depends on the kind of request is made to the api * @param idPetition the id of the ApiRequest petition */ @Override public void success(Object answer, int idPetition) { List transfers = (List) answer ; for(final HistoricalTransfer transfer : transfers) { if (transfer.getOperation() != null){ final CryptoCoinTransaction transaction = new CryptoCoinTransaction(); transaction.setAccountId(account.getId()); transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue()); BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId()); if (info == null) { //The cryptoCurrency is not in the database, queringfor its data ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { ArrayList assets = (ArrayList) answer; for(BitsharesAsset asset : assets){ long currencyId = -1; CryptoCurrency cryptoCurrencyDb = db.cryptoCurrencyDao().getByNameAndCryptoNet(asset.getName(),asset.getCryptoNet().name()); if (cryptoCurrencyDb != null){ currencyId = cryptoCurrencyDb.getId(); } else { long idCryptoCurrency = db.cryptoCurrencyDao().insertCryptoCurrency(asset)[0]; currencyId = idCryptoCurrency; } BitsharesAssetInfo info = new BitsharesAssetInfo(asset); info.setCryptoCurrencyId(currencyId); asset.setId((int)currencyId); db.bitsharesAssetDao().insertBitsharesAssetInfo(info); saveTransaction(transaction,info,transfer); } } @Override public void fail(int idPetition) { //TODO Error } }); ArrayList assets = new ArrayList<>(); assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId()); GrapheneApiGenerator.getAssetById(assets,assetRequest); }else{ saveTransaction(transaction,info,transfer); } } } if(transfers.size()>= limit){ // The amount of transaction in the answer is equal to the limit, we need to query to see if there is more transactions int newStart= start + limit; int newStop= stop + limit; ApiRequest transactionRequest = new ApiRequest(newStart/limit, new TransactionRequestListener(newStart,newStop,limit,account,db)); GrapheneApiGenerator.getAccountTransaction(account.getAccountId(),newStart,newStop,limit,transactionRequest); } } @Override public void fail(int idPetition) { } private void saveTransaction(CryptoCoinTransaction transaction, BitsharesAssetInfo info, HistoricalTransfer transfer){ transaction.setIdCurrency((int)info.getCryptoCurrencyId()); transaction.setConfirmed(true); //graphene transaction are always confirmed transaction.setFrom(transfer.getOperation().getFrom().getObjectId()); transaction.setInput(!transfer.getOperation().getFrom().getObjectId().equals(account.getAccountId())); transaction.setTo(transfer.getOperation().getTo().getObjectId()); GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao()))); } } /** * Gets the current change from two assets */ private void getEquivalentValue(final CryptoNetEquivalentRequest request){ if(request.getFromCurrency() instanceof BitsharesAsset && request.getToCurrency() instanceof BitsharesAsset) { BitsharesAsset fromAsset = (BitsharesAsset) request.getFromCurrency(); BitsharesAsset toAsset = (BitsharesAsset) request.getToCurrency(); ApiRequest getEquivalentRequest = new ApiRequest(0, new ApiRequestListener() { @Override public void success(Object answer, int idPetition) { request.setPrice((Long)answer); } @Override public void fail(int idPetition) { //TODO error } }); GrapheneApiGenerator.getEquivalentValue(fromAsset.getBitsharesId(),toAsset.getBitsharesId(), getEquivalentRequest); }else{ //TODO error System.out.println("Equivalent Value error "); } } /** * Class to retrieve the account id or the account name, if one of those is missing */ private class AccountIdOrNameListener implements ApiRequestListener{ final ManagerRequest request; GrapheneAccount account; AccountIdOrNameListener(ManagerRequest request) { this.request = request; } @Override public void success(Object answer, int idPetition) { if(answer instanceof AccountProperties){ AccountProperties props = (AccountProperties) answer; account = new GrapheneAccount(); account.setAccountId(props.id); account.setName(props.name); } request.success(account); } @Override public void fail(int idPetition) { request.fail(); } } /** * Class to retrieve the asset id or the asset name, if one of those is missing */ private class AssetIdOrNameListener implements ApiRequestListener{ final ManagerRequest request; BitsharesAsset asset; AssetIdOrNameListener(ManagerRequest request) { this.request = request; } @Override public void success(Object answer, int idPetition) { if(answer instanceof ArrayList) { if (((ArrayList) answer).get(0) instanceof BitsharesAsset) { asset = (BitsharesAsset) ((ArrayList) answer).get(0); request.success(asset); } } } @Override public void fail(int idPetition) { //TODO fail asset retrieve } } /** * Class to retrieve the transaction date */ public static class GetTransactionDate implements ApiRequestListener{ /** * The transaction to retrieve */ private CryptoCoinTransaction transaction; /** * The DAO to insert or update the transaction */ TransactionDao transactionDao; GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) { this.transaction = transaction; this.transactionDao = transactionDao; } @Override public void success(Object answer, int idPetition) { if(answer instanceof BlockHeader){ @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat(SIMPLE_DATE_FORMAT); dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_TIME_ZONE)); try { transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp)); if (transactionDao.getByTransaction(transaction.getDate(),transaction.getFrom(),transaction.getTo(),transaction.getAmount()) == null) { transactionDao.insertTransaction(transaction); } } catch (ParseException e) { e.printStackTrace(); } } } @Override public void fail(int idPetition) { } } }