mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +00:00
Give Blocks/Transactions an idea of their optimally-encoded size.
In the case of Blocks/Transactions which are encoded using VarInts which are not optimally encoded, we need to compare MAX_BLOCK_SIZE with the optimally encoded size, not the actually encoded size.
This commit is contained in:
parent
009939f9be
commit
810b03dd0a
@ -90,6 +90,11 @@ public class Block extends Message {
|
||||
|
||||
private transient boolean headerBytesValid;
|
||||
private transient boolean transactionBytesValid;
|
||||
|
||||
// Blocks can be encoded in a way that will use more bytes than is optimal (due to VarInts having multiple encodings)
|
||||
// MAX_BLOCK_SIZE must be compared to the optimal encoding, not the actual encoding, so when parsing, we keep track
|
||||
// of the size of the ideal encoding in addition to the actual message size (which Message needs)
|
||||
private transient int optimalEncodingMessageSize;
|
||||
|
||||
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
|
||||
Block(NetworkParameters params) {
|
||||
@ -167,6 +172,7 @@ public class Block extends Message {
|
||||
return;
|
||||
|
||||
cursor = offset + HEADER_SIZE;
|
||||
optimalEncodingMessageSize = HEADER_SIZE;
|
||||
if (bytes.length == cursor) {
|
||||
// This message is just a header, it has no transactions.
|
||||
transactionsParsed = true;
|
||||
@ -175,11 +181,13 @@ public class Block extends Message {
|
||||
}
|
||||
|
||||
int numTransactions = (int) readVarInt();
|
||||
optimalEncodingMessageSize += VarInt.sizeOf(numTransactions);
|
||||
transactions = new ArrayList<Transaction>(numTransactions);
|
||||
for (int i = 0; i < numTransactions; i++) {
|
||||
Transaction tx = new Transaction(params, bytes, cursor, this, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
||||
transactions.add(tx);
|
||||
cursor += tx.getMessageSize();
|
||||
optimalEncodingMessageSize += tx.getOptimalEncodingMessageSize();
|
||||
}
|
||||
// No need to set length here. If length was not provided then it should be set at the end of parseLight().
|
||||
// If this is a genuine lazy parse then length must have been provided to the constructor.
|
||||
@ -192,6 +200,16 @@ public class Block extends Message {
|
||||
parseTransactions();
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
public int getOptimalEncodingMessageSize() {
|
||||
if (optimalEncodingMessageSize != 0)
|
||||
return optimalEncodingMessageSize;
|
||||
maybeParseTransactions();
|
||||
if (optimalEncodingMessageSize != 0)
|
||||
return optimalEncodingMessageSize;
|
||||
optimalEncodingMessageSize = getMessageSize();
|
||||
return optimalEncodingMessageSize;
|
||||
}
|
||||
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// Ignore the header since it has fixed length. If length is not provided we will have to
|
||||
@ -716,7 +734,7 @@ public class Block extends Message {
|
||||
if (transactions.isEmpty())
|
||||
throw new VerificationException("Block had no transactions");
|
||||
maybeParseTransactions();
|
||||
if (this.getMessageSize() > MAX_BLOCK_SIZE)
|
||||
if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
|
||||
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
||||
checkTransactions();
|
||||
checkMerkleRoot();
|
||||
|
@ -79,6 +79,13 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
//
|
||||
// If this transaction is not stored in the wallet, appearsInHashes is null.
|
||||
private Set<Sha256Hash> appearsInHashes;
|
||||
|
||||
// Transactions can be encoded in a way that will use more bytes than is optimal
|
||||
// (due to VarInts having multiple encodings)
|
||||
// MAX_BLOCK_SIZE must be compared to the optimal encoding, not the actual encoding, so when parsing, we keep track
|
||||
// of the size of the ideal encoding in addition to the actual message size (which Message needs) so that Blocks
|
||||
// can properly keep track of optimal encoded size
|
||||
private transient int optimalEncodingMessageSize;
|
||||
|
||||
public Transaction(NetworkParameters params) {
|
||||
super(params);
|
||||
@ -478,26 +485,44 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
cursor = offset;
|
||||
|
||||
version = readUint32();
|
||||
optimalEncodingMessageSize = 4;
|
||||
|
||||
// First come the inputs.
|
||||
long numInputs = readVarInt();
|
||||
optimalEncodingMessageSize += VarInt.sizeOf(numInputs);
|
||||
inputs = new ArrayList<TransactionInput>((int) numInputs);
|
||||
for (long i = 0; i < numInputs; i++) {
|
||||
TransactionInput input = new TransactionInput(params, this, bytes, cursor, parseLazy, parseRetain);
|
||||
inputs.add(input);
|
||||
cursor += input.getMessageSize();
|
||||
long scriptLen = readVarInt(TransactionOutPoint.MESSAGE_LENGTH);
|
||||
optimalEncodingMessageSize += TransactionOutPoint.MESSAGE_LENGTH + VarInt.sizeOf(scriptLen) + scriptLen + 4;
|
||||
cursor += scriptLen + 4;
|
||||
}
|
||||
// Now the outputs
|
||||
long numOutputs = readVarInt();
|
||||
optimalEncodingMessageSize += VarInt.sizeOf(numOutputs);
|
||||
outputs = new ArrayList<TransactionOutput>((int) numOutputs);
|
||||
for (long i = 0; i < numOutputs; i++) {
|
||||
TransactionOutput output = new TransactionOutput(params, this, bytes, cursor, parseLazy, parseRetain);
|
||||
outputs.add(output);
|
||||
cursor += output.getMessageSize();
|
||||
long scriptLen = readVarInt(8);
|
||||
optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen;
|
||||
cursor += scriptLen;
|
||||
}
|
||||
lockTime = readUint32();
|
||||
optimalEncodingMessageSize += 4;
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
public int getOptimalEncodingMessageSize() {
|
||||
if (optimalEncodingMessageSize != 0)
|
||||
return optimalEncodingMessageSize;
|
||||
maybeParse();
|
||||
if (optimalEncodingMessageSize != 0)
|
||||
return optimalEncodingMessageSize;
|
||||
optimalEncodingMessageSize = getMessageSize();
|
||||
return optimalEncodingMessageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* A coinbase transaction is one that creates a new coin. They are the first transaction in each block and their
|
||||
|
Loading…
Reference in New Issue
Block a user