Repository navigation
fix(net): validate block transactions before propagation #7066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9b6221e
9126dfd
fdf2ccc
d24fa79
2b1a063
3421300
d3328c9
9b3ab87
35bd037
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,11 +62,15 @@ | |
| import org.tron.core.net.peer.PeerConnection; | ||
| import org.tron.core.store.WitnessScheduleStore; | ||
| import org.tron.protos.Protocol.Inventory.InventoryType; | ||
| import org.tron.protos.Protocol.Transaction; | ||
|
|
||
| @Slf4j(topic = "net") | ||
| @Component | ||
| public class TronNetDelegate { | ||
|
|
||
| // Keep Merkle leaf encodings larger than the 64-byte internal-node input. | ||
| private static final int MIN_TRANSACTION_SIZE = 80; | ||
|
|
||
| @Autowired | ||
| private Manager dbManager; | ||
|
|
||
|
|
@@ -89,8 +93,6 @@ public class TronNetDelegate { | |
|
|
||
| private int blockIdCacheSize = 100; | ||
|
|
||
| private long timeout = 1000; | ||
|
|
||
| @Getter // for test | ||
| private volatile boolean hitDown = false; | ||
|
|
||
|
|
@@ -352,10 +354,19 @@ public void validSignature(BlockCapsule block) throws P2pException { | |
| } | ||
|
|
||
| public void validBlock(BlockCapsule block) throws P2pException { | ||
| long time = System.currentTimeMillis(); | ||
| if (block.getTimeStamp() - time > timeout) { | ||
| throw new P2pException(TypeEnum.BAD_BLOCK, | ||
| "time:" + time + ",block time:" + block.getTimeStamp()); | ||
| for (Transaction transaction : block.getInstance().getTransactionsList()) { | ||
| int contractCount = transaction.getRawData().getContractCount(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SHOULD] Please also check each transaction’s full serialized size here, rejecting transactions smaller than 65 bytes or larger than Merkle leaves and internal nodes use the same hash function without domain separation. A 64-byte transaction can match the concatenation of two 32-byte child hashes, allowing different transaction lists to share the same Merkle root. Checking contract count alone does not rule this out. Please add boundary tests and verify compatibility with historical blocks and shielded transactions.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented in 9b3ab87. Tests cover 64/79-byte rejection, 80/81-byte acceptance, signatures and This change adds the lower bound only; upper-bound validation is unchanged. A full historical-chain replay has not been performed. |
||
| if (contractCount != 1) { | ||
| throw new P2pException(TypeEnum.BAD_BLOCK, | ||
| "Invalid contract count: " + contractCount | ||
| + ", block: " + block.getBlockId().getString()); | ||
| } | ||
| int transactionSize = transaction.getSerializedSize(); | ||
| if (transactionSize < MIN_TRANSACTION_SIZE) { | ||
| throw new P2pException(TypeEnum.BAD_BLOCK, | ||
| "Invalid transaction size: " + transactionSize | ||
| + ", block: " + block.getBlockId().getString()); | ||
| } | ||
| } | ||
| try { | ||
| block.validateMerkleRoot(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[DISCUSS] Confirm the Merkle-validation boundary for future callers
This removes the Merkle-root check from
pushBlockand makes network prevalidation a caller prerequisite. The current advertised and sync paths both callvalidBlock, so I did not find a current bypass; however, a future direct caller that misses this Javadoc contract would no longer get the previous defense-in-depth check.Suggestion: Keep the prevalidation requirement explicit for all new entry points, and consider retaining a core Merkle check or introducing a validated-block abstraction if that caller contract cannot be enforced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an intentional change in validation responsibility: the advertised-block path calls
validAdvBlock -> validBlock, and the sync path callsvalidBlockdirectly before enteringpushBlock. The duplicate Merkle validation in the core layer was therefore removed.I agree with the reminder about future callers. The Javadoc for
pushBlockexplicitly requires untrusted blocks to undergo prevalidation, and new entry points must respect that prerequisite as well. No further code changes are planned for this comment.