crystal-wallet-android/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java

456 lines
20 KiB
Java
Raw Normal View History

package cy.agorise.crystalwallet.manager;
2017-10-10 02:44:44 +00:00
import android.arch.lifecycle.LiveData;
import android.content.Context;
2017-10-10 02:44:44 +00:00
import com.google.common.primitives.UnsignedLong;
2017-10-24 03:25:09 +00:00
import org.bitcoinj.core.Block;
import org.bitcoinj.core.ECKey;
2017-10-24 03:25:09 +00:00
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
2017-10-24 03:25:09 +00:00
import java.util.TimeZone;
2017-10-03 02:49:17 +00:00
import cy.agorise.crystalwallet.apigenerator.ApiRequest;
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
import cy.agorise.crystalwallet.apigenerator.BitsharesFaucetApiGenerator;
2017-10-03 02:49:17 +00:00
import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator;
2017-10-02 02:18:56 +00:00
import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequest;
import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestsListener;
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest;
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateExistBitsharesAccountRequest;
2017-10-02 02:18:56 +00:00
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateImportBitsharesAccountRequest;
2017-10-10 02:44:44 +00:00
import cy.agorise.crystalwallet.dao.CrystalDatabase;
2017-10-24 03:25:09 +00:00
import cy.agorise.crystalwallet.dao.TransactionDao;
import cy.agorise.crystalwallet.enums.CryptoNet;
2017-10-10 02:44:44 +00:00
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitsharesAsset;
2017-10-24 03:25:09 +00:00
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;
2017-10-05 03:28:19 +00:00
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount;
import cy.agorise.graphenej.BaseOperation;
2017-10-04 01:31:16 +00:00
import cy.agorise.graphenej.BrainKey;
import cy.agorise.graphenej.PublicKey;
import cy.agorise.graphenej.Transaction;
import cy.agorise.graphenej.UserAccount;
2017-10-04 01:31:16 +00:00
import cy.agorise.graphenej.models.AccountProperties;
2017-10-24 03:25:09 +00:00
import cy.agorise.graphenej.models.BlockHeader;
import cy.agorise.graphenej.models.HistoricalTransfer;
import cy.agorise.graphenej.objects.Memo;
import cy.agorise.graphenej.operations.TransferOperationBuilder;
/**
* Created by henry on 26/9/2017.
*/
2017-10-02 02:18:56 +00:00
public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
@Override
public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account, Context context) {
if(account instanceof GrapheneAccount) {
GrapheneAccount grapheneAccount = (GrapheneAccount) account;
BitsharesFaucetApiGenerator.registerBitsharesAccount(grapheneAccount.getName(),
new Address(grapheneAccount.getOwnerKey(),"BTS").toString(),
new Address(grapheneAccount.getActiveKey(),"BTS").toString(),
new Address(grapheneAccount.getMemoKey(),"BTS").toString());
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
this.refreshAccountTransactions(account.getId(), context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
return grapheneAccount;
}
return null;
}
@Override
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account, Context context) {
if(account instanceof GrapheneAccount) {
GrapheneAccount grapheneAccount = (GrapheneAccount) account;
if(grapheneAccount.getAccountId() == null){
2017-10-24 03:25:09 +00:00
System.out.println("Importing without accountid");
GrapheneAccount fetch = this.getAccountInfoByName(grapheneAccount.getName());
if(fetch == null) {
//TODO grapaheneAccount null, error fetching
return null;
}
grapheneAccount.setAccountId(fetch.getAccountId());
}else if(grapheneAccount.getName() == null){
2017-10-24 03:25:09 +00:00
System.out.println("Importing without accountname");
GrapheneAccount fetch = this.getAccountInfoById(grapheneAccount.getAccountId());
if(fetch == null) {
//TODO grapaheneAccount null, error fetching
return null;
}
grapheneAccount.setName(fetch.getName());
}
2017-10-24 03:25:09 +00:00
System.out.println("Importing not null " + ((GrapheneAccount) account).getName() + " " + ((GrapheneAccount) account).getAccountId());
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
this.refreshAccountTransactions(account.getId(), context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
return grapheneAccount;
}
return null;
}
@Override
public void loadAccountFromDB(CryptoNetAccount account, Context context) {
System.out.println("On loadAccount from db");
if(account instanceof GrapheneAccount){
GrapheneAccount grapheneAccount = (GrapheneAccount) account;
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
GrapheneAccountInfo info = db.grapheneAccountInfoDao().getByAccountId(account.getId());
grapheneAccount.loadInfo(info);
if(grapheneAccount.getAccountId() == null){
System.out.println("On loadAccount null id");
GrapheneAccount fetch = this.getAccountInfoByName(grapheneAccount.getName());
if(fetch != null){
info.setAccountId(fetch.getAccountId());
grapheneAccount.setAccountId(fetch.getAccountId());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
}
}else if(grapheneAccount.getName() == null){
System.out.println("On loadAccount null name");
GrapheneAccount fetch = this.getAccountInfoById(grapheneAccount.getAccountId());
if(fetch != null) {
info.setName(fetch.getName());
grapheneAccount.setName(fetch.getName());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
}
}
if(grapheneAccount == null) {
//TODO grapaheneAccount null, error fetching
return;
}
System.out.println("On loadAccount no nulls " + grapheneAccount.getName() + " " + grapheneAccount.getAccountId());
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
this.refreshAccountTransactions(account.getId(),context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
}
2017-10-02 02:18:56 +00:00
@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);
}
}
private void validateImportAccount(final ValidateImportBitsharesAccountRequest importRequest){
ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
importRequest.setAccountExists(true);
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;
//TODO change the way to compare keys
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
for(PublicKey activeKey : prop.active.getKeyAuthList()){
2017-10-05 03:28:19 +00:00
if((new Address(activeKey.getKey(),"BTS")).toString().equals(bk.getPublicAddress("BTS").toString())){
2017-10-04 01:31:16 +00:00
importRequest.setMnemonicIsCorrect(true);
return;
}
}
importRequest.setMnemonicIsCorrect(false);
2017-10-03 02:49:17 +00:00
}
}
2017-10-03 02:49:17 +00:00
@Override
public void fail(int idPetition) {
//
}
});
GrapheneApiGenerator.getAccountById((String)answer,getAccountInfo);
}
2017-10-03 02:49:17 +00:00
@Override
public void fail(int idPetition) {
//
}
});
GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName);
}
private void validateExistAcccount(final ValidateExistBitsharesAccountRequest validateRequest){
ApiRequest checkAccountName = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
validateRequest.setAccountExists(true);
}
@Override
public void fail(int idPetition) {
//TODO verified
validateRequest.setAccountExists(false);
}
});
GrapheneApiGenerator.getAccountIdByName(validateRequest.getAccountName(),checkAccountName);
}
private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest){
Asset feeAsset = new Asset(sendRequest.getAsset());
UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId());
UserAccount toUserAccount = new UserAccount(sendRequest.getToAccount());
TransferOperationBuilder builder = new TransferOperationBuilder()
.setSource(fromUserAccount)
.setDestination(toUserAccount)
.setTransferAmount(new AssetAmount(UnsignedLong.valueOf(sendRequest.getAmount()), new Asset(sendRequest.getAsset())))
.setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset));
if(sendRequest.getMemo() != null) {
//builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes()));
//TODO memo
}
ArrayList<BaseOperation> operationList = new ArrayList();
operationList.add(builder.build());
ECKey privateKey = sendRequest.getSourceAccount().getActiveKey();
2017-10-10 02:44:44 +00:00
Transaction transaction = new Transaction(privateKey, null, operationList);
ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
sendRequest.setSend(true);
}
@Override
public void fail(int idPetition) {
sendRequest.setSend(false);
}
});
GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest);
2017-10-02 02:18:56 +00:00
}
private GrapheneAccount getAccountInfoById(String grapheneId){
final Object SYNC = new Object();
long timeout = 60000;
AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC);
ApiRequest request = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountById(grapheneId,request);
long cTime = System.currentTimeMillis();
while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
synchronized (SYNC){
try {
SYNC.wait(100);
} catch (InterruptedException e) {}
}
}
return listener.account;
}
private GrapheneAccount getAccountInfoByName(String grapheneName){
final Object SYNC = new Object();
long timeout = 60000;
AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC);
ApiRequest request = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountByName(grapheneName,request);
long cTime = System.currentTimeMillis();
while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
synchronized (SYNC){
try {
SYNC.wait(100);
} catch (InterruptedException e) {}
}
}
return listener.account;
}
public static void refreshAccountTransactions(long idAccount, Context context){
2017-10-24 03:25:09 +00:00
System.out.println("Call refresh Account transactions from " + idAccount);
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
List<CryptoCoinTransaction> 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);
}
}
private static class TransactionRequestListener implements ApiRequestListener{
int start;
int stop;
int limit;
GrapheneAccount account;
CrystalDatabase db;
public 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;
}
@Override
public void success(Object answer, int idPetition) {
List<HistoricalTransfer> transfers = (List<HistoricalTransfer>) answer ;
for(HistoricalTransfer transfer : transfers) {
if (transfer.getOperation() != null){
CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(account.getId());
transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue());
2017-10-24 03:25:09 +00:00
CryptoCurrency currency = db.cryptoCurrencyDao().getByName(transfer.getOperation().getAssetAmount().getAsset().getSymbol());
if (currency == null) {
System.out.println("CryptoCurrency not in database");
//CryptoCurrency not in database
Asset asset = transfer.getOperation().getAssetAmount().getAsset();
2017-10-24 03:25:09 +00:00
BitsharesAsset.Type assetType = BitsharesAsset.Type.UIA;
/*if(asset.getAssetType() == Asset.AssetType.CORE_ASSET){
assetType = BitsharesAsset.Type.CORE;
}else if(asset.getAssetType() == Asset.AssetType.SMART_COIN){
assetType = BitsharesAsset.Type.SMART_COIN;
}else if(asset.getAssetType() == Asset.AssetType.PREDICTION_MARKET){
assetType = BitsharesAsset.Type.PREDICTION_MARKET;
}else if(asset.getAssetType() == Asset.AssetType.UIA){
assetType = BitsharesAsset.Type.UIA;
2017-10-24 03:25:09 +00:00
}*/
currency = new BitsharesAsset(asset.getSymbol(), asset.getPrecision(), asset.getObjectId(), assetType);
long idCryptoCurrency = db.cryptoCurrencyDao().insertCryptoCurrency(currency)[0];
BitsharesAssetInfo info = new BitsharesAssetInfo((BitsharesAsset) currency);
info.setCryptoCurrencyId(idCryptoCurrency);
currency.setId((int)idCryptoCurrency);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
}
transaction.setIdCurrency(currency.getId());
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());
2017-10-24 03:25:09 +00:00
GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao())));
}
}
if(transfers.size()>= limit){
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 class AccountIdOrNameListener implements ApiRequestListener{
Object SYNC;
boolean ready = false;
GrapheneAccount account;
public AccountIdOrNameListener(Object SYNC) {
this.SYNC = SYNC;
}
@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);
}
synchronized (SYNC){
ready = true;
SYNC.notifyAll();
}
}
@Override
public void fail(int idPetition) {
synchronized (SYNC){
ready = true;
SYNC.notifyAll();
}
}
}
2017-10-24 03:25:09 +00:00
private static class GetTransactionDate implements ApiRequestListener{
private CryptoCoinTransaction transaction;
TransactionDao transactionDao;
public GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) {
this.transaction = transaction;
this.transactionDao = transactionDao;
}
@Override
public void success(Object answer, int idPetition) {
if(answer instanceof BlockHeader){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp));
transactionDao.insertTransaction(transaction);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
@Override
public void fail(int idPetition) {
}
}
}