package com.greenaddress.greenapi;
import android.webkit.URLUtil;
import com.blockstream.libwally.Wally;
import com.greenaddress.greenbits.GaService;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.params.RegTestParams;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class PreparedTransaction {
public final Integer mChangePointer;
public final int mSubAccount;
public final Boolean mRequiresTwoFactor;
public List<Output> mPrevOutputs = new ArrayList<>();
public Transaction mDecoded;
public Map<String, Transaction> mPrevoutRawTxs = new HashMap<>();
public final byte[] mTwoOfThreeBackupChaincode;
public final byte[] mTwoOfThreeBackupPubkey;
private static byte[] getBytes(final Map<String, Object> map, final String key) {
return map == null ? null : Wally.hex_to_bytes((String) map.get(key));
}
public PreparedTransaction(final Integer changePointer, final int subAccount, final Transaction decoded, final Map<String, Object> twoOfThree) {
mChangePointer = changePointer;
mSubAccount = subAccount;
mRequiresTwoFactor = false;
mDecoded = decoded;
mTwoOfThreeBackupChaincode = getBytes(twoOfThree, "2of3_backup_chaincode");
mTwoOfThreeBackupPubkey = getBytes(twoOfThree, "2of3_backup_pubkey");
}
public static class PreparedData {
public PreparedData(final Map<?, ?> values,
final Map<String, ?> privateData,
final ArrayList<Map<String, Object>> subAccounts,
final OkHttpClient client)
{
mValues = values;
mPrivateData = privateData;
mSubAccounts = subAccounts;
mClient = client;
}
final Map<?, ?> mValues;
final Map<String, ?> mPrivateData;
final ArrayList<Map<String, Object>> mSubAccounts;
final OkHttpClient mClient;
}
public PreparedTransaction(final PreparedData pte) {
if (pte.mPrivateData == null || pte.mPrivateData.get("subaccount") == null) {
mSubAccount = 0;
mTwoOfThreeBackupChaincode = null;
mTwoOfThreeBackupPubkey = null;
} else {
mSubAccount = (Integer) pte.mPrivateData.get("subaccount");
byte[] chaincode = null, pubkey = null;
if (mSubAccount != 0) {
// Check if the sub-account is 2of3 and if so store its chaincode/public key
for (final Map<String, Object> m : pte.mSubAccounts)
if (m.get("type").equals("2of3") && m.get("pointer").equals(mSubAccount)) {
chaincode = getBytes(m, "2of3_backup_chaincode");
pubkey = getBytes(m, "2of3_backup_pubkey");
break;
}
}
mTwoOfThreeBackupChaincode = chaincode;
mTwoOfThreeBackupPubkey = pubkey;
}
for (final Object obj : (List) pte.mValues.get("prev_outputs"))
mPrevOutputs.add(new Output((Map<?, ?>) obj));
if (pte.mValues.get("change_pointer") != null)
mChangePointer = Integer.parseInt(pte.mValues.get("change_pointer").toString());
else
mChangePointer = null;
mRequiresTwoFactor = (Boolean) pte.mValues.get("requires_2factor");
mDecoded = GaService.buildTransaction((String) pte.mValues.get("tx"));
if (Network.NETWORK == RegTestParams.get()) {
// For REGTEST we fetch the previous outputs inline
// FIXME: Do this for the other environments too after more testing
final Map<String, String> txs;
txs = (Map<String, String>) pte.mValues.get("prevout_rawtxs");
// if txs is null, the caller passed 'skip' to avoid returning previous txs
if (txs != null)
for (final String txHash : txs.keySet())
mPrevoutRawTxs.put(txHash, GaService.buildTransaction(txs.get(txHash)));
return;
}
// Return early if no rawtxs url is given, assumes user asked for 'skip'
try {
if (!URLUtil.isValidUrl((String) pte.mValues.get("prevout_rawtxs")))
return;
} catch (final Exception e) {
return;
}
final Request request = new Request.Builder()
.url((String)pte.mValues.get("prevout_rawtxs"))
.build();
try {
final String jsonStr = pte.mClient.newCall(request).execute().body().string();
final JSONObject prevout_rawtxs = new JSONObject(jsonStr);
final Iterator<?> keys = prevout_rawtxs.keys();
while (keys.hasNext()) {
final String k = (String)keys.next();
mPrevoutRawTxs.put(k, GaService.buildTransaction(prevout_rawtxs.getString(k)));
}
} catch (final IOException | JSONException e) {
throw new RuntimeException(e);
}
}
}