-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(net): validate merkle root before block broadcast #6716
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
base: develop
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -347,6 +347,11 @@ public boolean validBlock(BlockCapsule block) throws P2pException { | |
| throw new P2pException(TypeEnum.BAD_BLOCK, | ||
| "time:" + time + ",block time:" + block.getTimeStamp()); | ||
| } | ||
| try { | ||
| block.validateMerkleRoot(); | ||
| } catch (BadBlockException e) { | ||
| throw new P2pException(TypeEnum.BLOCK_MERKLE_ERROR, e.getMessage()); | ||
| } | ||
|
Comment on lines
+350
to
+354
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. Nice work moving the merkle check before broadcast — that's exactly the right place to catch tampered blocks before we forward them. 🎯 Minor consistency nit: when wrapping
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. The BLOCK_MERKLE_ERROR exception is quite obvious, and the message information is sufficient. There's no need to re-throw the original exception, and the previous logic doesn't need to be changed; there's no impact whatsoever. |
||
| validSignature(block); | ||
| return witnessScheduleStore.getActiveWitnesses().contains(block.getWitnessAddress()); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -500,8 +500,8 @@ public void pushBlockInvalidMerkelRoot() { | |
| } catch (BadBlockException e) { | ||
| Assert.assertTrue(e instanceof BadBlockException); | ||
| Assert.assertTrue(e.getType().equals(CALC_MERKLE_ROOT_FAILED)); | ||
| Assert.assertEquals("The merkle hash is not validated for " | ||
| + blockCapsule2.getNum(), e.getMessage()); | ||
| Assert.assertTrue(e.getMessage().startsWith( | ||
| "merkle root mismatch for block " + blockCapsule2.getNum() + ":")); | ||
| } catch (Exception e) { | ||
| Assert.assertFalse(e instanceof Exception); | ||
| } | ||
|
|
@@ -1292,6 +1292,35 @@ public void blockTrigger() { | |
| Assert.assertEquals(TronError.ErrCode.EVENT_SUBSCRIBE_ERROR, thrown.getErrCode()); | ||
| } | ||
|
|
||
| @Test | ||
|
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] The PR title is "validate merkle root before block broadcast", but
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. Agreed. |
||
| public void testRePushResetsVerifiedOnOwnerAddressSetHit() throws Exception { | ||
| TransferContract transferContract = TransferContract.newBuilder() | ||
| .setAmount(1L) | ||
| .setOwnerAddress(ByteString.copyFrom( | ||
| ByteArray.fromHexString(Wallet.getAddressPreFixString() | ||
| + "548794500882809695A8A687866E76D4271A1ABC"))) | ||
| .setToAddress(ByteString.copyFrom( | ||
| ByteArray.fromHexString(Wallet.getAddressPreFixString() | ||
| + "A389132D6639FBDA4FBC8B659264E6B7C90DB086"))) | ||
| .build(); | ||
| TransactionCapsule tx = new TransactionCapsule(transferContract, ContractType.TransferContract); | ||
| tx.setVerified(true); // simulate mempool-cached state | ||
|
|
||
| String ownerAddress = ByteArray.toHexString(tx.getOwnerAddress()); | ||
|
|
||
| // Inject ownerAddress into ownerAddressSet via reflection | ||
| Set<String> ownerAddressSet = | ||
| (Set<String>) ReflectUtils.getFieldObject(dbManager, "ownerAddressSet"); | ||
| ownerAddressSet.add(ownerAddress); | ||
|
|
||
| // rePush should reset isVerified to false before pushTransaction | ||
| dbManager.rePush(tx); | ||
|
|
||
| // After rePush, isVerified must be false | ||
| Boolean verified = (Boolean) ReflectUtils.getFieldObject(tx, "isVerified"); | ||
| Assert.assertFalse(verified); | ||
| } | ||
|
|
||
| public void adjustBalance(AccountStore accountStore, byte[] accountAddress, long amount) | ||
| throws BalanceInsufficientException { | ||
| Commons.adjustBalance(accountStore, accountAddress, amount, | ||
|
|
||
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.
[INT] Give code comment why should setVerified as false here.
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 if statement is already quite concise and clear. Transactions involving permission changes should reset this flag. Re-verification is required to prevent transactions with incorrect signatures from entering the pending queue.