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 headerBytesValid;
|
||||||
private transient boolean transactionBytesValid;
|
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. */
|
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
|
||||||
Block(NetworkParameters params) {
|
Block(NetworkParameters params) {
|
||||||
@ -167,6 +172,7 @@ public class Block extends Message {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
cursor = offset + HEADER_SIZE;
|
cursor = offset + HEADER_SIZE;
|
||||||
|
optimalEncodingMessageSize = HEADER_SIZE;
|
||||||
if (bytes.length == cursor) {
|
if (bytes.length == cursor) {
|
||||||
// This message is just a header, it has no transactions.
|
// This message is just a header, it has no transactions.
|
||||||
transactionsParsed = true;
|
transactionsParsed = true;
|
||||||
@ -175,11 +181,13 @@ public class Block extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int numTransactions = (int) readVarInt();
|
int numTransactions = (int) readVarInt();
|
||||||
|
optimalEncodingMessageSize += VarInt.sizeOf(numTransactions);
|
||||||
transactions = new ArrayList<Transaction>(numTransactions);
|
transactions = new ArrayList<Transaction>(numTransactions);
|
||||||
for (int i = 0; i < numTransactions; i++) {
|
for (int i = 0; i < numTransactions; i++) {
|
||||||
Transaction tx = new Transaction(params, bytes, cursor, this, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
Transaction tx = new Transaction(params, bytes, cursor, this, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
||||||
transactions.add(tx);
|
transactions.add(tx);
|
||||||
cursor += tx.getMessageSize();
|
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().
|
// 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.
|
// 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();
|
parseTransactions();
|
||||||
length = cursor - offset;
|
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 {
|
protected void parseLite() throws ProtocolException {
|
||||||
// Ignore the header since it has fixed length. If length is not provided we will have to
|
// 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())
|
if (transactions.isEmpty())
|
||||||
throw new VerificationException("Block had no transactions");
|
throw new VerificationException("Block had no transactions");
|
||||||
maybeParseTransactions();
|
maybeParseTransactions();
|
||||||
if (this.getMessageSize() > MAX_BLOCK_SIZE)
|
if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
|
||||||
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
||||||
checkTransactions();
|
checkTransactions();
|
||||||
checkMerkleRoot();
|
checkMerkleRoot();
|
||||||
|
@ -79,6 +79,13 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
//
|
//
|
||||||
// If this transaction is not stored in the wallet, appearsInHashes is null.
|
// If this transaction is not stored in the wallet, appearsInHashes is null.
|
||||||
private Set<Sha256Hash> appearsInHashes;
|
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) {
|
public Transaction(NetworkParameters params) {
|
||||||
super(params);
|
super(params);
|
||||||
@ -478,26 +485,44 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
cursor = offset;
|
cursor = offset;
|
||||||
|
|
||||||
version = readUint32();
|
version = readUint32();
|
||||||
|
optimalEncodingMessageSize = 4;
|
||||||
|
|
||||||
// First come the inputs.
|
// First come the inputs.
|
||||||
long numInputs = readVarInt();
|
long numInputs = readVarInt();
|
||||||
|
optimalEncodingMessageSize += VarInt.sizeOf(numInputs);
|
||||||
inputs = new ArrayList<TransactionInput>((int) numInputs);
|
inputs = new ArrayList<TransactionInput>((int) numInputs);
|
||||||
for (long i = 0; i < numInputs; i++) {
|
for (long i = 0; i < numInputs; i++) {
|
||||||
TransactionInput input = new TransactionInput(params, this, bytes, cursor, parseLazy, parseRetain);
|
TransactionInput input = new TransactionInput(params, this, bytes, cursor, parseLazy, parseRetain);
|
||||||
inputs.add(input);
|
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
|
// Now the outputs
|
||||||
long numOutputs = readVarInt();
|
long numOutputs = readVarInt();
|
||||||
|
optimalEncodingMessageSize += VarInt.sizeOf(numOutputs);
|
||||||
outputs = new ArrayList<TransactionOutput>((int) numOutputs);
|
outputs = new ArrayList<TransactionOutput>((int) numOutputs);
|
||||||
for (long i = 0; i < numOutputs; i++) {
|
for (long i = 0; i < numOutputs; i++) {
|
||||||
TransactionOutput output = new TransactionOutput(params, this, bytes, cursor, parseLazy, parseRetain);
|
TransactionOutput output = new TransactionOutput(params, this, bytes, cursor, parseLazy, parseRetain);
|
||||||
outputs.add(output);
|
outputs.add(output);
|
||||||
cursor += output.getMessageSize();
|
long scriptLen = readVarInt(8);
|
||||||
|
optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen;
|
||||||
|
cursor += scriptLen;
|
||||||
}
|
}
|
||||||
lockTime = readUint32();
|
lockTime = readUint32();
|
||||||
|
optimalEncodingMessageSize += 4;
|
||||||
length = cursor - offset;
|
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
|
* 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