Change Insight api classes to handle multiple address and to adapt to the new architecture.

Added Bitshares Account Manager to handle all txi response
TODOs: Add cache data to handle incoming transaction and balances
This commit is contained in:
hvarona 2018-09-27 22:19:19 -04:00
parent 47088391de
commit 7db8212ab4
6 changed files with 191 additions and 156 deletions

View file

@ -13,33 +13,62 @@ import cy.agorise.crystalwallet.network.CryptoNetManager;
public class InsightApiGenerator { public class InsightApiGenerator {
private static HashMap<CryptoNet,BroadcastTransaction> broadcaster = new HashMap();
private static HashMap<CryptoNet,GetTransactionByAddress> transactionGetters = new HashMap(); private static HashMap<CryptoNet,GetTransactionByAddress> transactionGetters = new HashMap();
private static HashMap<CryptoNet,GetTransactionData> transacitonFollowers = new HashMap(); private static HashMap<CryptoNet,GetTransactionData> transacitonFollowers = new HashMap();
/**
* Fecth all the transaciton for a giving address
* @param cryptoNet the crypto net of the address
* @param address The address String
* @param request the request api to response
* @param subscribe If needs to follow the address (Real time)
*/
public static void getTransactionFromAddress(CryptoNet cryptoNet, String address, public static void getTransactionFromAddress(CryptoNet cryptoNet, String address,
ApiRequest request, Context context, ApiRequest request, boolean subscribe){
boolean subscribe){
if(!transactionGetters.containsKey(cryptoNet)){ if(!transactionGetters.containsKey(cryptoNet)){
//TODO change this line transactionGetters.put(cryptoNet,new GetTransactionByAddress(cryptoNet,CryptoNetManager.getURL(cryptoNet)));
transactionGetters.put(cryptoNet,new GetTransactionByAddress(null,CryptoNetManager.getURL(cryptoNet),context));
} }
transactionGetters.get(cryptoNet).addAddress(address);
//TODO process request
} }
/**
* Funciton used for unconfirmed transactions
* @param cryptoNet
* @param txid
* @param context
*/
public static void followTransaction(CryptoNet cryptoNet, String txid, Context context){ public static void followTransaction(CryptoNet cryptoNet, String txid, Context context){
} }
public static void broadcastTransaction(CryptoNet cryptoNet, String rawtx, ApiRequest request){ /**
if(!broadcaster.containsKey(cryptoNet)){ * Broadcast an insight api transaction
//TODO change to multiple broadcast * @param cryptoNet The cryptoNet of the transaction
broadcaster.put(cryptoNet,new BroadcastTransaction(rawtx,null, * @param rawtx the transaction to be broadcasted
CryptoNetManager.getURL(cryptoNet),null)); */
broadcaster.get(cryptoNet).start(); public static void broadcastTransaction(CryptoNet cryptoNet, String rawtx, final ApiRequest request){
} BroadcastTransaction bTransaction = new BroadcastTransaction(rawtx, CryptoNetManager.getURL(cryptoNet), "api", new BroadcastTransaction.BroadCastTransactionListener() {
@Override
public void onSuccess() {
request.getListener().success(true,request.getId());
}
@Override
public void onFailure(String msg) {
request.getListener().fail(request.getId());
}
@Override
public void onConnecitonFailure() {
request.getListener().fail(request.getId());
}
});
} }
/**
* Fetch the estimated fee for a transaction
*/
public static void getEstimateFee(CryptoNet cryptoNet, final ApiRequest request){ public static void getEstimateFee(CryptoNet cryptoNet, final ApiRequest request){
GetEstimateFee.getEstimateFee(CryptoNetManager.getURL(cryptoNet), new GetEstimateFee.estimateFeeListener() { GetEstimateFee.getEstimateFee(CryptoNetManager.getURL(cryptoNet), new GetEstimateFee.estimateFeeListener() {
@Override @Override

View file

@ -22,29 +22,21 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
* The serviceGenerator to call * The serviceGenerator to call
*/ */
private InsightApiServiceGenerator mServiceGenerator; private InsightApiServiceGenerator mServiceGenerator;
/**
* This app context, used to save on the DB
*/
private Context mContext;
/**
* The account who sign the transaction
*/
private GeneralCoinAccount mAccount;
private String serverUrl; private String mPath;
private BroadCastTransactionListener listener;
/** /**
* Basic Consturctor * Basic Consturctor
* @param RawTx The RawTX in Hex String * @param RawTx The RawTX in Hex String
* @param account The account who signs the transaction *
* @param context This app context
*/ */
public BroadcastTransaction(String RawTx, GeneralCoinAccount account, String serverUrl, Context context){ public BroadcastTransaction(String RawTx, String serverUrl, String path, BroadCastTransactionListener listener){
this.serverUrl = serverUrl;
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl); this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
this.mContext = context;
this.mRawTx = RawTx; this.mRawTx = RawTx;
this.mAccount = account; this.listener = listener;
this.mPath = path;
} }
/** /**
@ -54,13 +46,9 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
@Override @Override
public void onResponse(Call<Txi> call, Response<Txi> response) { public void onResponse(Call<Txi> call, Response<Txi> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
//TODO invalidated send listener.onSuccess();
//TODO call getTransactionData
GetTransactionData trData = new GetTransactionData(response.body().txid,this.mAccount, this.serverUrl, this.mContext);
trData.start();
} else { } else {
System.out.println("SENDTEST: not succesful " + response.message()); listener.onFailure(response.message());
//TODO change how to handle invalid transaction
} }
} }
@ -69,8 +57,7 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
*/ */
@Override @Override
public void onFailure(Call<Txi> call, Throwable t) { public void onFailure(Call<Txi> call, Throwable t) {
//TODO change how to handle invalid transaction listener.onConnecitonFailure();
System.out.println("SENDTEST: sendError " + t.getMessage() );
} }
/** /**
@ -79,7 +66,13 @@ public class BroadcastTransaction extends Thread implements Callback<Txi> {
@Override @Override
public void run() { public void run() {
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class); InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<Txi> broadcastTransaction = service.broadcastTransaction(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),this.mRawTx); Call<Txi> broadcastTransaction = service.broadcastTransaction(this.mPath,this.mRawTx);
broadcastTransaction.enqueue(this); broadcastTransaction.enqueue(this);
} }
public interface BroadCastTransactionListener{
void onSuccess();
void onFailure(String msg);
void onConnecitonFailure();
}
} }

View file

@ -3,14 +3,20 @@ package cy.agorise.crystalwallet.apigenerator.insightapi;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import com.idescout.sql.SqlScoutServer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import cy.agorise.crystalwallet.apigenerator.InsightApiGenerator;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.AddressTxi; import cy.agorise.crystalwallet.apigenerator.insightapi.models.AddressTxi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi; import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin; import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout; import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.GTxIO; import cy.agorise.crystalwallet.models.GTxIO;
import cy.agorise.crystalwallet.models.GeneralCoinAccount; import cy.agorise.crystalwallet.models.GeneralCoinAccount;
import cy.agorise.crystalwallet.models.GeneralCoinAddress; import cy.agorise.crystalwallet.models.GeneralCoinAddress;
@ -25,43 +31,34 @@ import retrofit2.Response;
*/ */
public class GetTransactionByAddress extends Thread implements Callback<AddressTxi> { public class GetTransactionByAddress extends Thread implements Callback<AddressTxi> {
/**
* The account to be query
*/
private GeneralCoinAccount mAccount;
/** /**
* The list of address to query * The list of address to query
*/ */
private List<GeneralCoinAddress> mAddresses = new ArrayList<>(); private List<String> mAddresses = new ArrayList<>();
/** /**
* The serviceGenerator to call * The serviceGenerator to call
*/ */
private InsightApiServiceGenerator mServiceGenerator; private InsightApiServiceGenerator mServiceGenerator;
/**
* This app context, used to save on the DB
*/
private Context mContext;
private String serverUrl; private String serverUrl;
private CryptoNet cryptoNet;
private boolean inProcess = false;
/** /**
* Basic consturcotr * Basic consturcotr
* @param account The account to be query
* @param context This app context
*/ */
public GetTransactionByAddress(GeneralCoinAccount account, String serverUrl, Context context) { public GetTransactionByAddress(CryptoNet cryptoNet, String serverUrl) {
this.cryptoNet = cryptoNet;
this.serverUrl = serverUrl; this.serverUrl = serverUrl;
this.mAccount = account;
this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl); this.mServiceGenerator = new InsightApiServiceGenerator(serverUrl);
this.mContext = context;
} }
/** /**
* add an address to be query * add an address to be query
* @param address the address to be query * @param address the address to be query
*/ */
public void addAddress(GeneralCoinAddress address) { public void addAddress(String address) {
this.mAddresses.add(address); this.mAddresses.add(address);
} }
@ -73,110 +70,15 @@ public class GetTransactionByAddress extends Thread implements Callback<AddressT
*/ */
@Override @Override
public void onResponse(Call<AddressTxi> call, Response<AddressTxi> response) { public void onResponse(Call<AddressTxi> call, Response<AddressTxi> response) {
inProcess = false;
if (response.isSuccessful()) { if (response.isSuccessful()) {
boolean changed = false; boolean changed = false;
AddressTxi addressTxi = response.body(); AddressTxi addressTxi = response.body();
for (Txi txi : addressTxi.items) { for (Txi txi : addressTxi.items) {
GeneralCoinAccount tempAccount = null; //TODO call manager
GeneralTransaction transaction = new GeneralTransaction();
transaction.setAccount(this.mAccount);
transaction.setTxid(txi.txid);
transaction.setBlock(txi.blockheight);
transaction.setDate(new Date(txi.time * 1000));
transaction.setFee((long) (txi.fee * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
transaction.setConfirm(txi.confirmations);
transaction.setType(this.mAccount.getCryptoCoin());
transaction.setBlockHeight(txi.blockheight);
for (Vin vin : txi.vin) {
GTxIO input = new GTxIO();
input.setAmount((long) (vin.value * Math.pow(10,this.mAccount.getCryptoCoin().getPrecision())));
input.setTransaction(transaction);
input.setOut(true);
input.setType(this.mAccount.getCryptoCoin());
String addr = vin.addr;
input.setAddressString(addr);
input.setIndex(vin.n);
input.setScriptHex(vin.scriptSig.hex);
input.setOriginalTxid(vin.txid);
for (GeneralCoinAddress address : this.mAddresses) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
input.setAddress(address);
tempAccount = address.getAccount();
if (!address.hasTransactionOutput(input, this.mAccount.getNetworkParam())) {
address.getTransactionOutput().add(input);
}
changed = true;
}
}
transaction.getTxInputs().add(input);
}
for (Vout vout : txi.vout) {
if(vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0){
// The address is null, this must be a memo
String hex = vout.scriptPubKey.hex;
int opReturnIndex = hex.indexOf("6a");
if(opReturnIndex >= 0) {
byte[] memoBytes = new byte[Integer.parseInt(hex.substring(opReturnIndex+2,opReturnIndex+4),16)];
for(int i = 0; i < memoBytes.length;i++){
memoBytes[i] = Byte.parseByte(hex.substring(opReturnIndex+4+(i*2),opReturnIndex+6+(i*2)),16);
}
transaction.setMemo(new String(memoBytes));
}
}else {
GTxIO output = new GTxIO();
output.setAmount((long) (vout.value * Math.pow(10, this.mAccount.getCryptoCoin().getPrecision())));
output.setTransaction(transaction);
output.setOut(false);
output.setType(this.mAccount.getCryptoCoin());
String addr = vout.scriptPubKey.addresses[0];
output.setAddressString(addr);
output.setIndex(vout.n);
output.setScriptHex(vout.scriptPubKey.hex);
for (GeneralCoinAddress address : this.mAddresses) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
output.setAddress(address);
tempAccount = address.getAccount();
if (!address.hasTransactionInput(output, this.mAccount.getNetworkParam())) {
address.getTransactionInput().add(output);
}
changed = true;
}
}
transaction.getTxOutputs().add(output);
}
}
if(txi.txlock && txi.confirmations< this.mAccount.getCryptoNet().getConfirmationsNeeded()){
transaction.setConfirm(this.mAccount.getCryptoNet().getConfirmationsNeeded());
}
//TODO database
/*SCWallDatabase db = new SCWallDatabase(this.mContext);
long idTransaction = db.getGeneralTransactionId(transaction);
if (idTransaction == -1) {
db.putGeneralTransaction(transaction);
} else {
transaction.setId(idTransaction);
db.updateGeneralTransaction(transaction);
}*/
if (tempAccount != null && transaction.getConfirm() < this.mAccount.getCryptoNet().getConfirmationsNeeded()) {
new GetTransactionData(transaction.getTxid(), tempAccount, this.serverUrl, this.mContext, true).start();
}
for (GeneralCoinAddress address : this.mAddresses) {
if (address.updateTransaction(transaction)) {
break;
}
}
} }
if(changed) {
this.mAccount.balanceChange();
}
} }
} }
@ -187,6 +89,7 @@ public class GetTransactionByAddress extends Thread implements Callback<AddressT
*/ */
@Override @Override
public void onFailure(Call<AddressTxi> call, Throwable t) { public void onFailure(Call<AddressTxi> call, Throwable t) {
inProcess = false;
Log.e("GetTransactionByAddress", "Error in json format"); Log.e("GetTransactionByAddress", "Error in json format");
} }
@ -195,14 +98,15 @@ public class GetTransactionByAddress extends Thread implements Callback<AddressT
*/ */
@Override @Override
public void run() { public void run() {
if (this.mAddresses.size() > 0) { if (this.mAddresses.size() > 0 && !inProcess) {
inProcess = true;
StringBuilder addressToQuery = new StringBuilder(); StringBuilder addressToQuery = new StringBuilder();
for (GeneralCoinAddress address : this.mAddresses) { for (String address : this.mAddresses) {
addressToQuery.append(address.getAddressString(this.mAccount.getNetworkParam())).append(","); addressToQuery.append(address).append(",");
} }
addressToQuery.deleteCharAt(addressToQuery.length() - 1); addressToQuery.deleteCharAt(addressToQuery.length() - 1);
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class); InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<AddressTxi> addressTxiCall = service.getTransactionByAddress(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),addressToQuery.toString()); Call<AddressTxi> addressTxiCall = service.getTransactionByAddress(this.serverUrl,addressToQuery.toString());
addressTxiCall.enqueue(this); addressTxiCall.enqueue(this);
} }
} }

View file

@ -84,7 +84,7 @@ public class GetTransactionData extends Thread implements Callback<Txi> {
} }
InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class); InsightApiService service = this.mServiceGenerator.getService(InsightApiService.class);
Call<Txi> txiCall = service.getTransaction(InsightApiConstants.getPath(this.mAccount.getCryptoCoin()),this.mTxId); Call<Txi> txiCall = service.getTransaction(this.mServerUrl,this.mTxId);
txiCall.enqueue(this); txiCall.enqueue(this);
} }

View file

@ -64,8 +64,6 @@ import cy.agorise.graphenej.operations.TransferOperationBuilder;
*/ */
public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { 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 SIMPLE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private final static String DEFAULT_TIME_ZONE = "GMT"; private final static String DEFAULT_TIME_ZONE = "GMT";

View file

@ -12,14 +12,21 @@ import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import cy.agorise.crystalwallet.apigenerator.ApiRequest; import cy.agorise.crystalwallet.apigenerator.ApiRequest;
import cy.agorise.crystalwallet.apigenerator.ApiRequestListener; import cy.agorise.crystalwallet.apigenerator.ApiRequestListener;
import cy.agorise.crystalwallet.apigenerator.InsightApiGenerator; import cy.agorise.crystalwallet.apigenerator.InsightApiGenerator;
import cy.agorise.crystalwallet.apigenerator.insightapi.BroadcastTransaction; import cy.agorise.crystalwallet.apigenerator.insightapi.BroadcastTransaction;
import cy.agorise.crystalwallet.apigenerator.insightapi.GetTransactionData;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Txi;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vin;
import cy.agorise.crystalwallet.apigenerator.insightapi.models.Vout;
import cy.agorise.crystalwallet.enums.CryptoCoin;
import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GTxIO; import cy.agorise.crystalwallet.models.GTxIO;
import cy.agorise.crystalwallet.models.GeneralCoinAccount;
import cy.agorise.crystalwallet.models.GeneralCoinAddress; import cy.agorise.crystalwallet.models.GeneralCoinAddress;
import cy.agorise.crystalwallet.models.GeneralTransaction; import cy.agorise.crystalwallet.models.GeneralTransaction;
import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequest;
@ -29,6 +36,8 @@ import cy.agorise.graphenej.Util;
public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
CryptoCoin cryptoCoin;
@Override @Override
public void createAccountFromSeed(CryptoNetAccount account, ManagerRequest request, Context context) { public void createAccountFromSeed(CryptoNetAccount account, ManagerRequest request, Context context) {
@ -49,6 +58,108 @@ public class GeneralAccountManager implements CryptoAccountManager, CryptoNetInf
} }
/**
* Class that process each transaction fetched by the insight api
* @param txi
*/
public void processTxi(Txi txi){
GeneralCoinAccount tempAccount = null;
GeneralTransaction transaction = new GeneralTransaction();
//transaction.setAccount(this.mAccount);
transaction.setTxid(txi.txid);
transaction.setBlock(txi.blockheight);
transaction.setDate(new Date(txi.time * 1000));
transaction.setFee((long) (txi.fee * Math.pow(10,cryptoCoin.getPrecision())));
transaction.setConfirm(txi.confirmations);
transaction.setType(cryptoCoin);
transaction.setBlockHeight(txi.blockheight);
for (Vin vin : txi.vin) {
GTxIO input = new GTxIO();
input.setAmount((long) (vin.value * Math.pow(10,cryptoCoin.getPrecision())));
input.setTransaction(transaction);
input.setOut(true);
input.setType(cryptoCoin);
String addr = vin.addr;
input.setAddressString(addr);
input.setIndex(vin.n);
input.setScriptHex(vin.scriptSig.hex);
input.setOriginalTxid(vin.txid);
/*for (GeneralCoinAddress address : this.mAddresses) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
input.setAddress(address);
tempAccount = address.getAccount();
if (!address.hasTransactionOutput(input, this.mAccount.getNetworkParam())) {
address.getTransactionOutput().add(input);
}
}
}*/
transaction.getTxInputs().add(input);
}
for (Vout vout : txi.vout) {
if(vout.scriptPubKey.addresses == null || vout.scriptPubKey.addresses.length <= 0){
// The address is null, this must be a memo
String hex = vout.scriptPubKey.hex;
int opReturnIndex = hex.indexOf("6a");
if(opReturnIndex >= 0) {
byte[] memoBytes = new byte[Integer.parseInt(hex.substring(opReturnIndex+2,opReturnIndex+4),16)];
for(int i = 0; i < memoBytes.length;i++){
memoBytes[i] = Byte.parseByte(hex.substring(opReturnIndex+4+(i*2),opReturnIndex+6+(i*2)),16);
}
transaction.setMemo(new String(memoBytes));
}
}else {
GTxIO output = new GTxIO();
output.setAmount((long) (vout.value * Math.pow(10, cryptoCoin.getPrecision())));
output.setTransaction(transaction);
output.setOut(false);
output.setType(cryptoCoin);
String addr = vout.scriptPubKey.addresses[0];
output.setAddressString(addr);
output.setIndex(vout.n);
output.setScriptHex(vout.scriptPubKey.hex);
/*for (GeneralCoinAddress address : this.mAddresses) {
if (address.getAddressString(this.mAccount.getNetworkParam()).equals(addr)) {
output.setAddress(address);
tempAccount = address.getAccount();
if (!address.hasTransactionInput(output, this.mAccount.getNetworkParam())) {
address.getTransactionInput().add(output);
}
changed = true;
}
}*/
transaction.getTxOutputs().add(output);
}
}
if(txi.txlock && txi.confirmations< cryptoCoin.getCryptoNet().getConfirmationsNeeded()){
transaction.setConfirm(cryptoCoin.getCryptoNet().getConfirmationsNeeded());
}
//TODO database
/*SCWallDatabase db = new SCWallDatabase(this.mContext);
long idTransaction = db.getGeneralTransactionId(transaction);
if (idTransaction == -1) {
db.putGeneralTransaction(transaction);
} else {
transaction.setId(idTransaction);
db.updateGeneralTransaction(transaction);
}*/
/*if (tempAccount != null && transaction.getConfirm() < this.mAccount.getCryptoNet().getConfirmationsNeeded()) {
InsightApiGenerator.followTransaction();
new GetTransactionData(transaction.getTxid(), tempAccount, this.serverUrl, this.mContext, true).start();
}
for (GeneralCoinAddress address : this.mAddresses) {
if (address.updateTransaction(transaction)) {
break;
}
}*/
}
public void send(final GeneralAccountSendRequest request){ public void send(final GeneralAccountSendRequest request){
//TODO check server connection //TODO check server connection
//TODO validate to address //TODO validate to address