Search in sources :

Example 1 with BIP69OutputComparator

use of com.samourai.wallet.bip69.BIP69OutputComparator in project samourai-wallet-android by Samourai-Wallet.

the class Stowaway method doStep3.

// 
// receiver
// 
public boolean doStep3(HashMap<String, ECKey> keyBag) {
    Transaction transaction = this.getTransaction();
    List<TransactionInput> inputs = new ArrayList<TransactionInput>();
    inputs.addAll(transaction.getInputs());
    Collections.sort(inputs, new BIP69InputComparator());
    transaction.clearInputs();
    for (TransactionInput input : inputs) {
        transaction.addInput(input);
    }
    List<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
    outputs.addAll(transaction.getOutputs());
    Collections.sort(outputs, new BIP69OutputComparator());
    transaction.clearOutputs();
    for (TransactionOutput output : outputs) {
        transaction.addOutput(output);
    }
    psbt.setTransaction(transaction);
    signTx(keyBag);
    this.setStep(3);
    return true;
}
Also used : BIP69InputComparator(com.samourai.wallet.bip69.BIP69InputComparator) BIP69OutputComparator(com.samourai.wallet.bip69.BIP69OutputComparator)

Example 2 with BIP69OutputComparator

use of com.samourai.wallet.bip69.BIP69OutputComparator in project samourai-wallet-android by Samourai-Wallet.

the class RBFTask method doInBackground.

@Override
protected String doInBackground(final String... params) {
    Looper.prepare();
    Log.d("RBF", "hash:" + params[0]);
    rbf = RBFUtil.getInstance().get(params[0]);
    Log.d("RBF", "rbf:" + rbf.toJSON().toString());
    final Transaction tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams(), Hex.decode(rbf.getSerializedTx()));
    Log.d("RBF", "tx serialized:" + rbf.getSerializedTx());
    Log.d("RBF", "tx inputs:" + tx.getInputs().size());
    Log.d("RBF", "tx outputs:" + tx.getOutputs().size());
    JSONObject txObj = APIFactory.getInstance(activity).getTxInfo(params[0]);
    if (tx != null && txObj.has("inputs") && txObj.has("outputs")) {
        try {
            JSONArray inputs = txObj.getJSONArray("inputs");
            JSONArray outputs = txObj.getJSONArray("outputs");
            int p2pkh = 0;
            int p2sh_p2wpkh = 0;
            int p2wpkh = 0;
            for (int i = 0; i < inputs.length(); i++) {
                if (inputs.getJSONObject(i).has("outpoint") && inputs.getJSONObject(i).getJSONObject("outpoint").has("scriptpubkey")) {
                    String scriptpubkey = inputs.getJSONObject(i).getJSONObject("outpoint").getString("scriptpubkey");
                    Script script = new Script(Hex.decode(scriptpubkey));
                    String address = null;
                    if (Bech32Util.getInstance().isBech32Script(scriptpubkey)) {
                        try {
                            address = Bech32Util.getInstance().getAddressFromScript(scriptpubkey);
                        } catch (Exception e) {
                            ;
                        }
                    } else {
                        address = script.getToAddress(SamouraiWallet.getInstance().getCurrentNetworkParams()).toString();
                    }
                    if (FormatsUtil.getInstance().isValidBech32(address)) {
                        p2wpkh++;
                    } else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), address).isP2SHAddress()) {
                        p2sh_p2wpkh++;
                    } else {
                        p2pkh++;
                    }
                }
            }
            SuggestedFee suggestedFee = FeeUtil.getInstance().getSuggestedFee();
            FeeUtil.getInstance().setSuggestedFee(FeeUtil.getInstance().getHighFee());
            BigInteger estimatedFee = FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, outputs.length());
            long total_inputs = 0L;
            long total_outputs = 0L;
            long fee = 0L;
            long total_change = 0L;
            List<String> selfAddresses = new ArrayList<String>();
            for (int i = 0; i < inputs.length(); i++) {
                JSONObject obj = inputs.getJSONObject(i);
                if (obj.has("outpoint")) {
                    JSONObject objPrev = obj.getJSONObject("outpoint");
                    if (objPrev.has("value")) {
                        total_inputs += objPrev.getLong("value");
                        String key = objPrev.getString("txid") + ":" + objPrev.getLong("vout");
                        input_values.put(key, objPrev.getLong("value"));
                    }
                }
            }
            for (int i = 0; i < outputs.length(); i++) {
                JSONObject obj = outputs.getJSONObject(i);
                if (obj.has("value")) {
                    total_outputs += obj.getLong("value");
                    String _addr = null;
                    if (obj.has("address")) {
                        _addr = obj.getString("address");
                    }
                    selfAddresses.add(_addr);
                    if (_addr != null && rbf.getChangeAddrs().contains(_addr.toString())) {
                        total_change += obj.getLong("value");
                    }
                }
            }
            boolean feeWarning = false;
            fee = total_inputs - total_outputs;
            if (fee > estimatedFee.longValue()) {
                feeWarning = true;
            }
            long remainingFee = (estimatedFee.longValue() > fee) ? estimatedFee.longValue() - fee : 0L;
            Log.d("RBF", "total inputs:" + total_inputs);
            Log.d("RBF", "total outputs:" + total_outputs);
            Log.d("RBF", "total change:" + total_change);
            Log.d("RBF", "fee:" + fee);
            Log.d("RBF", "estimated fee:" + estimatedFee.longValue());
            Log.d("RBF", "fee warning:" + feeWarning);
            Log.d("RBF", "remaining fee:" + remainingFee);
            List<TransactionOutput> txOutputs = new ArrayList<TransactionOutput>();
            txOutputs.addAll(tx.getOutputs());
            long remainder = remainingFee;
            if (total_change > remainder) {
                for (TransactionOutput output : txOutputs) {
                    Script script = output.getScriptPubKey();
                    String scriptPubKey = Hex.toHexString(script.getProgram());
                    Address _p2sh = output.getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams());
                    Address _p2pkh = output.getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams());
                    try {
                        if ((Bech32Util.getInstance().isBech32Script(scriptPubKey) && rbf.getChangeAddrs().contains(Bech32Util.getInstance().getAddressFromScript(scriptPubKey))) || (_p2sh != null && rbf.getChangeAddrs().contains(_p2sh.toString())) || (_p2pkh != null && rbf.getChangeAddrs().contains(_p2pkh.toString()))) {
                            if (output.getValue().longValue() >= (remainder + SamouraiWallet.bDust.longValue())) {
                                output.setValue(Coin.valueOf(output.getValue().longValue() - remainder));
                                remainder = 0L;
                                break;
                            } else {
                                remainder -= output.getValue().longValue();
                                // output will be discarded later
                                output.setValue(Coin.valueOf(0L));
                            }
                        }
                    } catch (Exception e) {
                        ;
                    }
                }
            }
            // 
            // original inputs are not modified
            // 
            List<MyTransactionInput> _inputs = new ArrayList<MyTransactionInput>();
            List<TransactionInput> txInputs = tx.getInputs();
            for (TransactionInput input : txInputs) {
                MyTransactionInput _input = new MyTransactionInput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, new byte[0], input.getOutpoint(), input.getOutpoint().getHash().toString(), (int) input.getOutpoint().getIndex());
                _input.setSequenceNumber(SamouraiWallet.RBF_SEQUENCE_VAL.longValue());
                _inputs.add(_input);
                Log.d("RBF", "add outpoint:" + _input.getOutpoint().toString());
            }
            Triple<Integer, Integer, Integer> outpointTypes = null;
            if (remainder > 0L) {
                List<UTXO> selectedUTXO = new ArrayList<UTXO>();
                long selectedAmount = 0L;
                int selected = 0;
                long _remainingFee = remainder;
                Collections.sort(utxos, new UTXO.UTXOComparator());
                for (UTXO _utxo : utxos) {
                    Log.d("RBF", "utxo value:" + _utxo.getValue());
                    // 
                    // do not select utxo that are change outputs in current rbf tx
                    // 
                    boolean isChange = false;
                    boolean isSelf = false;
                    for (MyTransactionOutPoint outpoint : _utxo.getOutpoints()) {
                        if (rbf.containsChangeAddr(outpoint.getAddress())) {
                            Log.d("RBF", "is change:" + outpoint.getAddress());
                            Log.d("RBF", "is change:" + outpoint.getValue().longValue());
                            isChange = true;
                            break;
                        }
                        if (selfAddresses.contains(outpoint.getAddress())) {
                            Log.d("RBF", "is self:" + outpoint.getAddress());
                            Log.d("RBF", "is self:" + outpoint.getValue().longValue());
                            isSelf = true;
                            break;
                        }
                    }
                    if (isChange || isSelf) {
                        continue;
                    }
                    selectedUTXO.add(_utxo);
                    selected += _utxo.getOutpoints().size();
                    Log.d("RBF", "selected utxo:" + selected);
                    selectedAmount += _utxo.getValue();
                    Log.d("RBF", "selected utxo value:" + _utxo.getValue());
                    outpointTypes = FeeUtil.getInstance().getOutpointCount(new Vector(_utxo.getOutpoints()));
                    p2pkh += outpointTypes.getLeft();
                    p2sh_p2wpkh += outpointTypes.getMiddle();
                    p2wpkh += outpointTypes.getRight();
                    _remainingFee = FeeUtil.getInstance().estimatedFeeSegwit(p2pkh, p2sh_p2wpkh, p2wpkh, outputs.length() == 1 ? 2 : outputs.length()).longValue();
                    Log.d("RBF", "_remaining fee:" + _remainingFee);
                    if (selectedAmount >= (_remainingFee + SamouraiWallet.bDust.longValue())) {
                        break;
                    }
                }
                long extraChange = 0L;
                if (selectedAmount < (_remainingFee + SamouraiWallet.bDust.longValue())) {
                    handler.post(new Runnable() {

                        public void run() {
                            Toast.makeText(activity, R.string.insufficient_funds, Toast.LENGTH_SHORT).show();
                        }
                    });
                    return "KO";
                } else {
                    extraChange = selectedAmount - _remainingFee;
                    Log.d("RBF", "extra change:" + extraChange);
                }
                boolean addedChangeOutput = false;
                // parent tx didn't have change output
                if (outputs.length() == 1 && extraChange > 0L) {
                    try {
                        boolean isSegwitChange = (FormatsUtil.getInstance().isValidBech32(outputs.getJSONObject(0).getString("address")) || Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), outputs.getJSONObject(0).getString("address")).isP2SHAddress()) || PrefsUtil.getInstance(activity).getValue(PrefsUtil.USE_LIKE_TYPED_CHANGE, true) == false;
                        String change_address = null;
                        if (isSegwitChange) {
                            int changeIdx = BIP49Util.getInstance(activity).getWallet().getAccount(0).getChange().getAddrIdx();
                            change_address = BIP49Util.getInstance(activity).getAddressAt(AddressFactory.CHANGE_CHAIN, changeIdx).getAddressAsString();
                        } else {
                            int changeIdx = HD_WalletFactory.getInstance(activity).get().getAccount(0).getChange().getAddrIdx();
                            change_address = HD_WalletFactory.getInstance(activity).get().getAccount(0).getChange().getAddressAt(changeIdx).getAddressString();
                        }
                        Script toOutputScript = ScriptBuilder.createOutputScript(Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), change_address));
                        TransactionOutput output = new TransactionOutput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, Coin.valueOf(extraChange), toOutputScript.getProgram());
                        txOutputs.add(output);
                        addedChangeOutput = true;
                    } catch (MnemonicException.MnemonicLengthException | IOException e) {
                        handler.post(new Runnable() {

                            public void run() {
                                Toast.makeText(activity, e.getMessage(), Toast.LENGTH_SHORT).show();
                                Toast.makeText(activity, R.string.cannot_create_change_output, Toast.LENGTH_SHORT).show();
                            }
                        });
                        return "KO";
                    }
                } else // parent tx had change output
                {
                    for (TransactionOutput output : txOutputs) {
                        Script script = output.getScriptPubKey();
                        String scriptPubKey = Hex.toHexString(script.getProgram());
                        String _addr = null;
                        if (Bech32Util.getInstance().isBech32Script(scriptPubKey)) {
                            try {
                                _addr = Bech32Util.getInstance().getAddressFromScript(scriptPubKey);
                            } catch (Exception e) {
                                ;
                            }
                        }
                        if (_addr == null) {
                            Address _address = output.getAddressFromP2PKHScript(SamouraiWallet.getInstance().getCurrentNetworkParams());
                            if (_address == null) {
                                _address = output.getAddressFromP2SH(SamouraiWallet.getInstance().getCurrentNetworkParams());
                            }
                            _addr = _address.toString();
                        }
                        Log.d("RBF", "checking for change:" + _addr);
                        if (rbf.containsChangeAddr(_addr)) {
                            Log.d("RBF", "before extra:" + output.getValue().longValue());
                            output.setValue(Coin.valueOf(extraChange + output.getValue().longValue()));
                            Log.d("RBF", "after extra:" + output.getValue().longValue());
                            addedChangeOutput = true;
                            break;
                        }
                    }
                }
                // sanity check
                if (extraChange > 0L && !addedChangeOutput) {
                    handler.post(new Runnable() {

                        public void run() {
                            Toast.makeText(activity, R.string.cannot_create_change_output, Toast.LENGTH_SHORT).show();
                        }
                    });
                    return "KO";
                }
                // 
                // update keyBag w/ any new paths
                // 
                final HashMap<String, String> keyBag = rbf.getKeyBag();
                for (UTXO _utxo : selectedUTXO) {
                    for (MyTransactionOutPoint outpoint : _utxo.getOutpoints()) {
                        MyTransactionInput _input = new MyTransactionInput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, new byte[0], outpoint, outpoint.getTxHash().toString(), outpoint.getTxOutputN());
                        _input.setSequenceNumber(SamouraiWallet.RBF_SEQUENCE_VAL.longValue());
                        _inputs.add(_input);
                        Log.d("RBF", "add selected outpoint:" + _input.getOutpoint().toString());
                        String path = APIFactory.getInstance(activity).getUnspentPaths().get(outpoint.getAddress());
                        if (path != null) {
                            if (FormatsUtil.getInstance().isValidBech32(outpoint.getAddress())) {
                                rbf.addKey(outpoint.toString(), path + "/84");
                            } else if (Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), outpoint.getAddress()) != null && Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), outpoint.getAddress()).isP2SHAddress()) {
                                rbf.addKey(outpoint.toString(), path + "/49");
                            } else {
                                rbf.addKey(outpoint.toString(), path);
                            }
                            Log.d("RBF", "outpoint address:" + outpoint.getAddress());
                        } else {
                            String pcode = BIP47Meta.getInstance().getPCode4Addr(outpoint.getAddress());
                            int idx = BIP47Meta.getInstance().getIdx4Addr(outpoint.getAddress());
                            rbf.addKey(outpoint.toString(), pcode + "/" + idx);
                        }
                    }
                }
                rbf.setKeyBag(keyBag);
            }
            // 
            // BIP69 sort of outputs/inputs
            // 
            final Transaction _tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams());
            List<TransactionOutput> _txOutputs = new ArrayList<TransactionOutput>();
            _txOutputs.addAll(txOutputs);
            Collections.sort(_txOutputs, new BIP69OutputComparator());
            for (TransactionOutput to : _txOutputs) {
                // zero value outputs discarded here
                if (to.getValue().longValue() > 0L) {
                    _tx.addOutput(to);
                }
            }
            List<MyTransactionInput> __inputs = new ArrayList<MyTransactionInput>();
            __inputs.addAll(_inputs);
            Collections.sort(__inputs, new SendFactory.BIP69InputComparator());
            for (TransactionInput input : __inputs) {
                _tx.addInput(input);
            }
            FeeUtil.getInstance().setSuggestedFee(suggestedFee);
            String message = "";
            if (feeWarning) {
                message += activity.getString(R.string.fee_bump_not_necessary);
                message += "\n\n";
            }
            message += activity.getString(R.string.bump_fee) + " " + Coin.valueOf(remainingFee).toPlainString() + " BTC";
            AlertDialog.Builder dlg = new AlertDialog.Builder(activity).setTitle(R.string.app_name).setMessage(message).setCancelable(false).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int whichButton) {
                    Transaction __tx = signTx(_tx);
                    final String hexTx = new String(Hex.encode(__tx.bitcoinSerialize()));
                    Log.d("RBF", "hex tx:" + hexTx);
                    final String strTxHash = __tx.getHashAsString();
                    Log.d("RBF", "tx hash:" + strTxHash);
                    if (__tx != null) {
                        boolean isOK = false;
                        try {
                            isOK = PushTx.getInstance(activity).pushTx(hexTx);
                            if (isOK) {
                                handler.post(new Runnable() {

                                    public void run() {
                                        Toast.makeText(activity, R.string.rbf_spent, Toast.LENGTH_SHORT).show();
                                        // includes updated 'keyBag'
                                        RBFSpend _rbf = rbf;
                                        _rbf.setSerializedTx(hexTx);
                                        _rbf.setHash(strTxHash);
                                        _rbf.setPrevHash(params[0]);
                                        RBFUtil.getInstance().add(_rbf);
                                        Intent _intent = new Intent(activity, MainActivity2.class);
                                        _intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                                        activity.startActivity(_intent);
                                    }
                                });
                            } else {
                                handler.post(new Runnable() {

                                    public void run() {
                                        Toast.makeText(activity, R.string.tx_failed, Toast.LENGTH_SHORT).show();
                                    }
                                });
                            }
                        } catch (final DecoderException de) {
                            handler.post(new Runnable() {

                                public void run() {
                                    Toast.makeText(activity, "pushTx:" + de.getMessage(), Toast.LENGTH_SHORT).show();
                                }
                            });
                        } finally {
                            ;
                        }
                    }
                }
            }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int whichButton) {
                    dialog.dismiss();
                }
            });
            if (!activity.isFinishing()) {
                dlg.show();
            }
        } catch (final JSONException je) {
            handler.post(new Runnable() {

                public void run() {
                    Toast.makeText(activity, "rbf:" + je.getMessage(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    } else {
        Toast.makeText(activity, R.string.cpfp_cannot_retrieve_tx, Toast.LENGTH_SHORT).show();
    }
    Looper.loop();
    return "OK";
}
Also used : AlertDialog(android.app.AlertDialog) TransactionOutput(org.bitcoinj.core.TransactionOutput) Address(org.bitcoinj.core.Address) HD_Address(com.samourai.wallet.hd.HD_Address) PaymentAddress(com.samourai.wallet.bip47.rpc.PaymentAddress) SegwitAddress(com.samourai.wallet.segwit.SegwitAddress) DialogInterface(android.content.DialogInterface) ArrayList(java.util.ArrayList) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) MyTransactionInput(com.samourai.wallet.send.MyTransactionInput) TransactionInput(org.bitcoinj.core.TransactionInput) MainActivity2(com.samourai.wallet.MainActivity2) BIP69OutputComparator(com.samourai.wallet.bip69.BIP69OutputComparator) RBFSpend(com.samourai.wallet.send.RBFSpend) Vector(java.util.Vector) Script(org.bitcoinj.script.Script) SendFactory(com.samourai.wallet.send.SendFactory) SuggestedFee(com.samourai.wallet.send.SuggestedFee) JSONArray(org.json.JSONArray) JSONException(org.json.JSONException) Intent(android.content.Intent) IOException(java.io.IOException) MyTransactionOutPoint(com.samourai.wallet.send.MyTransactionOutPoint) JSONException(org.json.JSONException) DecoderException(org.bouncycastle.util.encoders.DecoderException) MnemonicException(org.bitcoinj.crypto.MnemonicException) IOException(java.io.IOException) BigInteger(java.math.BigInteger) UTXO(com.samourai.wallet.send.UTXO) DecoderException(org.bouncycastle.util.encoders.DecoderException) Transaction(org.bitcoinj.core.Transaction) JSONObject(org.json.JSONObject) MyTransactionInput(com.samourai.wallet.send.MyTransactionInput) BigInteger(java.math.BigInteger)

Example 3 with BIP69OutputComparator

use of com.samourai.wallet.bip69.BIP69OutputComparator in project samourai-wallet-android by Samourai-Wallet.

the class SendFactory method makeTransaction.

/*
    Used by spends
     */
private Transaction makeTransaction(int accountIdx, HashMap<String, BigInteger> receivers, List<MyTransactionOutPoint> unspent) throws Exception {
    BigInteger amount = BigInteger.ZERO;
    for (Iterator<Map.Entry<String, BigInteger>> iterator = receivers.entrySet().iterator(); iterator.hasNext(); ) {
        Map.Entry<String, BigInteger> mapEntry = iterator.next();
        amount = amount.add(mapEntry.getValue());
    }
    List<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
    Transaction tx = new Transaction(SamouraiWallet.getInstance().getCurrentNetworkParams());
    for (Iterator<Map.Entry<String, BigInteger>> iterator = receivers.entrySet().iterator(); iterator.hasNext(); ) {
        Map.Entry<String, BigInteger> mapEntry = iterator.next();
        String toAddress = mapEntry.getKey();
        BigInteger value = mapEntry.getValue();
        /*
            if(value.compareTo(SamouraiWallet.bDust) < 1)    {
                throw new Exception(context.getString(R.string.dust_amount));
            }
*/
        if (value == null || (value.compareTo(BigInteger.ZERO) <= 0 && !FormatsUtil.getInstance().isValidBIP47OpReturn(toAddress))) {
            throw new Exception(context.getString(R.string.invalid_amount));
        }
        TransactionOutput output = null;
        Script toOutputScript = null;
        if (!FormatsUtil.getInstance().isValidBitcoinAddress(toAddress) && FormatsUtil.getInstance().isValidBIP47OpReturn(toAddress)) {
            toOutputScript = new ScriptBuilder().op(ScriptOpCodes.OP_RETURN).data(Hex.decode(toAddress)).build();
            output = new TransactionOutput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, Coin.valueOf(0L), toOutputScript.getProgram());
        } else if (FormatsUtil.getInstance().isValidBech32(toAddress)) {
            output = Bech32Util.getInstance().getTransactionOutput(toAddress, value.longValue());
        } else {
            toOutputScript = ScriptBuilder.createOutputScript(org.bitcoinj.core.Address.fromBase58(SamouraiWallet.getInstance().getCurrentNetworkParams(), toAddress));
            output = new TransactionOutput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, Coin.valueOf(value.longValue()), toOutputScript.getProgram());
        }
        outputs.add(output);
    }
    List<MyTransactionInput> inputs = new ArrayList<MyTransactionInput>();
    for (MyTransactionOutPoint outPoint : unspent) {
        Script script = new Script(outPoint.getScriptBytes());
        if (script.getScriptType() == Script.ScriptType.NO_TYPE) {
            continue;
        }
        MyTransactionInput input = new MyTransactionInput(SamouraiWallet.getInstance().getCurrentNetworkParams(), null, new byte[0], outPoint, outPoint.getTxHash().toString(), outPoint.getTxOutputN());
        if (PrefsUtil.getInstance(context).getValue(PrefsUtil.RBF_OPT_IN, false) == true) {
            input.setSequenceNumber(SamouraiWallet.RBF_SEQUENCE_VAL.longValue());
        }
        inputs.add(input);
    }
    // 
    // deterministically sort inputs and outputs, see BIP69 (OBPP)
    // 
    Collections.sort(inputs, new BIP69InputComparator());
    for (TransactionInput input : inputs) {
        tx.addInput(input);
    }
    Collections.sort(outputs, new BIP69OutputComparator());
    for (TransactionOutput to : outputs) {
        tx.addOutput(to);
    }
    return tx;
}
Also used : Script(org.bitcoinj.script.Script) TransactionOutput(org.bitcoinj.core.TransactionOutput) ArrayList(java.util.ArrayList) ScriptBuilder(org.bitcoinj.script.ScriptBuilder) ScriptException(org.bitcoinj.script.ScriptException) AddressFormatException(org.bitcoinj.core.AddressFormatException) MnemonicException(org.bitcoinj.crypto.MnemonicException) IOException(java.io.IOException) TransactionInput(org.bitcoinj.core.TransactionInput) Transaction(org.bitcoinj.core.Transaction) BIP69OutputComparator(com.samourai.wallet.bip69.BIP69OutputComparator) BigInteger(java.math.BigInteger) Map(java.util.Map) HashMap(java.util.HashMap)

Example 4 with BIP69OutputComparator

use of com.samourai.wallet.bip69.BIP69OutputComparator in project samourai-wallet-android by Samourai-Wallet.

the class STONEWALLx2 method doStep3.

// 
// counterparty
// 
protected boolean doStep3(HashMap<String, ECKey> keyBag) {
    Transaction transaction = this.getTransaction();
    List<TransactionInput> inputs = new ArrayList<TransactionInput>();
    inputs.addAll(transaction.getInputs());
    Collections.sort(inputs, new BIP69InputComparator());
    transaction.clearInputs();
    for (TransactionInput input : inputs) {
        transaction.addInput(input);
    }
    List<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
    outputs.addAll(transaction.getOutputs());
    Collections.sort(outputs, new BIP69OutputComparator());
    transaction.clearOutputs();
    for (TransactionOutput output : outputs) {
        transaction.addOutput(output);
    }
    psbt.setTransaction(transaction);
    signTx(keyBag);
    this.setStep(3);
    return true;
}
Also used : BIP69InputComparator(com.samourai.wallet.bip69.BIP69InputComparator) BIP69OutputComparator(com.samourai.wallet.bip69.BIP69OutputComparator)

Aggregations

BIP69OutputComparator (com.samourai.wallet.bip69.BIP69OutputComparator)4 BIP69InputComparator (com.samourai.wallet.bip69.BIP69InputComparator)2 IOException (java.io.IOException)2 BigInteger (java.math.BigInteger)2 ArrayList (java.util.ArrayList)2 Transaction (org.bitcoinj.core.Transaction)2 TransactionInput (org.bitcoinj.core.TransactionInput)2 TransactionOutput (org.bitcoinj.core.TransactionOutput)2 MnemonicException (org.bitcoinj.crypto.MnemonicException)2 Script (org.bitcoinj.script.Script)2 AlertDialog (android.app.AlertDialog)1 DialogInterface (android.content.DialogInterface)1 Intent (android.content.Intent)1 MainActivity2 (com.samourai.wallet.MainActivity2)1 PaymentAddress (com.samourai.wallet.bip47.rpc.PaymentAddress)1 HD_Address (com.samourai.wallet.hd.HD_Address)1 SegwitAddress (com.samourai.wallet.segwit.SegwitAddress)1 MyTransactionInput (com.samourai.wallet.send.MyTransactionInput)1 MyTransactionOutPoint (com.samourai.wallet.send.MyTransactionOutPoint)1 RBFSpend (com.samourai.wallet.send.RBFSpend)1