Search in sources :

Example 6 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class APIFactory method parseMixUnspentOutputs.

private synchronized boolean parseMixUnspentOutputs(String unspents) {
    final int PRE_MIX = 0;
    final int POST_MIX = 1;
    final int BAD_BANK = 2;
    int account_type = 0;
    if (unspents != null) {
        try {
            JSONObject jsonObj = new JSONObject(unspents);
            if (jsonObj == null || !jsonObj.has("unspent_outputs")) {
                return false;
            }
            JSONArray utxoArray = jsonObj.getJSONArray("unspent_outputs");
            if (utxoArray == null || utxoArray.length() == 0) {
                return false;
            }
            for (int i = 0; i < utxoArray.length(); i++) {
                JSONObject outDict = utxoArray.getJSONObject(i);
                byte[] hashBytes = Hex.decode((String) outDict.get("tx_hash"));
                Sha256Hash txHash = Sha256Hash.wrap(hashBytes);
                int txOutputN = ((Number) outDict.get("tx_output_n")).intValue();
                BigInteger value = BigInteger.valueOf(((Number) outDict.get("value")).longValue());
                String script = (String) outDict.get("script");
                byte[] scriptBytes = Hex.decode(script);
                int confirmations = ((Number) outDict.get("confirmations")).intValue();
                String path = null;
                try {
                    String address = Bech32Util.getInstance().getAddressFromScript(script);
                    if (outDict.has("xpub")) {
                        JSONObject xpubObj = (JSONObject) outDict.get("xpub");
                        path = (String) xpubObj.get("path");
                        String m = (String) xpubObj.get("m");
                        unspentPaths.put(address, path);
                        if (m.equals(BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolPostmix()).xpubstr())) {
                            unspentBIP84PostMix.put(address, WhirlpoolMeta.getInstance(context).getWhirlpoolPostmix());
                            account_type = POST_MIX;
                        } else if (m.equals(BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolPremixAccount()).xpubstr())) {
                            unspentBIP84PreMix.put(address, WhirlpoolMeta.getInstance(context).getWhirlpoolPremixAccount());
                            account_type = PRE_MIX;
                        } else if (m.equals(BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolBadBank()).xpubstr())) {
                            unspentBIP84BadBank.put(address, WhirlpoolMeta.getInstance(context).getWhirlpoolBadBank());
                            account_type = BAD_BANK;
                        } else {
                            ;
                        }
                    } else {
                        ;
                    }
                    // Construct the output
                    MyTransactionOutPoint outPoint = new MyTransactionOutPoint(txHash, txOutputN, value, scriptBytes, address);
                    outPoint.setConfirmations(confirmations);
                    if (account_type == POST_MIX) {
                        if (utxosPostMix.containsKey(script)) {
                            utxosPostMix.get(script).getOutpoints().add(outPoint);
                        } else {
                            UTXO utxo = new UTXO();
                            utxo.getOutpoints().add(outPoint);
                            utxo.setPath(path);
                            utxosPostMix.put(script, utxo);
                        }
                        UTXOFactory.getInstance().addPostMix(txHash.toString(), txOutputN, script, utxosPostMix.get(script));
                    } else if (account_type == PRE_MIX) {
                        if (utxosPreMix.containsKey(script)) {
                            utxosPreMix.get(script).getOutpoints().add(outPoint);
                        } else {
                            UTXO utxo = new UTXO();
                            utxo.getOutpoints().add(outPoint);
                            utxo.setPath(path);
                            utxosPreMix.put(script, utxo);
                        }
                        UTXOFactory.getInstance().addPreMix(txHash.toString(), txOutputN, script, utxosPreMix.get(script));
                    }
                    if (account_type == BAD_BANK) {
                        if (utxosBadBank.containsKey(script)) {
                            utxosBadBank.get(script).getOutpoints().add(outPoint);
                        } else {
                            UTXO utxo = new UTXO();
                            utxo.getOutpoints().add(outPoint);
                            utxo.setPath(path);
                            utxosBadBank.put(script, utxo);
                        }
                        UTXOFactory.getInstance().addBadBank(txHash.toString(), txOutputN, script, utxosBadBank.get(script));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    ;
                }
            }
            if (account_type == POST_MIX) {
                PayloadUtil.getInstance(context).serializeUTXOPost(jsonObj);
            } else if (account_type == PRE_MIX) {
                PayloadUtil.getInstance(context).serializeUTXOPre(jsonObj);
            } else {
                PayloadUtil.getInstance(context).serializeUTXOBadBank(jsonObj);
            }
            return true;
        } catch (Exception j) {
            j.printStackTrace();
            ;
        }
    }
    return false;
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) JSONObject(org.json.JSONObject) Sha256Hash(org.bitcoinj.core.Sha256Hash) JSONArray(org.json.JSONArray) BigInteger(java.math.BigInteger) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) NotSecp256k1Exception(com.samourai.wallet.bip47.rpc.NotSecp256k1Exception) JSONException(org.json.JSONException) AddressFormatException(org.bitcoinj.core.AddressFormatException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InvalidKeyException(java.security.InvalidKeyException) InvalidKeySpecException(java.security.spec.InvalidKeySpecException) MnemonicException(org.bitcoinj.crypto.MnemonicException) DecryptionException(com.samourai.wallet.crypto.DecryptionException) IOException(java.io.IOException) NoSuchProviderException(java.security.NoSuchProviderException)

Example 7 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class APIFactory method getUtxosWithLocalCache.

public List<UTXO> getUtxosWithLocalCache(boolean filter, boolean useLocalCache) {
    List<UTXO> unspents = new ArrayList<UTXO>();
    if (utxos.isEmpty() && useLocalCache) {
        try {
            String response = PayloadUtil.getInstance(context).deserializeUTXO().toString();
            parseUnspentOutputs(response);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    if (filter) {
        for (String key : utxos.keySet()) {
            UTXO u = new UTXO();
            for (MyTransactionOutPoint out : utxos.get(key).getOutpoints()) {
                if (!BlockedUTXO.getInstance().contains(out.getTxHash().toString(), out.getTxOutputN())) {
                    u.getOutpoints().add(out);
                }
            }
            if (u.getOutpoints().size() > 0) {
                unspents.add(u);
            }
        }
    } else {
        unspents.addAll(utxos.values());
    }
    return unspents;
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) ArrayList(java.util.ArrayList) JSONException(org.json.JSONException) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) IOException(java.io.IOException)

Example 8 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class APIFactory method initWalletAmounts.

private synchronized void initWalletAmounts() {
    APIFactory.getInstance(context).reset();
    List<String> addressStrings = new ArrayList<String>();
    String[] s = null;
    try {
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUB44REG, false) == false) {
            registerXPUB(HD_WalletFactory.getInstance(context).get().getAccount(0).xpubstr(), 44, null);
        }
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUB49REG, false) == false) {
            registerXPUB(BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr(), 49, null);
        }
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUB84REG, false) == false) {
            registerXPUB(BIP84Util.getInstance(context).getWallet().getAccount(0).xpubstr(), 84, null);
        }
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUBPREREG, false) == false) {
            registerXPUB(BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolPremixAccount()).xpubstr(), 84, PrefsUtil.XPUBPREREG);
        }
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUBPOSTREG, false) == false) {
            registerXPUB(BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolPostmix()).xpubstr(), 84, PrefsUtil.XPUBPOSTREG);
        }
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUBBADBANKREG, false) == false) {
            registerXPUB(BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolBadBank()).xpubstr(), 84, PrefsUtil.XPUBPOSTREG);
        }
        xpub_txs.put(HD_WalletFactory.getInstance(context).get().getAccount(0).xpubstr(), new ArrayList<Tx>());
        addressStrings.addAll(Arrays.asList(BIP47Meta.getInstance().getIncomingAddresses(false)));
        for (String _s : Arrays.asList(BIP47Meta.getInstance().getIncomingLookAhead(context))) {
            if (!addressStrings.contains(_s)) {
                addressStrings.add(_s);
            }
        }
        for (String pcode : BIP47Meta.getInstance().getUnspentProviders()) {
            for (String addr : BIP47Meta.getInstance().getUnspentAddresses(context, pcode)) {
                if (!addressStrings.contains(addr)) {
                    addressStrings.add(addr);
                }
            }
            List<Integer> idxs = BIP47Meta.getInstance().getUnspent(pcode);
            for (Integer idx : idxs) {
                String receivePubKey = BIP47Util.getInstance(context).getReceivePubKey(new PaymentCode(pcode), idx);
                BIP47Meta.getInstance().getIdx4AddrLookup().put(receivePubKey, idx);
                BIP47Meta.getInstance().getPCode4AddrLookup().put(receivePubKey, pcode.toString());
                if (!addressStrings.contains(receivePubKey)) {
                    addressStrings.add(receivePubKey);
                }
            }
        }
        if (addressStrings.size() > 0) {
            s = addressStrings.toArray(new String[0]);
            // info("APIFactory", addressStrings.toString());
            utxoObj0 = getUnspentOutputs(s);
        }
        debug("APIFactory", "addresses:" + addressStrings.toString());
        HD_Wallet hdw = HD_WalletFactory.getInstance(context).get();
        if (hdw != null && hdw.getXPUBs() != null) {
            String[] all = null;
            if (s != null && s.length > 0) {
                all = new String[hdw.getXPUBs().length + 2 + s.length];
                all[0] = BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr();
                all[1] = BIP84Util.getInstance(context).getWallet().getAccount(0).xpubstr();
                System.arraycopy(hdw.getXPUBs(), 0, all, 2, hdw.getXPUBs().length);
                System.arraycopy(s, 0, all, hdw.getXPUBs().length + 2, s.length);
            } else {
                all = new String[hdw.getXPUBs().length + 2];
                all[0] = BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr();
                all[1] = BIP84Util.getInstance(context).getWallet().getAccount(0).xpubstr();
                System.arraycopy(hdw.getXPUBs(), 0, all, 2, hdw.getXPUBs().length);
            }
            APIFactory.getInstance(context).getXPUB(all, true);
            String[] xs = new String[3];
            xs[0] = HD_WalletFactory.getInstance(context).get().getAccount(0).xpubstr();
            xs[1] = BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr();
            xs[2] = BIP84Util.getInstance(context).getWallet().getAccount(0).xpubstr();
            utxoObj1 = getUnspentOutputs(xs);
            getDynamicFees();
        }
        try {
            List<JSONObject> utxoObjs = new ArrayList<JSONObject>();
            if (utxoObj0 != null) {
                utxoObjs.add(utxoObj0);
            }
            if (utxoObj1 != null) {
                utxoObjs.add(utxoObj1);
            }
            PayloadUtil.getInstance(context).serializeUTXO(utxoObjs);
        } catch (IOException | DecryptionException e) {
            ;
        }
        // 
        // 
        // 
        List<String> seenOutputs = new ArrayList<String>();
        List<UTXO> _utxos = getUtxos(false);
        for (UTXO _u : _utxos) {
            for (MyTransactionOutPoint _o : _u.getOutpoints()) {
                seenOutputs.add(_o.getTxHash().toString() + "-" + _o.getTxOutputN());
            }
        }
        for (String _s : BlockedUTXO.getInstance().getNotDustedUTXO()) {
            // debug("APIFactory", "not dusted:" + _s);
            if (!seenOutputs.contains(_s)) {
                BlockedUTXO.getInstance().removeNotDusted(_s);
            // debug("APIFactory", "not dusted removed:" + _s);
            }
        }
        for (String _s : BlockedUTXO.getInstance().getBlockedUTXO().keySet()) {
            // debug("APIFactory", "blocked:" + _s);
            if (!seenOutputs.contains(_s)) {
                BlockedUTXO.getInstance().remove(_s);
            // debug("APIFactory", "blocked removed:" + _s);
            }
        }
        String strPreMix = BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolPremixAccount()).xpubstr();
        String strPostMix = BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolPostmix()).xpubstr();
        String strBadBank = BIP84Util.getInstance(context).getWallet().getAccountAt(WhirlpoolMeta.getInstance(context).getWhirlpoolBadBank()).xpubstr();
        JSONObject preMultiAddrObj = getRawXPUB(new String[] { strPreMix });
        JSONObject preUnspentObj = getRawUnspentOutputs(new String[] { strPreMix });
        debug("APIFactory", "pre-mix multi:" + preMultiAddrObj.toString(2));
        debug("APIFactory", "pre-mix unspent:" + preUnspentObj.toString());
        boolean parsedPreMultiAddr = parseMixXPUB(preMultiAddrObj);
        boolean parsedPreUnspent = parseMixUnspentOutputs(preUnspentObj.toString());
        JSONObject postMultiAddrObj = getRawXPUB(new String[] { strPostMix });
        JSONObject postUnspentObj = getRawUnspentOutputs(new String[] { strPostMix });
        debug("APIFactory", "post-mix multi:" + postMultiAddrObj.toString());
        debug("APIFactory", "post-mix unspent:" + postUnspentObj.toString());
        boolean parsedPostMultiAddr = parseMixXPUB(postMultiAddrObj);
        boolean parsedPostUnspent = parseMixUnspentOutputs(postUnspentObj.toString());
        // debug("APIFactory", "post-mix multi:" + parsedPostMultiAddr);
        // debug("APIFactory", "post-mix unspent:" + parsedPostUnspent);
        // debug("APIFactory", "post-mix multi:" + getXpubPostMixBalance());
        // debug("APIFactory", "post-mix unspent:" + getUtxosPostMix().size());
        JSONObject badbankMultiAddrObj = getRawXPUB(new String[] { strBadBank });
        JSONObject badbankUnspentObj = getRawUnspentOutputs(new String[] { strBadBank });
        debug("APIFactory", "bad bank multi:" + badbankMultiAddrObj.toString());
        debug("APIFactory", "bad bank unspent:" + badbankUnspentObj.toString());
        boolean parsedBadBankMultiAddr = parseMixXPUB(badbankMultiAddrObj);
        boolean parsedBadBanktUnspent = parseMixUnspentOutputs(badbankUnspentObj.toString());
        // 
        // 
        // 
        List<String> seenOutputsPostMix = new ArrayList<String>();
        List<UTXO> _utxosPostMix = getUtxosPostMix(false);
        for (UTXO _u : _utxosPostMix) {
            for (MyTransactionOutPoint _o : _u.getOutpoints()) {
                seenOutputsPostMix.add(_o.getTxHash().toString() + "-" + _o.getTxOutputN());
            }
        }
        for (String _s : UTXOUtil.getInstance().getTags().keySet()) {
            if (!seenOutputsPostMix.contains(_s) && !seenOutputs.contains(_s)) {
                UTXOUtil.getInstance().remove(_s);
                UTXOUtil.getInstance().removeNote(_s);
            }
        }
        List<String> seenOutputsBadBank = new ArrayList<String>();
        List<UTXO> _utxosBadBank = getUtxosBadBank(false);
        for (UTXO _u : _utxosBadBank) {
            for (MyTransactionOutPoint _o : _u.getOutpoints()) {
                seenOutputsBadBank.add(_o.getTxHash().toString() + "-" + _o.getTxOutputN());
            }
        }
        for (String _s : UTXOUtil.getInstance().getTags().keySet()) {
            if (!seenOutputsBadBank.contains(_s) && !seenOutputs.contains(_s)) {
                UTXOUtil.getInstance().remove(_s);
            }
        }
        /*
            for(String _s : BlockedUTXO.getInstance().getNotDustedUTXO())   {
//                debug("APIFactory", "not dusted:" + _s);
                if(!seenOutputsPostMix.contains(_s))    {
                    BlockedUTXO.getInstance().removeNotDusted(_s);
//                    debug("APIFactory", "not dusted removed:" + _s);
                }
            }
            */
        for (String _s : BlockedUTXO.getInstance().getBlockedUTXOPostMix().keySet()) {
            debug("APIFactory", "blocked post-mix:" + _s);
            if (!seenOutputsPostMix.contains(_s)) {
                BlockedUTXO.getInstance().removePostMix(_s);
                debug("APIFactory", "blocked removed:" + _s);
            }
        }
        // refresh Whirlpool utxos
        Optional<WhirlpoolWallet> whirlpoolWalletOpt = AndroidWhirlpoolWalletService.getInstance().getWhirlpoolWallet();
        if (whirlpoolWalletOpt.isPresent()) {
            whirlpoolWalletOpt.get().clearCache(WhirlpoolAccount.DEPOSIT);
            whirlpoolWalletOpt.get().clearCache(WhirlpoolAccount.PREMIX);
            whirlpoolWalletOpt.get().clearCache(WhirlpoolAccount.POSTMIX);
            whirlpoolWalletOpt.get().clearCache(WhirlpoolAccount.BADBANK);
        }
    } catch (IndexOutOfBoundsException ioobe) {
        ioobe.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    walletInit = true;
}
Also used : PaymentCode(com.samourai.wallet.bip47.rpc.PaymentCode) HD_Wallet(com.samourai.wallet.hd.HD_Wallet) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) IOException(java.io.IOException) NotSecp256k1Exception(com.samourai.wallet.bip47.rpc.NotSecp256k1Exception) JSONException(org.json.JSONException) AddressFormatException(org.bitcoinj.core.AddressFormatException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InvalidKeyException(java.security.InvalidKeyException) InvalidKeySpecException(java.security.spec.InvalidKeySpecException) MnemonicException(org.bitcoinj.crypto.MnemonicException) DecryptionException(com.samourai.wallet.crypto.DecryptionException) IOException(java.io.IOException) NoSuchProviderException(java.security.NoSuchProviderException) BigInteger(java.math.BigInteger) UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) JSONObject(org.json.JSONObject) WhirlpoolWallet(com.samourai.whirlpool.client.wallet.WhirlpoolWallet) DecryptionException(com.samourai.wallet.crypto.DecryptionException)

Example 9 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class PayNymDetailsActivity method doNotifTx.

private void doNotifTx() {
    // 
    // get wallet balance
    // 
    long balance = 0L;
    try {
        balance = APIFactory.getInstance(PayNymDetailsActivity.this).getXpubAmounts().get(HD_WalletFactory.getInstance(PayNymDetailsActivity.this).get().getAccount(0).xpubstr());
    } catch (IOException ioe) {
        balance = 0L;
    } catch (MnemonicException.MnemonicLengthException mle) {
        balance = 0L;
    } catch (java.lang.NullPointerException npe) {
        balance = 0L;
    }
    final List<UTXO> selectedUTXO = new ArrayList<UTXO>();
    long totalValueSelected = 0L;
    // long change = 0L;
    BigInteger fee = null;
    // 
    // spend dust threshold amount to notification address
    // 
    long amount = SendNotifTxFactory._bNotifTxValue.longValue();
    // 
    // add Samourai Wallet fee to total amount
    // 
    amount += SendNotifTxFactory._bSWFee.longValue();
    // 
    // get unspents
    // 
    List<UTXO> utxos = null;
    if (UTXOFactory.getInstance().getTotalP2SH_P2WPKH() > amount + FeeUtil.getInstance().estimatedFeeSegwit(0, 1, 4).longValue()) {
        utxos = new ArrayList<UTXO>();
        utxos.addAll(UTXOFactory.getInstance().getAllP2SH_P2WPKH().values());
    } else {
        utxos = APIFactory.getInstance(PayNymDetailsActivity.this).getUtxos(true);
    }
    // sort in ascending order by value
    final List<UTXO> _utxos = utxos;
    Collections.sort(_utxos, new UTXO.UTXOComparator());
    Collections.reverse(_utxos);
    // 
    for (UTXO u : _utxos) {
        if (u.getValue() >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFee(1, 4).longValue())) {
            selectedUTXO.add(u);
            totalValueSelected += u.getValue();
            Log.d("PayNymDetailsActivity", "single output");
            Log.d("PayNymDetailsActivity", "value selected:" + u.getValue());
            Log.d("PayNymDetailsActivity", "total value selected:" + totalValueSelected);
            Log.d("PayNymDetailsActivity", "nb inputs:" + u.getOutpoints().size());
            break;
        }
    }
    // 
    // use normal fee settings
    // 
    SuggestedFee suggestedFee = FeeUtil.getInstance().getSuggestedFee();
    long lo = FeeUtil.getInstance().getLowFee().getDefaultPerKB().longValue() / 1000L;
    long mi = FeeUtil.getInstance().getNormalFee().getDefaultPerKB().longValue() / 1000L;
    long hi = FeeUtil.getInstance().getHighFee().getDefaultPerKB().longValue() / 1000L;
    if (lo == mi && mi == hi) {
        SuggestedFee hi_sf = new SuggestedFee();
        hi_sf.setDefaultPerKB(BigInteger.valueOf((long) (hi * 1.15 * 1000.0)));
        FeeUtil.getInstance().setSuggestedFee(hi_sf);
    } else if (lo == mi) {
        FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getHighFee());
    } else {
        FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getNormalFee());
    }
    if (selectedUTXO.size() == 0) {
        // sort in descending order by value
        Collections.sort(_utxos, new UTXO.UTXOComparator());
        int selected = 0;
        // get largest UTXOs > than spend + fee + dust
        for (UTXO u : _utxos) {
            selectedUTXO.add(u);
            totalValueSelected += u.getValue();
            selected += u.getOutpoints().size();
            if (totalValueSelected >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFee(selected, 4).longValue())) {
                Log.d("PayNymDetailsActivity", "multiple outputs");
                Log.d("PayNymDetailsActivity", "total value selected:" + totalValueSelected);
                Log.d("PayNymDetailsActivity", "nb inputs:" + u.getOutpoints().size());
                break;
            }
        }
        // fee = FeeUtil.getInstance().estimatedFee(selected, 4);
        fee = FeeUtil.getInstance().estimatedFee(selected, 7);
    } else {
        // fee = FeeUtil.getInstance().estimatedFee(1, 4);
        fee = FeeUtil.getInstance().estimatedFee(1, 7);
    }
    // 
    // reset fee to previous setting
    // 
    FeeUtil.getInstance().setSuggestedFee(suggestedFee);
    // 
    if ((amount + fee.longValue()) >= balance) {
        String message = getText(R.string.bip47_notif_tx_insufficient_funds_1) + " ";
        BigInteger biAmount = SendNotifTxFactory._bSWFee.add(SendNotifTxFactory._bNotifTxValue.add(FeeUtil.getInstance().estimatedFee(1, 4, FeeUtil.getInstance().getLowFee().getDefaultPerKB())));
        String strAmount = MonetaryUtil.getInstance().getBTCFormat().format(((double) biAmount.longValue()) / 1e8) + " BTC ";
        message += strAmount;
        message += " " + getText(R.string.bip47_notif_tx_insufficient_funds_2);
        AlertDialog.Builder dlg = new AlertDialog.Builder(PayNymDetailsActivity.this).setTitle(R.string.app_name).setMessage(message).setCancelable(false).setPositiveButton(R.string.help, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int whichButton) {
                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://support.samourai.io/article/58-connecting-to-a-paynym-contact"));
                startActivity(browserIntent);
            }
        }).setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int whichButton) {
                dialog.dismiss();
            }
        });
        if (!isFinishing()) {
            dlg.show();
        }
        return;
    }
    // 
    // payment code to be notified
    // 
    PaymentCode payment_code;
    try {
        payment_code = new PaymentCode(pcode);
    } catch (AddressFormatException afe) {
        payment_code = null;
    }
    if (payment_code == null) {
        return;
    }
    // 
    // create outpoints for spend later
    // 
    final List<MyTransactionOutPoint> outpoints = new ArrayList<MyTransactionOutPoint>();
    for (UTXO u : selectedUTXO) {
        outpoints.addAll(u.getOutpoints());
    }
    // 
    // create inputs from outpoints
    // 
    List<MyTransactionInput> inputs = new ArrayList<MyTransactionInput>();
    for (MyTransactionOutPoint o : outpoints) {
        Script script = new Script(o.getScriptBytes());
        if (script.getScriptType() == Script.ScriptType.NO_TYPE) {
            continue;
        }
        MyTransactionInput input = new MyTransactionInput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, new byte[0], o, o.getTxHash().toString(), o.getTxOutputN());
        inputs.add(input);
    }
    // 
    // sort inputs
    // 
    Collections.sort(inputs, new SendFactory.BIP69InputComparator());
    // 
    // find outpoint that corresponds to 0th input
    // 
    MyTransactionOutPoint outPoint = null;
    for (MyTransactionOutPoint o : outpoints) {
        if (o.getTxHash().toString().equals(inputs.get(0).getTxHash()) && o.getTxOutputN() == inputs.get(0).getTxPos()) {
            outPoint = o;
            break;
        }
    }
    if (outPoint == null) {
        Toast.makeText(PayNymDetailsActivity.this, R.string.bip47_cannot_identify_outpoint, Toast.LENGTH_SHORT).show();
        return;
    }
    byte[] op_return = null;
    // 
    try {
        // Script inputScript = new Script(outPoint.getConnectedPubKeyScript());
        byte[] scriptBytes = outPoint.getConnectedPubKeyScript();
        String address = null;
        if (Bech32Util.getInstance().isBech32Script(Hex.toHexString(scriptBytes))) {
            address = Bech32Util.getInstance().getAddressFromScript(Hex.toHexString(scriptBytes));
        } else {
            address = new Script(scriptBytes).getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
        }
        // String address = inputScript.getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
        ECKey ecKey = SendFactory.getPrivKey(address, 0);
        if (ecKey == null || !ecKey.hasPrivKey()) {
            Toast.makeText(PayNymDetailsActivity.this, R.string.bip47_cannot_compose_notif_tx, Toast.LENGTH_SHORT).show();
            return;
        }
        // 
        // use outpoint for payload masking
        // 
        byte[] privkey = ecKey.getPrivKeyBytes();
        byte[] pubkey = payment_code.notificationAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).getPubKey();
        byte[] outpoint = outPoint.bitcoinSerialize();
        // Log.i("PayNymDetailsActivity", "outpoint:" + Hex.toHexString(outpoint));
        // Log.i("PayNymDetailsActivity", "payer shared secret:" + Hex.toHexString(new SecretPoint(privkey, pubkey).ECDHSecretAsBytes()));
        byte[] mask = PaymentCode.getMask(new SecretPoint(privkey, pubkey).ECDHSecretAsBytes(), outpoint);
        // Log.i("PayNymDetailsActivity", "mask:" + Hex.toHexString(mask));
        // Log.i("PayNymDetailsActivity", "mask length:" + mask.length);
        // Log.i("PayNymDetailsActivity", "payload0:" + Hex.toHexString(BIP47Util.getInstance(context).getPaymentCode().getPayload()));
        op_return = PaymentCode.blind(BIP47Util.getInstance(PayNymDetailsActivity.this).getPaymentCode().getPayload(), mask);
    // Log.i("PayNymDetailsActivity", "payload1:" + Hex.toHexString(op_return));
    } catch (InvalidKeyException ike) {
        Toast.makeText(PayNymDetailsActivity.this, ike.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (InvalidKeySpecException ikse) {
        Toast.makeText(PayNymDetailsActivity.this, ikse.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (NoSuchAlgorithmException nsae) {
        Toast.makeText(PayNymDetailsActivity.this, nsae.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (NoSuchProviderException nspe) {
        Toast.makeText(PayNymDetailsActivity.this, nspe.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (Exception e) {
        Toast.makeText(PayNymDetailsActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    }
    final HashMap<String, BigInteger> receivers = new HashMap<String, BigInteger>();
    receivers.put(Hex.toHexString(op_return), BigInteger.ZERO);
    receivers.put(payment_code.notificationAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).getAddressString(), SendNotifTxFactory._bNotifTxValue);
    receivers.put(SamouraiWallet.getInstance().isTestNet() ? SendNotifTxFactory.TESTNET_SAMOURAI_NOTIF_TX_FEE_ADDRESS : SendNotifTxFactory.SAMOURAI_NOTIF_TX_FEE_ADDRESS, SendNotifTxFactory._bSWFee);
    final long change = totalValueSelected - (amount + fee.longValue());
    if (change > 0L) {
        String change_address = BIP84Util.getInstance(PayNymDetailsActivity.this).getAddressAt(AddressFactory.CHANGE_CHAIN, BIP84Util.getInstance(PayNymDetailsActivity.this).getWallet().getAccount(0).getChange().getAddrIdx()).getBech32AsString();
        receivers.put(change_address, BigInteger.valueOf(change));
    }
    Log.d("PayNymDetailsActivity", "outpoints:" + outpoints.size());
    Log.d("PayNymDetailsActivity", "totalValueSelected:" + BigInteger.valueOf(totalValueSelected).toString());
    Log.d("PayNymDetailsActivity", "amount:" + BigInteger.valueOf(amount).toString());
    Log.d("PayNymDetailsActivity", "change:" + BigInteger.valueOf(change).toString());
    Log.d("PayNymDetailsActivity", "fee:" + fee.toString());
    if (change < 0L) {
        Toast.makeText(PayNymDetailsActivity.this, R.string.bip47_cannot_compose_notif_tx, Toast.LENGTH_SHORT).show();
        return;
    }
    final MyTransactionOutPoint _outPoint = outPoint;
    String strNotifTxMsg = getText(R.string.bip47_setup4_text1) + " ";
    long notifAmount = amount;
    String strAmount = MonetaryUtil.getInstance().getBTCFormat().format(((double) notifAmount + fee.longValue()) / 1e8) + " BTC ";
    strNotifTxMsg += strAmount + getText(R.string.bip47_setup4_text2);
    showFollowAlert(strAmount, view -> {
        new Thread(new Runnable() {

            @Override
            public void run() {
                Looper.prepare();
                Transaction tx = SendFactory.getInstance(PayNymDetailsActivity.this).makeTransaction(0, outpoints, receivers);
                if (tx != null) {
                    String input0hash = tx.getInput(0L).getOutpoint().getHash().toString();
                    Log.d("PayNymDetailsActivity", "input0 hash:" + input0hash);
                    Log.d("PayNymDetailsActivity", "_outPoint hash:" + _outPoint.getTxHash().toString());
                    int input0index = (int) tx.getInput(0L).getOutpoint().getIndex();
                    Log.d("PayNymDetailsActivity", "input0 index:" + input0index);
                    Log.d("PayNymDetailsActivity", "_outPoint index:" + _outPoint.getTxOutputN());
                    if (!input0hash.equals(_outPoint.getTxHash().toString()) || input0index != _outPoint.getTxOutputN()) {
                        Toast.makeText(PayNymDetailsActivity.this, R.string.bip47_cannot_compose_notif_tx, Toast.LENGTH_SHORT).show();
                        return;
                    }
                    tx = SendFactory.getInstance(PayNymDetailsActivity.this).signTransaction(tx, 0);
                    final String hexTx = new String(org.bouncycastle.util.encoders.Hex.encode(tx.bitcoinSerialize()));
                    Log.d("SendActivity", tx.getHashAsString());
                    Log.d("SendActivity", hexTx);
                    boolean isOK = false;
                    String response = null;
                    try {
                        response = PushTx.getInstance(PayNymDetailsActivity.this).samourai(hexTx);
                        Log.d("SendActivity", "pushTx:" + response);
                        if (response != null) {
                            org.json.JSONObject jsonObject = new org.json.JSONObject(response);
                            if (jsonObject.has("status")) {
                                if (jsonObject.getString("status").equals("ok")) {
                                    isOK = true;
                                }
                            }
                        } else {
                            Toast.makeText(PayNymDetailsActivity.this, R.string.pushtx_returns_null, Toast.LENGTH_SHORT).show();
                            return;
                        }
                        if (isOK) {
                            Toast.makeText(PayNymDetailsActivity.this, R.string.payment_channel_init, Toast.LENGTH_SHORT).show();
                            // 
                            // set outgoing index for payment code to 0
                            // 
                            BIP47Meta.getInstance().setOutgoingIdx(pcode, 0);
                            // Log.i("SendNotifTxFactory", "tx hash:" + tx.getHashAsString());
                            // 
                            // status to NO_CFM
                            // 
                            BIP47Meta.getInstance().setOutgoingStatus(pcode, tx.getHashAsString(), BIP47Meta.STATUS_SENT_NO_CFM);
                            // 
                            if (change > 0L) {
                                BIP49Util.getInstance(PayNymDetailsActivity.this).getWallet().getAccount(0).getChange().incAddrIdx();
                            }
                            savePayLoad();
                        } else {
                            Toast.makeText(PayNymDetailsActivity.this, R.string.tx_failed, Toast.LENGTH_SHORT).show();
                        }
                        runOnUiThread(() -> {
                            setPayNym();
                        });
                    } catch (JSONException je) {
                        Toast.makeText(PayNymDetailsActivity.this, "pushTx:" + je.getMessage(), Toast.LENGTH_SHORT).show();
                        return;
                    } catch (MnemonicException.MnemonicLengthException mle) {
                        Toast.makeText(PayNymDetailsActivity.this, "pushTx:" + mle.getMessage(), Toast.LENGTH_SHORT).show();
                        return;
                    } catch (DecoderException de) {
                        Toast.makeText(PayNymDetailsActivity.this, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                        return;
                    } catch (IOException ioe) {
                        Toast.makeText(PayNymDetailsActivity.this, "pushTx:" + ioe.getMessage(), Toast.LENGTH_SHORT).show();
                        return;
                    } catch (DecryptionException de) {
                        Toast.makeText(PayNymDetailsActivity.this, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
                Looper.loop();
            }
        }).start();
    });
}
Also used : AlertDialog(android.app.AlertDialog) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) ECKey(org.bitcoinj.core.ECKey) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) JSONObject(org.json.JSONObject) InvalidKeySpecException(java.security.spec.InvalidKeySpecException) SecretPoint(com.samourai.wallet.bip47.rpc.SecretPoint) AddressFormatException(org.bitcoinj.core.AddressFormatException) Script(org.bitcoinj.script.Script) SuggestedFee(com.samourai.wallet.send.SuggestedFee) Transaction(org.bitcoinj.core.Transaction) JSONObject(org.json.JSONObject) BigInteger(java.math.BigInteger) DialogInterface(android.content.DialogInterface) MnemonicException(org.bitcoinj.crypto.MnemonicException) PaymentCode(com.samourai.wallet.bip47.rpc.PaymentCode) SendFactory(com.samourai.wallet.send.SendFactory) JSONException(org.json.JSONException) Intent(android.content.Intent) IOException(java.io.IOException) InvalidKeyException(java.security.InvalidKeyException) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) SecretPoint(com.samourai.wallet.bip47.rpc.SecretPoint) JSONException(org.json.JSONException) DecoderException(org.bouncycastle.util.encoders.DecoderException) AddressFormatException(org.bitcoinj.core.AddressFormatException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InvalidKeyException(java.security.InvalidKeyException) InvalidKeySpecException(java.security.spec.InvalidKeySpecException) MnemonicException(org.bitcoinj.crypto.MnemonicException) DecryptionException(com.samourai.wallet.crypto.DecryptionException) IOException(java.io.IOException) NoSuchProviderException(java.security.NoSuchProviderException) UTXO(com.samourai.wallet.send.UTXO) DecoderException(org.bouncycastle.util.encoders.DecoderException) MyTransactionInput(com.samourai.wallet.send.MyTransactionInput) NoSuchProviderException(java.security.NoSuchProviderException) DecryptionException(com.samourai.wallet.crypto.DecryptionException)

Example 10 with UTXO

use of com.samourai.wallet.send.UTXO in project samourai-wallet-android by Samourai-Wallet.

the class APIFactory method getUtxos.

public List<UTXO> getUtxos(boolean filter) {
    long amount = 0L;
    for (String key : utxos.keySet()) {
    // for(MyTransactionOutPoint out : utxos.get(key).getOutpoints())    {
    // debug("APIFactory", "utxo:" + out.getAddress() + "," + out.getValue());
    // debug("APIFactory", "utxo:" + utxos.get(key).getPath());
    // amount += out.getValue().longValue();
    // }
    }
    debug("APIFactory", "utxos by value:" + amount);
    List<UTXO> unspents = new ArrayList<UTXO>();
    if (filter) {
        for (String key : utxos.keySet()) {
            UTXO u = new UTXO();
            for (MyTransactionOutPoint out : utxos.get(key).getOutpoints()) {
                if (!BlockedUTXO.getInstance().contains(out.getTxHash().toString(), out.getTxOutputN())) {
                    u.getOutpoints().add(out);
                    u.setPath(utxos.get(key).getPath());
                }
            }
            if (u.getOutpoints().size() > 0) {
                unspents.add(u);
            }
        }
    } else {
        unspents.addAll(utxos.values());
    }
    return unspents;
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint)

Aggregations

UTXO (com.samourai.wallet.send.UTXO)35 MyTransactionOutPoint (com.samourai.wallet.send.MyTransactionOutPoint)33 ArrayList (java.util.ArrayList)23 HashMap (java.util.HashMap)19 Transaction (org.bitcoinj.core.Transaction)17 IOException (java.io.IOException)14 TransactionOutPoint (org.bitcoinj.core.TransactionOutPoint)14 BlockedUTXO (com.samourai.wallet.send.BlockedUTXO)13 BigInteger (java.math.BigInteger)13 MnemonicException (org.bitcoinj.crypto.MnemonicException)13 JSONException (org.json.JSONException)13 JSONObject (org.json.JSONObject)13 ECKey (org.bitcoinj.core.ECKey)12 TransactionInput (org.bitcoinj.core.TransactionInput)10 Intent (android.content.Intent)9 SegwitAddress (com.samourai.wallet.segwit.SegwitAddress)9 Script (org.bitcoinj.script.Script)9 AlertDialog (android.app.AlertDialog)8 DecryptionException (com.samourai.wallet.crypto.DecryptionException)8 DialogInterface (android.content.DialogInterface)6