mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-31 20:11:23 +00:00
Basic support for version 2 transactions.
Rather than considering all version 2 transactions risky, we now look more closely if any of its inputs has a relative lock time. We can't check the relative locks though, because we usually don't have the spent outputs (to know when they were creted).
This commit is contained in:
@@ -659,6 +659,9 @@ public class Transaction extends ChildMessage {
|
||||
}
|
||||
s.append('\n');
|
||||
}
|
||||
if (hasRelativeLockTime()) {
|
||||
s.append(" has relative lock time\n");
|
||||
}
|
||||
if (isOptInFullRBF()) {
|
||||
s.append(" opts into full replace-by-fee\n");
|
||||
}
|
||||
@@ -702,6 +705,8 @@ public class Transaction extends ChildMessage {
|
||||
s.append("\n sequence:").append(Long.toHexString(in.getSequenceNumber()));
|
||||
if (in.isOptInFullRBF())
|
||||
s.append(", opts into full RBF");
|
||||
if (version >=2 && in.hasRelativeLockTime())
|
||||
s.append(", has RLT");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s.append("[exception: ").append(e.getMessage()).append("]");
|
||||
@@ -1287,7 +1292,8 @@ public class Transaction extends ChildMessage {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A transaction is time locked if at least one of its inputs is non-final and it has a lock time</p>
|
||||
* <p>A transaction is time-locked if at least one of its inputs is non-final and it has a lock time. A transaction can
|
||||
* also have a relative lock time which this method doesn't tell. Use {@link #hasRelativeLockTime()} to find out.</p>
|
||||
*
|
||||
* <p>To check if this transaction is final at a given height and time, see {@link Transaction#isFinal(int, long)}
|
||||
* </p>
|
||||
@@ -1301,6 +1307,20 @@ public class Transaction extends ChildMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A transaction has a relative lock time
|
||||
* (<a href="https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki">BIP 68</a>) if it is version 2 or
|
||||
* higher and at least one of its inputs has its {@link TransactionInput.SEQUENCE_LOCKTIME_DISABLE_FLAG} cleared.
|
||||
*/
|
||||
public boolean hasRelativeLockTime() {
|
||||
if (version < 2)
|
||||
return false;
|
||||
for (TransactionInput input : getInputs())
|
||||
if (input.hasRelativeLockTime())
|
||||
return true;
|
||||
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.
|
||||
|
@@ -46,6 +46,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
public class TransactionInput extends ChildMessage {
|
||||
/** Magic sequence number that indicates there is no sequence number. */
|
||||
public static final long NO_SEQUENCE = 0xFFFFFFFFL;
|
||||
/**
|
||||
* BIP68: If this flag set, sequence is NOT interpreted as a relative lock-time.
|
||||
*/
|
||||
public static final long SEQUENCE_LOCKTIME_DISABLE_FLAG = 1L << 31;
|
||||
|
||||
private static final byte[] EMPTY_ARRAY = new byte[0];
|
||||
// Magic outpoint index that indicates the input is in fact unconnected.
|
||||
private static final long UNCONNECTED = 0xFFFFFFFFL;
|
||||
@@ -385,6 +390,14 @@ public class TransactionInput extends ChildMessage {
|
||||
return sequence < NO_SEQUENCE - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this input, if it belongs to a version 2 (or higher) transaction, has
|
||||
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki">relative lock-time</a> enabled.
|
||||
*/
|
||||
public boolean hasRelativeLockTime() {
|
||||
return (sequence & SEQUENCE_LOCKTIME_DISABLE_FLAG) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a connected transaction, runs the script against the connected pubkey and verifies they are correct.
|
||||
* @throws ScriptException if the script did not verify.
|
||||
|
@@ -89,6 +89,13 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
return Result.NON_FINAL;
|
||||
}
|
||||
|
||||
// Relative time-locked transactions are risky too. We can't check the locks because usually we don't know the
|
||||
// spent outputs (to know when they were created).
|
||||
if (tx.hasRelativeLockTime()) {
|
||||
nonFinal = tx;
|
||||
return Result.NON_FINAL;
|
||||
}
|
||||
|
||||
if (wallet == null)
|
||||
return null;
|
||||
|
||||
@@ -133,7 +140,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
*/
|
||||
public static RuleViolation isStandard(Transaction tx) {
|
||||
// TODO: Finish this function off.
|
||||
if (tx.getVersion() > 1 || tx.getVersion() < 1) {
|
||||
if (tx.getVersion() > 2 || tx.getVersion() < 1) {
|
||||
log.warn("TX considered non-standard due to unknown version number {}", tx.getVersion());
|
||||
return RuleViolation.VERSION;
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import org.junit.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.bitcoinj.core.Coin.*;
|
||||
import static org.bitcoinj.script.ScriptOpCodes.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -231,4 +232,37 @@ public class DefaultRiskAnalysisTest {
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx, analysis.getNonFinal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void relativeLockTime() throws Exception {
|
||||
Transaction tx = FakeTxBuilder.createFakeTx(PARAMS);
|
||||
tx.setVersion(2);
|
||||
checkState(!tx.hasRelativeLockTime());
|
||||
|
||||
tx.getInput(0).setSequenceNumber(TransactionInput.NO_SEQUENCE);
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
|
||||
tx.getInput(0).setSequenceNumber(0);
|
||||
analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx, analysis.getNonFinal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionVersions() throws Exception {
|
||||
Transaction tx = FakeTxBuilder.createFakeTx(PARAMS);
|
||||
tx.setVersion(1);
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
|
||||
tx.setVersion(2);
|
||||
analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
|
||||
tx.setVersion(3);
|
||||
analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.NON_STANDARD, analysis.analyze());
|
||||
assertEquals(tx, analysis.getNonStandard());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user