mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-08-01 12:31:23 +00:00
Be aware of opt-in full replace-by-fee.
This commit is contained in:
@@ -24,6 +24,8 @@ import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.script.ScriptOpCodes;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
import org.bitcoinj.wallet.WalletTransaction.Pool;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
@@ -636,6 +638,9 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
}
|
||||
s.append(String.format(Locale.US, " time locked until %s%n", time));
|
||||
}
|
||||
if (isOptInFullRBF()) {
|
||||
s.append(" opts into full replace-by-fee%n");
|
||||
}
|
||||
if (inputs.size() == 0) {
|
||||
s.append(String.format(Locale.US, " INCOMPLETE: No inputs!%n"));
|
||||
return s.toString();
|
||||
@@ -675,6 +680,10 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
s.append(Utils.HEX.encode(scriptPubKey.getPubKeyHash()));
|
||||
}
|
||||
}
|
||||
String flags = Joiner.on(", ").skipNulls().join(in.hasSequence() ? "has sequence" : null,
|
||||
in.isOptInFullRBF() ? "opts into full RBF" : null);
|
||||
if (!flags.isEmpty())
|
||||
s.append("\n (").append(flags).append(')');
|
||||
} catch (Exception e) {
|
||||
s.append("[exception: ").append(e.getMessage()).append("]");
|
||||
}
|
||||
@@ -1272,6 +1281,17 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this transaction will opt into the
|
||||
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki">full replace-by-fee </a> semantics.
|
||||
*/
|
||||
public boolean isOptInFullRBF() {
|
||||
for (TransactionInput input : getInputs())
|
||||
if (input.isOptInFullRBF())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns true if this transaction is considered finalized and can be placed in a block. Non-finalized
|
||||
* transactions won't be included by miners and can be replaced with newer versions using sequence numbers.
|
||||
|
@@ -22,6 +22,8 @@ import org.bitcoinj.wallet.DefaultRiskAnalysis;
|
||||
import org.bitcoinj.wallet.KeyBag;
|
||||
import org.bitcoinj.wallet.RedeemData;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
@@ -48,7 +50,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
||||
// Magic outpoint index that indicates the input is in fact unconnected.
|
||||
private static final long UNCONNECTED = 0xFFFFFFFFL;
|
||||
|
||||
// Allows for altering transactions after they were broadcast.
|
||||
// Allows for altering transactions after they were broadcast. Values below NO_SEQUENCE-1 mean it can be altered.
|
||||
private long sequence;
|
||||
// Data needed to connect to the output of the transaction we're gathering coins from.
|
||||
private TransactionOutPoint outpoint;
|
||||
@@ -273,18 +275,6 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable debug string.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return isCoinBase() ? "TxIn: COINBASE" : "TxIn for [" + outpoint + "]: " + getScriptSig();
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConnectionResult {
|
||||
NO_SUCH_TX,
|
||||
ALREADY_SPENT,
|
||||
@@ -409,6 +399,14 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
||||
return sequence != NO_SEQUENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this input will cause a transaction to opt into the
|
||||
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki">full replace-by-fee </a> semantics.
|
||||
*/
|
||||
public boolean isOptInFullRBF() {
|
||||
return sequence < NO_SEQUENCE - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a connected transaction, runs the script against the connected pubkey and verifies they are correct.
|
||||
* @throws ScriptException if the script did not verify.
|
||||
@@ -492,4 +490,26 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
||||
result = 31 * result + (scriptSig != null ? scriptSig.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable debug string.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder("TxIn");
|
||||
try {
|
||||
if (isCoinBase()) {
|
||||
s.append(": COINBASE");
|
||||
} else {
|
||||
s.append(" for [").append(outpoint).append("]: ").append(getScriptSig());
|
||||
String flags = Joiner.on(", ").skipNulls().join(hasSequence() ? "has sequence" : null,
|
||||
isOptInFullRBF() ? "opts into full RBF" : null);
|
||||
if (!flags.isEmpty())
|
||||
s.append(" (").append(flags).append(')');
|
||||
}
|
||||
return s.toString();
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ import org.junit.Test;
|
||||
import org.easymock.EasyMock;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
|
||||
@@ -296,4 +298,14 @@ public class TransactionTest {
|
||||
|
||||
tx.addSignedInput(fakeTx.getOutput(0).getOutPointFor(), script, key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optInFullRBF() {
|
||||
// a standard transaction as wallets would create
|
||||
Transaction tx = newTransaction();
|
||||
assertFalse(tx.isOptInFullRBF());
|
||||
|
||||
tx.getInputs().get(0).setSequenceNumber(TransactionInput.NO_SEQUENCE - 2);
|
||||
assertTrue(tx.isOptInFullRBF());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user