Incorrect Default Configuration Leading to Dead Code
Description
Discussions with the Shardeum team revealed that certain key features of the Archiver are currently unused. This suggests that the default configuration is incorrect, preventing critical functionality from being executed.
Example
When receiving receipt data from validators, storeReceiptData is triggered. Depending on the Archiver's configuration, several execution flows are possible. The most important one involves calling Utils.robustQuery to verify that the receipt matches on at least five nodes within the execution group.
However, in the current default configuration, this flow is inaccessible due to the following reasons:
config.newPOQReceipt === false
During verifyReceiptData, execution enters the if (config.newPOQReceipt === false) block, preventing it from reaching verifyReceiptMajority.
Even if config.newPOQReceipt === true, execution does reach verifyReceiptMajority, but another issue arises:
Since config.useRobustQueryForReceipt is undefined, !undefined evaluates to true, leading to verifyReceiptOffline being executed instead of verifyReceiptWithValidators.
As a result, robustQuery is never called, making it effectively dead code.
Impact
By skipping a crucial step in receipt verification, attackers gain more flexibility to bypass validation checks and exploit vulnerabilities. For instance, similar issues were demonstrated in report #39872.
Proposed Fix
Update the default configuration to align with the intended behavior of the Archiver, ensuring robustQuery is executed as expected.
Proof of Concept
Proof of Concept
Apply the following git diff on the Archiver, we'll check the logs to see that this code isn't reached:
diff --git a/src/Data/Collector.ts b/src/Data/Collector.ts
index 610e0df..fd81d24 100644
--- a/src/Data/Collector.ts
+++ b/src/Data/Collector.ts
@@ -68,6 +68,7 @@ const verifyReceiptMajority = async (
executionGroupNodes: ConsensusNodeInfo[],
minConfirmations: number = config.RECEIPT_CONFIRMATIONS
): Promise<{ success: boolean; newReceipt?: Receipt.ArchiverReceipt | Receipt.Receipt }> => {
+ Logger.mainLogger.info("BLOCKIAN - DEAD CODE 2")
/**
* Note:
* Currently, only the non-global receipt flow is implemented in `verifyReceiptMajority`,
@@ -133,6 +134,7 @@ const verifyNonGlobalTxReceiptWithValidators = async (
executionGroupNodes: ConsensusNodeInfo[],
minConfirmations: number = config.RECEIPT_CONFIRMATIONS
): Promise<{ success: boolean; newReceipt?: Receipt.ArchiverReceipt | Receipt.Receipt }> => {
+ Logger.mainLogger.info("BLOCKIAN - DEAD CODE 3")
const result = { success: false }
// Created signedData with full_receipt = false outside of queryReceipt to avoid signing the same data multiple times
let signedData = Crypto.sign({
@@ -615,6 +617,8 @@ export const verifyReceiptData = async (
return { success: true, requiredSignatures }
}
+ Logger.mainLogger.info("BLOCKIAN - DEAD CODE 1")
+
// const { confirmOrChallenge } = appliedReceipt as Receipt.AppliedReceipt2
// // Check if the appliedVote node is in the execution group
// if (!cycleShardData.nodeShardDataMap.has(appliedVote.node_id)) {
Add the following wallet to the genesis.json file: