Repository navigation
fix(db): validate block signatures against committed state #7076
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: release_v4.8.3
Are you sure you want to change the base?
Changes from all commits
1dad6be
9ee6ee7
98e0eab
442f32b
b1ebc3b
1bc52a6
fe1d4fc
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 |
|---|---|---|
|
|
@@ -283,7 +283,12 @@ public class Manager { | |
| while (isRunRePushThread) { | ||
| TransactionCapsule tx = null; | ||
| try { | ||
| tx = getRePushTransactions().peek(); | ||
| synchronized (this) { | ||
| tx = getRePushTransactions().peek(); | ||
| if (tx != null) { | ||
| pushTransactionQueue.add(tx); | ||
| } | ||
| } | ||
| if (tx != null) { | ||
| this.rePush(tx); | ||
| } else { | ||
|
|
@@ -302,9 +307,12 @@ public class Manager { | |
| throw e; | ||
| }); | ||
| } finally { | ||
| if (tx != null && getRePushTransactions().remove(tx)) { | ||
| Metrics.gaugeInc(MetricKeys.Gauge.MANAGER_QUEUE, -1, | ||
| MetricLabels.Gauge.QUEUE_REPUSH); | ||
| if (tx != null) { | ||
| if (getRePushTransactions().remove(tx)) { | ||
| Metrics.gaugeInc(MetricKeys.Gauge.MANAGER_QUEUE, -1, | ||
| MetricLabels.Gauge.QUEUE_REPUSH); | ||
| } | ||
| pushTransactionQueue.remove(tx); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1228,7 +1236,8 @@ public List<TransactionCapsule> getVerifyTxs(BlockCapsule block) { | |
|
|
||
| /** | ||
| * Save a block. Untrusted callers must first use network prevalidation to check its timestamp, | ||
| * transaction structure, Merkle root and block signature. | ||
| * transaction structure and Merkle root. Check witness signatures before broadcast as well. | ||
| * Received block witness authorization is checked against committed state before admission. | ||
| */ | ||
| public void pushBlock(final BlockCapsule block) | ||
| throws ValidateSignatureException, ContractValidateException, ContractExeException, | ||
|
|
@@ -1242,11 +1251,6 @@ public void pushBlock(final BlockCapsule block) | |
| synchronized (this) { | ||
| Metrics.histogramObserve(blockedTimer.get()); | ||
| blockedTimer.remove(); | ||
| if (Metrics.enabled()) { | ||
| Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, | ||
| block.getTransactions().size(), | ||
| StringUtil.encode58Check(block.getWitnessAddress().toByteArray())); | ||
| } | ||
| long headerNumber = getDynamicPropertiesStore().getLatestBlockHeaderNumber(); | ||
| if (block.getNum() <= headerNumber && khaosDb.containBlockInMiniStore(block.getBlockId())) { | ||
| logger.info("Block {} is already exist.", block.getBlockId().getString()); | ||
|
|
@@ -1261,15 +1265,15 @@ public void pushBlock(final BlockCapsule block) | |
| block.getNum(), rePushTransactions.size(), pendingTransactions.size(), | ||
| block.getTransactions().size(), txs.size()); | ||
|
|
||
| if (CommonParameter.getInstance().getShutdownBlockTime() != null | ||
| && CommonParameter.getInstance().getShutdownBlockTime() | ||
| .isSatisfiedBy(new Date(block.getTimeStamp()))) { | ||
| latestSolidityNumShutDown = block.getNum(); | ||
| } | ||
|
|
||
| try (PendingManager pm = new PendingManager(this)) { | ||
|
|
||
| if (!block.generatedByMyself) { | ||
| // Pending changes have been reset; use committed witness permissions for admission. | ||
| // switchFork also verifies admitted fork blocks against their own parent state. | ||
| if (!block.validateSignature(getDynamicPropertiesStore(), getAccountStore())) { | ||
|
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. [NIT] Null witness account surfaces as NPE instead of a clean signature-validation failure This new call site invokes Suggestion: normalize the failure at the source — either null-check the account lookup in
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 missing account null check is existing behavior in This PR focuses on validating block authorization under the Manager lock after resetting the pending state. Normalizing exception handling in the shared signature-validation method affects other call sites and is better handled separately with the corresponding regression coverage. Since the current rejection behavior remains effective, I would leave this non-blocking robustness improvement outside this PR. |
||
| throw new ValidateSignatureException( | ||
| "block " + block.getNum() + " signature invalid"); | ||
| } | ||
| consensus.receiveBlock(block); | ||
| } | ||
|
|
||
|
|
@@ -1324,6 +1328,7 @@ public void pushBlock(final BlockCapsule block) | |
| synchronized (forkLock) { | ||
| switchFork(newBlock); | ||
| } | ||
| onBlockApplied(block); | ||
| logger.info(SAVE_BLOCK, newBlock); | ||
|
|
||
| logger.warn( | ||
|
|
@@ -1353,6 +1358,7 @@ public void pushBlock(final BlockCapsule block) | |
| long newSolidNum = getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); | ||
| blockTrigger(newBlock, oldSolidNum, newSolidNum); | ||
| } | ||
| onBlockApplied(block); | ||
| logger.info(SAVE_BLOCK, newBlock); | ||
| } | ||
| //clear ownerAddressSet | ||
|
|
@@ -1381,6 +1387,20 @@ public void pushBlock(final BlockCapsule block) | |
| } | ||
| } | ||
|
|
||
| private void onBlockApplied(BlockCapsule block) { | ||
| if (Metrics.enabled()) { | ||
| Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, | ||
| block.getTransactions().size(), | ||
| StringUtil.encode58Check(block.getWitnessAddress().toByteArray())); | ||
| } | ||
|
|
||
| if (CommonParameter.getInstance().getShutdownBlockTime() != null | ||
| && CommonParameter.getInstance().getShutdownBlockTime() | ||
| .isSatisfiedBy(new Date(block.getTimeStamp()))) { | ||
| latestSolidityNumShutDown = block.getNum(); | ||
| } | ||
| } | ||
|
|
||
| void blockTrigger(final BlockCapsule block, long oldSolid, long newSolid) { | ||
| // post block and logs for jsonrpc | ||
| try { | ||
|
|
||
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.
[MUST] Agree with Federico2014's parent-state comment. Two additions:
rejectsForkWitnessNotAuthorizedAtCurrentHeadasserts that incorrect rejection and should be inverted; and whatever fix is chosen,consensus.receiveBlockand the shutdown target update must stay behind the parent-state authorization, not just be skipped for fork blocks.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.
rejectsForkWitnessNotAuthorizedAtCurrentHeadcovers the current implementation's admission check against committed permissions on the canonical chain; it does not establish that a fork block is authorized under its parent state. Changing the expected result to acceptance requires implementing parent-state validation as well, rather than only changing the test assertion.The current implementation does not defer fork authorization. Both
consensus.receiveBlockand the shutdown target update still run after successful authorization in Manager. If authorization is later deferred until the parent state is available, both operations will also need to run after that validation succeeds.For this PR, I would retain the current implementation and address parent-state validation and the corresponding test changes as a separate improvement, consistent with the scope described in my earlier review reply.