Search in sources :

Example 1 with MyTransactionOutPoint

use of com.samourai.wallet.send.MyTransactionOutPoint 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(), false);
            }
            */
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.XPUB49REG, false) == false) {
            registerXPUB(BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr(), true);
        }
        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);
                }
            }
        }
        if (addressStrings.size() > 0) {
            s = addressStrings.toArray(new String[0]);
            // Log.i("APIFactory", addressStrings.toString());
            getUnspentOutputs(s);
        }
        Log.d("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 + 1 + s.length];
                all[0] = BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr();
                System.arraycopy(hdw.getXPUBs(), 0, all, 1, hdw.getXPUBs().length);
                System.arraycopy(s, 0, all, hdw.getXPUBs().length + 1, s.length);
            } else {
                all = new String[hdw.getXPUBs().length + 1];
                all[0] = BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr();
                System.arraycopy(hdw.getXPUBs(), 0, all, 1, 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] = HD_WalletFactory.getInstance(context).get().getAccount(1).xpubstr();
            xs[2] = BIP49Util.getInstance(context).getWallet().getAccount(0).xpubstr();
            getUnspentOutputs(xs);
            getDynamicFees();
        }
        // 
        // 
        // 
        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()) {
            Log.d("APIFactory", "not dusted:" + _s);
            if (!seenOutputs.contains(_s)) {
                BlockedUTXO.getInstance().removeNotDusted(_s);
                Log.d("APIFactory", "not dusted removed:" + _s);
            }
        }
        for (String _s : BlockedUTXO.getInstance().getBlockedUTXO().keySet()) {
            Log.d("APIFactory", "blocked:" + _s);
            if (!seenOutputs.contains(_s)) {
                BlockedUTXO.getInstance().remove(_s);
                Log.d("APIFactory", "blocked removed:" + _s);
            }
        }
    } catch (IndexOutOfBoundsException ioobe) {
        ioobe.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) HD_Wallet(com.samourai.wallet.hd.HD_Wallet) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) JSONException(org.json.JSONException) AddressFormatException(org.bitcoinj.core.AddressFormatException) MnemonicException(org.bitcoinj.crypto.MnemonicException) IOException(java.io.IOException)

Example 2 with MyTransactionOutPoint

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

the class BIP47Activity method doNotifTx.

private void doNotifTx(final String pcode) {
    // 
    // get wallet balance
    // 
    long balance = 0L;
    try {
        balance = APIFactory.getInstance(BIP47Activity.this).getXpubAmounts().get(HD_WalletFactory.getInstance(BIP47Activity.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();
    // 
    // calc btc fee from USD Samourai fee
    // 
    double btc_fx = ExchangeRateFactory.getInstance(BIP47Activity.this).getBitfinexPrice("USD");
    BigInteger currentSWFee = BigInteger.valueOf((long) ((btc_fx / SendNotifTxFactory._dSWFeeUSD) * 1e8));
    if (currentSWFee.longValue() < SendNotifTxFactory._bSWFee.longValue() || currentSWFee.longValue() > SendNotifTxFactory._bSWCeilingFee.longValue()) {
        currentSWFee = SendNotifTxFactory._bSWFee;
    }
    // 
    // add Samourai Wallet fee to total amount
    // 
    amount += currentSWFee.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().getP2SH_P2WPKH().values());
    } else {
        utxos = APIFactory.getInstance(BIP47Activity.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("BIP47Activity", "single output");
            Log.d("BIP47Activity", "value selected:" + u.getValue());
            Log.d("BIP47Activity", "total value selected:" + totalValueSelected);
            Log.d("BIP47Activity", "nb inputs:" + u.getOutpoints().size());
            break;
        }
    }
    // 
    // use low fee settings
    // 
    SuggestedFee suggestedFee = FeeUtil.getInstance().getSuggestedFee();
    FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getLowFee());
    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("BIP47Activity", "multiple outputs");
                Log.d("BIP47Activity", "total value selected:" + totalValueSelected);
                Log.d("BIP47Activity", "nb inputs:" + u.getOutpoints().size());
                break;
            }
        }
        fee = FeeUtil.getInstance().estimatedFee(selected, 4);
    } else {
        fee = FeeUtil.getInstance().estimatedFee(1, 4);
    }
    // 
    // 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(BIP47Activity.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(BIP47Activity.this, R.string.bip47_cannot_identify_outpoint, Toast.LENGTH_SHORT).show();
        return;
    }
    byte[] op_return = null;
    // 
    try {
        Script inputScript = new Script(outPoint.getConnectedPubKeyScript());
        String address = inputScript.getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
        ECKey ecKey = SendFactory.getPrivKey(address);
        if (ecKey == null || !ecKey.hasPrivKey()) {
            Toast.makeText(BIP47Activity.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().getPubKey();
        byte[] outpoint = outPoint.bitcoinSerialize();
        // Log.i("BIP47Activity", "outpoint:" + Hex.toHexString(outpoint));
        // Log.i("BIP47Activity", "payer shared secret:" + Hex.toHexString(new SecretPoint(privkey, pubkey).ECDHSecretAsBytes()));
        byte[] mask = PaymentCode.getMask(new SecretPoint(privkey, pubkey).ECDHSecretAsBytes(), outpoint);
        // Log.i("BIP47Activity", "mask:" + Hex.toHexString(mask));
        // Log.i("BIP47Activity", "mask length:" + mask.length);
        // Log.i("BIP47Activity", "payload0:" + Hex.toHexString(BIP47Util.getInstance(context).getPaymentCode().getPayload()));
        op_return = PaymentCode.blind(BIP47Util.getInstance(BIP47Activity.this).getPaymentCode().getPayload(), mask);
    // Log.i("BIP47Activity", "payload1:" + Hex.toHexString(op_return));
    } catch (InvalidKeyException ike) {
        Toast.makeText(BIP47Activity.this, ike.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (InvalidKeySpecException ikse) {
        Toast.makeText(BIP47Activity.this, ikse.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (NoSuchAlgorithmException nsae) {
        Toast.makeText(BIP47Activity.this, nsae.getMessage(), Toast.LENGTH_SHORT).show();
        return;
    } catch (NoSuchProviderException nspe) {
        Toast.makeText(BIP47Activity.this, nspe.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().getAddressString(), SendNotifTxFactory._bNotifTxValue);
    receivers.put(SamouraiWallet.getInstance().isTestNet() ? SendNotifTxFactory.TESTNET_SAMOURAI_NOTIF_TX_FEE_ADDRESS : SendNotifTxFactory.SAMOURAI_NOTIF_TX_FEE_ADDRESS, currentSWFee);
    final long change = totalValueSelected - (amount + fee.longValue());
    if (change > 0L) {
        String change_address = BIP49Util.getInstance(BIP47Activity.this).getAddressAt(AddressFactory.CHANGE_CHAIN, BIP49Util.getInstance(BIP47Activity.this).getWallet().getAccount(0).getChange().getAddrIdx()).getAddressAsString();
        receivers.put(change_address, BigInteger.valueOf(change));
    }
    Log.d("BIP47Activity", "outpoints:" + outpoints.size());
    Log.d("BIP47Activity", "totalValueSelected:" + BigInteger.valueOf(totalValueSelected).toString());
    Log.d("BIP47Activity", "amount:" + BigInteger.valueOf(amount).toString());
    Log.d("BIP47Activity", "change:" + BigInteger.valueOf(change).toString());
    Log.d("BIP47Activity", "fee:" + fee.toString());
    if (change < 0L) {
        Toast.makeText(BIP47Activity.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);
    AlertDialog.Builder dlg = new AlertDialog.Builder(BIP47Activity.this).setTitle(R.string.bip47_setup4_title).setMessage(strNotifTxMsg).setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {

        public void onClick(DialogInterface dialog, int whichButton) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    Looper.prepare();
                    Transaction tx = SendFactory.getInstance(BIP47Activity.this).makeTransaction(0, outpoints, receivers);
                    if (tx != null) {
                        String input0hash = tx.getInput(0L).getOutpoint().getHash().toString();
                        Log.d("BIP47Activity", "input0 hash:" + input0hash);
                        Log.d("BIP47Activity", "_outPoint hash:" + _outPoint.getTxHash().toString());
                        int input0index = (int) tx.getInput(0L).getOutpoint().getIndex();
                        Log.d("BIP47Activity", "input0 index:" + input0index);
                        Log.d("BIP47Activity", "_outPoint index:" + _outPoint.getTxOutputN());
                        if (!input0hash.equals(_outPoint.getTxHash().toString()) || input0index != _outPoint.getTxOutputN()) {
                            Toast.makeText(BIP47Activity.this, R.string.bip47_cannot_compose_notif_tx, Toast.LENGTH_SHORT).show();
                            return;
                        }
                        tx = SendFactory.getInstance(BIP47Activity.this).signTransaction(tx);
                        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(BIP47Activity.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(BIP47Activity.this, R.string.pushtx_returns_null, Toast.LENGTH_SHORT).show();
                                return;
                            }
                            if (isOK) {
                                Toast.makeText(BIP47Activity.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(BIP47Activity.this).getWallet().getAccount(0).getChange().incAddrIdx();
                                }
                                PayloadUtil.getInstance(BIP47Activity.this).saveWalletToJSON(new CharSequenceX(AccessFactory.getInstance(BIP47Activity.this).getGUID() + AccessFactory.getInstance(BIP47Activity.this).getPIN()));
                            } else {
                                Toast.makeText(BIP47Activity.this, R.string.tx_failed, Toast.LENGTH_SHORT).show();
                            }
                        } catch (JSONException je) {
                            Toast.makeText(BIP47Activity.this, "pushTx:" + je.getMessage(), Toast.LENGTH_SHORT).show();
                            return;
                        } catch (MnemonicException.MnemonicLengthException mle) {
                            Toast.makeText(BIP47Activity.this, "pushTx:" + mle.getMessage(), Toast.LENGTH_SHORT).show();
                            return;
                        } catch (DecoderException de) {
                            Toast.makeText(BIP47Activity.this, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                            return;
                        } catch (IOException ioe) {
                            Toast.makeText(BIP47Activity.this, "pushTx:" + ioe.getMessage(), Toast.LENGTH_SHORT).show();
                            return;
                        } catch (DecryptionException de) {
                            Toast.makeText(BIP47Activity.this, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                            return;
                        }
                    }
                    Looper.loop();
                }
            }).start();
        }
    }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {

        public void onClick(DialogInterface dialog, int whichButton) {
            ;
        }
    });
    dlg.show();
}
Also used : AlertDialog(android.app.AlertDialog) CharSequenceX(com.samourai.wallet.util.CharSequenceX) 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) Point(android.graphics.Point) 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 3 with MyTransactionOutPoint

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

the class BatchSendActivity method doSpend.

private void doSpend() {
    HashMap<String, BigInteger> receivers = new HashMap<String, BigInteger>();
    long amount = 0L;
    for (BatchSendUtil.BatchSend _data : data) {
        Log.d("BatchSendActivity", "output:" + _data.amount);
        Log.d("BatchSendActivity", "output:" + _data.addr);
        Log.d("BatchSendActivity", "output:" + _data.pcode);
        amount += _data.amount;
        receivers.put(_data.addr, BigInteger.valueOf(_data.amount));
    }
    Log.d("BatchSendActivity", "amount:" + amount);
    List<UTXO> utxos = APIFactory.getInstance(BatchSendActivity.this).getUtxos(true);
    Collections.sort(utxos, new UTXO.UTXOComparator());
    List<UTXO> selectedUTXO = new ArrayList<UTXO>();
    int p2pkh = 0;
    int p2wpkh = 0;
    long totalValueSelected = 0L;
    int totalSelected = 0;
    for (UTXO utxo : utxos) {
        Log.d("BatchSendActivity", "utxo value:" + utxo.getValue());
        selectedUTXO.add(utxo);
        totalValueSelected += utxo.getValue();
        totalSelected += utxo.getOutpoints().size();
        Pair<Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(utxo.getOutpoints());
        p2pkh += outpointTypes.getLeft();
        p2wpkh += outpointTypes.getRight();
        if (totalValueSelected >= (amount + SamouraiWallet.bDust.longValue() + FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2wpkh, receivers.size() + 1).longValue())) {
            break;
        }
    }
    Log.d("BatchSendActivity", "totalSelected:" + totalSelected);
    Log.d("BatchSendActivity", "totalValueSelected:" + totalValueSelected);
    List<MyTransactionOutPoint> outpoints = new ArrayList<MyTransactionOutPoint>();
    for (UTXO utxo : selectedUTXO) {
        outpoints.addAll(utxo.getOutpoints());
        for (MyTransactionOutPoint out : utxo.getOutpoints()) {
            Log.d("BatchSendActivity", "outpoint hash:" + out.getTxHash().toString());
            Log.d("BatchSendActivity", "outpoint idx:" + out.getTxOutputN());
            Log.d("BatchSendActivity", "outpoint address:" + out.getAddress());
        }
    }
    Pair<Integer, Integer> outpointTypes = FeeUtil.getInstance().getOutpointCount(outpoints);
    BigInteger fee = FeeUtil.getInstance().estimatedFeeSegwit(outpointTypes.getLeft(), outpointTypes.getRight(), receivers.size() + 1);
    Log.d("BatchSendActivity", "fee:" + fee.longValue());
    if (amount + fee.longValue() > balance) {
        Toast.makeText(BatchSendActivity.this, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
        return;
    }
    long changeAmount = totalValueSelected - (amount + fee.longValue());
    String change_address = null;
    int change_idx = -1;
    if (changeAmount > 0L) {
        change_idx = BIP49Util.getInstance(BatchSendActivity.this).getWallet().getAccount(0).getChange().getAddrIdx();
        change_address = BIP49Util.getInstance(BatchSendActivity.this).getAddressAt(AddressFactory.CHANGE_CHAIN, change_idx).getAddressAsString();
        receivers.put(change_address, BigInteger.valueOf(changeAmount));
        Log.d("BatchSendActivity", "change output:" + changeAmount);
        Log.d("BatchSendActivity", "change output:" + change_address);
    }
    Transaction tx = SendFactory.getInstance(BatchSendActivity.this).makeTransaction(0, outpoints, receivers);
    if (tx != null) {
        final RBFSpend rbf;
        if (PrefsUtil.getInstance(BatchSendActivity.this).getValue(PrefsUtil.RBF_OPT_IN, false) == true) {
            rbf = new RBFSpend();
            for (TransactionInput input : tx.getInputs()) {
                boolean _isBIP49 = false;
                String _addr = null;
                Address _address = input.getConnectedOutput().getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams());
                if (_address != null) {
                    _addr = _address.toString();
                }
                if (_addr == null) {
                    _addr = input.getConnectedOutput().getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
                    _isBIP49 = true;
                }
                String path = APIFactory.getInstance(BatchSendActivity.this).getUnspentPaths().get(_addr);
                if (path != null) {
                    if (_isBIP49) {
                        rbf.addKey(input.getOutpoint().toString(), path + "/49");
                    } else {
                        rbf.addKey(input.getOutpoint().toString(), path);
                    }
                } else {
                    String pcode = BIP47Meta.getInstance().getPCode4Addr(_addr);
                    int idx = BIP47Meta.getInstance().getIdx4Addr(_addr);
                    rbf.addKey(input.getOutpoint().toString(), pcode + "/" + idx);
                }
            }
        } else {
            rbf = null;
        }
        String strChangeIsDust = "";
        String strPrivacyWarning = "";
        String strMessage = strChangeIsDust + strPrivacyWarning + "Send " + Coin.valueOf(amount).toPlainString() + " BTC. (fee:" + Coin.valueOf(fee.longValue()).toPlainString() + ")?\n";
        final long _change = changeAmount;
        final String _change_address = change_address;
        final int _change_idx = change_idx;
        tx = SendFactory.getInstance(BatchSendActivity.this).signTransaction(tx);
        final String hexTx = new String(Hex.encode(tx.bitcoinSerialize()));
        final String strTxHash = tx.getHashAsString();
        // Log.d("BatchSendActivity", "tx hash:" + tx.getHashAsString());
        // Log.d("BatchSendActivity", "hex signed tx:" + hexTx);
        AlertDialog.Builder dlg = new AlertDialog.Builder(BatchSendActivity.this).setTitle(R.string.app_name).setMessage(strMessage).setCancelable(false).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            public void onClick(final DialogInterface dialog, int whichButton) {
                dialog.dismiss();
                if (PrefsUtil.getInstance(BatchSendActivity.this).getValue(PrefsUtil.BROADCAST_TX, true) == false) {
                    doShowTx(hexTx, strTxHash);
                    return;
                }
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        Looper.prepare();
                        boolean isOK = false;
                        String response = null;
                        try {
                            if (PrefsUtil.getInstance(BatchSendActivity.this).getValue(PrefsUtil.USE_TRUSTED_NODE, false) == true) {
                                if (TrustedNodeUtil.getInstance().isSet()) {
                                    response = PushTx.getInstance(BatchSendActivity.this).trustedNode(hexTx);
                                    JSONObject jsonObject = new org.json.JSONObject(response);
                                    if (jsonObject.has("result")) {
                                        if (jsonObject.getString("result").matches("^[A-Za-z0-9]{64}$")) {
                                            isOK = true;
                                        } else {
                                            Toast.makeText(BatchSendActivity.this, R.string.trusted_node_tx_error, Toast.LENGTH_SHORT).show();
                                        }
                                    }
                                } else {
                                    Toast.makeText(BatchSendActivity.this, R.string.trusted_node_not_valid, Toast.LENGTH_SHORT).show();
                                }
                            } else {
                                response = PushTx.getInstance(BatchSendActivity.this).samourai(hexTx);
                                if (response != null) {
                                    JSONObject jsonObject = new org.json.JSONObject(response);
                                    if (jsonObject.has("status")) {
                                        if (jsonObject.getString("status").equals("ok")) {
                                            isOK = true;
                                        }
                                    }
                                } else {
                                    Toast.makeText(BatchSendActivity.this, R.string.pushtx_returns_null, Toast.LENGTH_SHORT).show();
                                }
                            }
                            if (isOK) {
                                BatchSendUtil.getInstance().clear();
                                if (PrefsUtil.getInstance(BatchSendActivity.this).getValue(PrefsUtil.USE_TRUSTED_NODE, false) == false) {
                                    Toast.makeText(BatchSendActivity.this, R.string.tx_sent, Toast.LENGTH_SHORT).show();
                                } else {
                                    Toast.makeText(BatchSendActivity.this, R.string.trusted_node_tx_sent, Toast.LENGTH_SHORT).show();
                                }
                                if (_change > 0L) {
                                    BIP49Util.getInstance(BatchSendActivity.this).getWallet().getAccount(0).getChange().incAddrIdx();
                                }
                                if (PrefsUtil.getInstance(BatchSendActivity.this).getValue(PrefsUtil.RBF_OPT_IN, false) == true) {
                                    if (_change > 0L && _change_address != null) {
                                        rbf.addChangeAddr(_change_address);
                                    }
                                    rbf.setHash(strTxHash);
                                    rbf.setSerializedTx(hexTx);
                                    RBFUtil.getInstance().add(rbf);
                                }
                                for (BatchSendUtil.BatchSend d : data) {
                                    String address = d.addr;
                                    String pcode = d.pcode;
                                    // increment counter if BIP47 spend
                                    if (pcode != null && pcode.length() > 0) {
                                        BIP47Meta.getInstance().getPCode4AddrLookup().put(address, pcode);
                                        BIP47Meta.getInstance().inc(pcode);
                                        SimpleDateFormat sd = new SimpleDateFormat("dd MMM");
                                        String strTS = sd.format(currentTimeMillis());
                                        String event = strTS + " " + BatchSendActivity.this.getString(R.string.sent) + " " + MonetaryUtil.getInstance().getBTCFormat().format((double) d.amount / 1e8) + " BTC";
                                        BIP47Meta.getInstance().setLatestEvent(strPCode, event);
                                    }
                                }
                                /*
                            if(strPrivacyWarning.length() > 0 && cbShowAgain != null)    {
                                SendAddressUtil.getInstance().add(address, cbShowAgain.isChecked() ? false : true);
                            }
                            else if(SendAddressUtil.getInstance().get(address) == 0)    {
                                SendAddressUtil.getInstance().add(address, false);
                            }
                            else    {
                                SendAddressUtil.getInstance().add(address, true);
                            }
                            */
                                /*
                                            Intent intent = new Intent("com.samourai.wallet.BalanceFragment.REFRESH");
                                            intent.putExtra("notifTx", false);
                                            intent.putExtra("fetch", true);
                                            LocalBroadcastManager.getInstance(BatchSendActivity.this).sendBroadcast(intent);
                                            */
                                View view = BatchSendActivity.this.getCurrentFocus();
                                if (view != null) {
                                    InputMethodManager imm = (InputMethodManager) BatchSendActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE);
                                    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
                                }
                                Intent _intent = new Intent(BatchSendActivity.this, BalanceActivity.class);
                                _intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                                startActivity(_intent);
                            } else {
                                Toast.makeText(BatchSendActivity.this, R.string.tx_failed, Toast.LENGTH_SHORT).show();
                                // reset change index upon tx fail
                                BIP49Util.getInstance(BatchSendActivity.this).getWallet().getAccount(0).getChange().setAddrIdx(_change_idx);
                            }
                        } catch (JSONException je) {
                            Toast.makeText(BatchSendActivity.this, "pushTx:" + je.getMessage(), Toast.LENGTH_SHORT).show();
                        } catch (DecoderException de) {
                            Toast.makeText(BatchSendActivity.this, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                        } finally {
                        /*
                        BatchSendActivity.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                btSend.setActivated(true);
                                btSend.setClickable(true);
                                progress.dismiss();
                                dialog.dismiss();
                            }
                        });
                        */
                        }
                        Looper.loop();
                    }
                }).start();
            }
        }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int whichButton) {
                dialog.dismiss();
            }
        });
        if (!isFinishing()) {
            dlg.show();
        }
    } else {
        // Log.d("SendActivity", "tx error");
        Toast.makeText(BatchSendActivity.this, "tx error", Toast.LENGTH_SHORT).show();
    }
}
Also used : AlertDialog(android.app.AlertDialog) Address(org.bitcoinj.core.Address) PaymentAddress(com.samourai.wallet.bip47.rpc.PaymentAddress) HashMap(java.util.HashMap) BatchSendUtil(com.samourai.wallet.util.BatchSendUtil) DialogInterface(android.content.DialogInterface) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) InputMethodManager(android.view.inputmethod.InputMethodManager) TransactionInput(org.bitcoinj.core.TransactionInput) RBFSpend(com.samourai.wallet.send.RBFSpend) JSONException(org.json.JSONException) Intent(android.content.Intent) ImageView(android.widget.ImageView) View(android.view.View) AdapterView(android.widget.AdapterView) TextView(android.widget.TextView) ListView(android.widget.ListView) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) Point(android.graphics.Point) BigInteger(java.math.BigInteger) UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) DecoderException(org.bouncycastle.util.encoders.DecoderException) Transaction(org.bitcoinj.core.Transaction) JSONObject(org.json.JSONObject) BigInteger(java.math.BigInteger) SimpleDateFormat(java.text.SimpleDateFormat)

Example 4 with MyTransactionOutPoint

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

the class BatchSendActivity method markUTXOAsUnspendable.

private void markUTXOAsUnspendable(String hexTx) {
    HashMap<String, Long> utxos = new HashMap<String, Long>();
    for (UTXO utxo : APIFactory.getInstance(BatchSendActivity.this).getUtxos(true)) {
        for (MyTransactionOutPoint outpoint : utxo.getOutpoints()) {
            utxos.put(outpoint.getTxHash().toString() + "-" + outpoint.getTxOutputN(), outpoint.getValue().longValue());
        }
    }
    Transaction tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams(), Hex.decode(hexTx));
    for (TransactionInput input : tx.getInputs()) {
        BlockedUTXO.getInstance().add(input.getOutpoint().getHash().toString(), (int) input.getOutpoint().getIndex(), utxos.get(input.getOutpoint().getHash().toString() + "-" + (int) input.getOutpoint().getIndex()));
    }
}
Also used : UTXO(com.samourai.wallet.send.UTXO) BlockedUTXO(com.samourai.wallet.send.BlockedUTXO) Transaction(org.bitcoinj.core.Transaction) HashMap(java.util.HashMap) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput)

Example 5 with MyTransactionOutPoint

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

the class RicochetMeta method getHop0Tx.

private Transaction getHop0Tx(List<UTXO> utxos, long spendAmount, String destination, long fee, boolean samouraiFeeViaBIP47) {
    List<MyTransactionOutPoint> unspent = new ArrayList<MyTransactionOutPoint>();
    long totalValueSelected = 0L;
    for (UTXO u : utxos) {
        totalValueSelected += u.getValue();
        unspent.addAll(u.getOutpoints());
    }
    // Log.d("RicochetMeta", "spendAmount:" + spendAmount);
    // Log.d("RicochetMeta", "fee:" + fee);
    // Log.d("RicochetMeta", "totalValueSelected:" + totalValueSelected);
    long changeAmount = totalValueSelected - (spendAmount + fee);
    // Log.d("RicochetMeta", "changeAmount:" + changeAmount);
    HashMap<String, BigInteger> receivers = new HashMap<String, BigInteger>();
    if (changeAmount > 0L) {
        String change_address = null;
        try {
            change_address = HD_WalletFactory.getInstance(context).get().getAccount(0).getChange().getAddressAt(HD_WalletFactory.getInstance(context).get().getAccount(0).getChange().getAddrIdx()).getAddressString();
            receivers.put(change_address, BigInteger.valueOf(changeAmount));
        } catch (IOException ioe) {
            Toast.makeText(context, R.string.error_change_output, Toast.LENGTH_SHORT).show();
            return null;
        } catch (MnemonicException.MnemonicLengthException mle) {
            Toast.makeText(context, R.string.error_change_output, Toast.LENGTH_SHORT).show();
            return null;
        }
    }
    receivers.put(destination, BigInteger.valueOf(spendAmount - samouraiFeeAmount.longValue()));
    if (samouraiFeeViaBIP47) {
        try {
            PaymentCode pcode = new PaymentCode(BIP47Meta.strSamouraiDonationPCode);
            PaymentAddress paymentAddress = BIP47Util.getInstance(context).getSendAddress(pcode, BIP47Meta.getInstance().getOutgoingIdx(BIP47Meta.strSamouraiDonationPCode));
            String strAddress = paymentAddress.getSendECKey().toAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
            receivers.put(strAddress, samouraiFeeAmount);
        } catch (Exception e) {
            receivers.put(SamouraiWallet.getInstance().isTestNet() ? TESTNET_SAMOURAI_RICOCHET_TX_FEE_ADDRESS : SAMOURAI_RICOCHET_TX_FEE_ADDRESS, samouraiFeeAmount);
        }
    } else {
        receivers.put(SamouraiWallet.getInstance().isTestNet() ? TESTNET_SAMOURAI_RICOCHET_TX_FEE_ADDRESS : SAMOURAI_RICOCHET_TX_FEE_ADDRESS, samouraiFeeAmount);
    }
    Transaction tx = SendFactory.getInstance(context).makeTransaction(0, unspent, receivers);
    tx = SendFactory.getInstance(context).signTransaction(tx);
    return tx;
}
Also used : PaymentCode(com.samourai.wallet.bip47.rpc.PaymentCode) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) IOException(java.io.IOException) PaymentAddress(com.samourai.wallet.bip47.rpc.PaymentAddress) MnemonicException(org.bitcoinj.crypto.MnemonicException) JSONException(org.json.JSONException) IOException(java.io.IOException) UTXO(com.samourai.wallet.send.UTXO) MnemonicException(org.bitcoinj.crypto.MnemonicException) Transaction(org.bitcoinj.core.Transaction) BigInteger(java.math.BigInteger)

Aggregations

MyTransactionOutPoint (com.samourai.wallet.send.MyTransactionOutPoint)11 UTXO (com.samourai.wallet.send.UTXO)11 BlockedUTXO (com.samourai.wallet.send.BlockedUTXO)8 BigInteger (java.math.BigInteger)7 HashMap (java.util.HashMap)7 Transaction (org.bitcoinj.core.Transaction)7 JSONException (org.json.JSONException)7 IOException (java.io.IOException)6 ArrayList (java.util.ArrayList)6 MnemonicException (org.bitcoinj.crypto.MnemonicException)6 JSONObject (org.json.JSONObject)6 Intent (android.content.Intent)4 AddressFormatException (org.bitcoinj.core.AddressFormatException)4 TransactionInput (org.bitcoinj.core.TransactionInput)4 AlertDialog (android.app.AlertDialog)3 DialogInterface (android.content.DialogInterface)3 Point (android.graphics.Point)3 PaymentAddress (com.samourai.wallet.bip47.rpc.PaymentAddress)3 SuggestedFee (com.samourai.wallet.send.SuggestedFee)3 Script (org.bitcoinj.script.Script)3