mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2024-11-20 02:09:29 +01:00
Fix a number of issues with message length caching.
This commit is contained in:
parent
deb9da67aa
commit
f2d9a6162d
@ -739,7 +739,7 @@ public class Block extends Message {
|
|||||||
else if (transactions.size() > 0 && t.isCoinBase())
|
else if (transactions.size() > 0 && t.isCoinBase())
|
||||||
throw new RuntimeException("Attempted to add a coinbase transaction when there already is one: " + t);
|
throw new RuntimeException("Attempted to add a coinbase transaction when there already is one: " + t);
|
||||||
transactions.add(t);
|
transactions.add(t);
|
||||||
adjustLength(t.length);
|
adjustLength(transactions.size(), t.length);
|
||||||
// Force a recalculation next time the values are needed.
|
// Force a recalculation next time the values are needed.
|
||||||
merkleRoot = null;
|
merkleRoot = null;
|
||||||
hash = null;
|
hash = null;
|
||||||
@ -842,6 +842,9 @@ public class Block extends Message {
|
|||||||
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++}));
|
coinbase.addInput(new TransactionInput(params, coinbase, new byte[]{(byte) txCounter++}));
|
||||||
coinbase.addOutput(new TransactionOutput(params, coinbase, Script.createOutputScript(pubKeyTo)));
|
coinbase.addOutput(new TransactionOutput(params, coinbase, Script.createOutputScript(pubKeyTo)));
|
||||||
transactions.add(coinbase);
|
transactions.add(coinbase);
|
||||||
|
coinbase.setParent(this);
|
||||||
|
coinbase.length = coinbase.bitcoinSerialize().length;
|
||||||
|
adjustLength(transactions.size(), coinbase.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final byte[] EMPTY_BYTES = new byte[32];
|
static final byte[] EMPTY_BYTES = new byte[32];
|
||||||
|
@ -74,10 +74,13 @@ public abstract class ChildMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void adjustLength(int adjustment) {
|
protected void adjustLength(int adjustment) {
|
||||||
if (length != UNKNOWN_LENGTH)
|
adjustLength(0, adjustment);
|
||||||
length += adjustment;
|
}
|
||||||
|
|
||||||
|
protected void adjustLength(int newArraySize, int adjustment) {
|
||||||
|
super.adjustLength(newArraySize, adjustment);
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
parent.adjustLength(adjustment);
|
parent.adjustLength(newArraySize, adjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public abstract class Message implements Serializable {
|
|||||||
|
|
||||||
public static final int MAX_SIZE = 0x02000000;
|
public static final int MAX_SIZE = 0x02000000;
|
||||||
|
|
||||||
public static final int UNKNOWN_LENGTH = -1;
|
public static final int UNKNOWN_LENGTH = Integer.MIN_VALUE;
|
||||||
|
|
||||||
// Useful to ensure serialize/deserialize are consistent with each other.
|
// Useful to ensure serialize/deserialize are consistent with each other.
|
||||||
private static final boolean SELF_CHECK = false;
|
private static final boolean SELF_CHECK = false;
|
||||||
@ -221,10 +221,20 @@ public abstract class Message implements Serializable {
|
|||||||
recached = false;
|
recached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void adjustLength(int adjustment) {
|
protected void adjustLength(int newArraySize, int adjustment) {
|
||||||
if (length != UNKNOWN_LENGTH)
|
if (length == UNKNOWN_LENGTH)
|
||||||
|
return;
|
||||||
// Our own length is now unknown if we have an unknown length adjustment.
|
// Our own length is now unknown if we have an unknown length adjustment.
|
||||||
length = adjustment == UNKNOWN_LENGTH ? UNKNOWN_LENGTH : length + adjustment;
|
if (adjustment == UNKNOWN_LENGTH) {
|
||||||
|
length = UNKNOWN_LENGTH;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
length += adjustment;
|
||||||
|
// Check if we will need more bytes to encode the length prefix.
|
||||||
|
if (newArraySize == 1)
|
||||||
|
length++; // The assumption here is we never call adjustLength with the same arraySize as before.
|
||||||
|
else if (newArraySize != 0)
|
||||||
|
length += VarInt.sizeOf(newArraySize) - VarInt.sizeOf(newArraySize - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,7 +83,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
inputs = new ArrayList<TransactionInput>();
|
inputs = new ArrayList<TransactionInput>();
|
||||||
outputs = new ArrayList<TransactionOutput>();
|
outputs = new ArrayList<TransactionOutput>();
|
||||||
// We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet.
|
// We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet.
|
||||||
length = 10; // 8 for std fields + 1 for each 0 varint
|
length = 8; // 8 for std fields
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transaction(NetworkParameters params, int version, Sha256Hash hash) {
|
public Transaction(NetworkParameters params, int version, Sha256Hash hash) {
|
||||||
@ -93,7 +93,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
outputs = new ArrayList<TransactionOutput>();
|
outputs = new ArrayList<TransactionOutput>();
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
// We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet.
|
// We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet.
|
||||||
length = 10; //8 for std fields + 1 for each 0 varint
|
length = 8; //8 for std fields
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -609,7 +609,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
unCache();
|
unCache();
|
||||||
input.setParent(this);
|
input.setParent(this);
|
||||||
inputs.add(input);
|
inputs.add(input);
|
||||||
adjustLength(input.length);
|
adjustLength(inputs.size(), input.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -619,7 +619,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
unCache();
|
unCache();
|
||||||
to.setParent(this);
|
to.setParent(this);
|
||||||
outputs.add(to);
|
outputs.add(to);
|
||||||
adjustLength(to.length);
|
adjustLength(outputs.size(), to.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,4 +149,37 @@ public class BlockTest {
|
|||||||
// of this test is to ensure no errors occur during the Java serialization/deserialization process.
|
// of this test is to ensure no errors occur during the Java serialization/deserialization process.
|
||||||
assertEquals(tx, tx2);
|
assertEquals(tx, tx2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLength() {
|
||||||
|
NetworkParameters params = NetworkParameters.unitTests();
|
||||||
|
Block block = params.genesisBlock.createNextBlockWithCoinbase(new ECKey().getPubKey());
|
||||||
|
assertEquals(block.bitcoinSerialize().length, block.length);
|
||||||
|
final int origBlockLen = block.length;
|
||||||
|
Transaction tx = new Transaction(params);
|
||||||
|
// this is broken until the transaction has > 1 input + output (which is required anyway...)
|
||||||
|
//assertTrue(tx.length == tx.bitcoinSerialize().length && tx.length == 8);
|
||||||
|
byte[] outputScript = new byte[10];
|
||||||
|
Arrays.fill(outputScript, (byte)Script.OP_FALSE);
|
||||||
|
tx.addOutput(new TransactionOutput(params, null, BigInteger.valueOf(1), outputScript));
|
||||||
|
tx.addInput(new TransactionInput(params, null, new byte[] {(byte)Script.OP_FALSE},
|
||||||
|
new TransactionOutPoint(params, 0, Sha256Hash.create(new byte[] {1}))));
|
||||||
|
int origTxLength = 8 + 2 + 8 + 1 + 10 + 40 + 1 + 1;
|
||||||
|
assertEquals(tx.bitcoinSerialize().length, tx.length);
|
||||||
|
assertEquals(origTxLength, tx.length);
|
||||||
|
block.addTransaction(tx);
|
||||||
|
assertEquals(block.bitcoinSerialize().length, block.length);
|
||||||
|
assertEquals(origBlockLen + tx.length, block.length);
|
||||||
|
block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {(byte)Script.OP_FALSE, (byte)Script.OP_FALSE});
|
||||||
|
assertEquals(block.length, origBlockLen + tx.length);
|
||||||
|
assertEquals(tx.length, origTxLength + 1);
|
||||||
|
block.getTransactions().get(1).getInputs().get(0).setScriptBytes(new byte[] {});
|
||||||
|
assertEquals(block.length, block.bitcoinSerialize().length);
|
||||||
|
assertEquals(block.length, origBlockLen + tx.length);
|
||||||
|
assertEquals(tx.length, origTxLength - 1);
|
||||||
|
block.getTransactions().get(1).addInput(new TransactionInput(params, null, new byte[] {(byte)Script.OP_FALSE},
|
||||||
|
new TransactionOutPoint(params, 0, Sha256Hash.create(new byte[] {1}))));
|
||||||
|
assertEquals(block.length, origBlockLen + tx.length);
|
||||||
|
assertEquals(tx.length, origTxLength + 41); // - 1 + 40 + 1 + 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user