-
Notifications
You must be signed in to change notification settings - Fork 0
GP 0.7.2 #376
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: master
Are you sure you want to change the base?
Conversation
qiweiii
commented
Dec 1, 2025
- udpates
- testvectors
- conformance traces
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.
Found 3 behavioral regressions in the host-call and guaranteeing logic.
Suggestions that couldn't be attached to a specific line
Blockchain/Sources/Blockchain/VMInvocations/HostCall/HostCalls.swift:1445,1541-1543
The Log host call now unconditionally writes HostCallResultCode.WHAT into register 7 after emitting the message. In the GP’s host-call result table (see ResultConstants.swift), WHAT represents “name unknown” and signals failure. Returning WHAT on every successful log means services can no longer distinguish success from an actual WHAT error. The call should report HostCallResultCode.OK (or propagate a real error) when the log is accepted so that callers can observe the true status.
| let srcAccount = try? await x.state.accounts.value.get(serviceAccount: x.serviceIndex) | ||
| let destAccount = try? await x.state.accounts.value.get(serviceAccount: dest) | ||
|
|
||
| logger.debug("src: \(x.serviceIndex), dest: \(dest), amount: \(amount), gasLimit: \(gasLimit)") | ||
| logger.debug("dest is found: \(destAccount != nil)") | ||
|
|
||
| let (resultCode, additionalGas): (UInt64, Gas) | ||
|
|
||
| if memo == nil { | ||
| throw VMInvocationsError.panic | ||
| return .exit(.panic(.trap)) | ||
| } else if destAccount == nil { | ||
| state.writeRegister(Registers.Index(raw: 7), HostCallResultCode.WHO.rawValue) | ||
| (resultCode, additionalGas) = (HostCallResultCode.WHO.rawValue, Gas(0)) | ||
| } else if gasLimit < destAccount!.minMemoGas { | ||
| state.writeRegister(Registers.Index(raw: 7), HostCallResultCode.LOW.rawValue) | ||
| (resultCode, additionalGas) = (HostCallResultCode.LOW.rawValue, Gas(0)) | ||
| } else if let srcAccount, srcAccount.balance - amount < srcAccount.thresholdBalance(config: config) { | ||
| state.writeRegister(Registers.Index(raw: 7), HostCallResultCode.CASH.rawValue) | ||
| } else if var srcAccount { | ||
| state.writeRegister(Registers.Index(raw: 7), HostCallResultCode.OK.rawValue) | ||
| (resultCode, additionalGas) = (HostCallResultCode.CASH.rawValue, Gas(0)) | ||
| } else { | ||
| (resultCode, additionalGas) = (HostCallResultCode.OK.rawValue, gasLimit) | ||
| } | ||
|
|
||
| state.consumeGas(additionalGas) | ||
| logger.debug("consumed additional \(additionalGas) gas, \(state.getGas()) left") | ||
|
|
||
| guard state.getGas() >= GasInt(0) else { | ||
| logger.debug("not enough gas for additional charge") | ||
| return .exit(.outOfGas) | ||
| } | ||
|
|
||
| state.writeRegister(Registers.Index(raw: 7), resultCode) | ||
| if resultCode == HostCallResultCode.OK.rawValue, var srcAccount { |
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.
The new Transfer implementation swallows every error from x.state.accounts.value.get(serviceAccount: x.serviceIndex) via try? and then falls through to the else branch that sets (resultCode, additionalGas) = (OK, gasLimit) even when the source account lookup actually failed. Because the subsequent if resultCode == …, var srcAccount block silently skips when the optional is nil, the host call now returns OK and burns gas without ever deducting the sender’s balance whenever storage access fails. Per the GP host-call semantics (see HostCallResultCode.OK/WHO definitions), a missing or unreadable service account must surface as an error (typically WHO or a panic), not as a successful transfer. Please keep the original try await so failures propagate, or explicitly treat a nil/failed lookup as HostCallResultCode.WHO before you ever set the result to OK.
| // Validate gas limits | ||
| var totalRefineGas = Gas(0) | ||
| var totalAccumulateGas = Gas(0) | ||
|
|
||
| for item in workPackage.workItems { | ||
| totalGas += item.refineGasLimit | ||
|
|
||
| // Check gas limits | ||
| let maxWorkItemRefineGas = Gas(1_000_000_000) // Default maximum refine gas | ||
| guard item.refineGasLimit <= maxWorkItemRefineGas else { | ||
| logger.debug("Work item refine gas exceeds limit", | ||
| metadata: ["actual": "\(item.refineGasLimit)", "limit": "\(maxWorkItemRefineGas)"]) | ||
| throw GuaranteeingServiceError.invalidWorkPackage | ||
| } | ||
| totalRefineGas += item.refineGasLimit | ||
| totalAccumulateGas += item.accumulateGasLimit | ||
| } | ||
|
|
||
| let maxWorkItemAccumulateGas = Gas(1_000_000) // Default maximum accumulate gas | ||
| guard item.accumulateGasLimit <= maxWorkItemAccumulateGas else { | ||
| logger.debug("Work item accumulate gas exceeds limit", | ||
| metadata: ["actual": "\(item.accumulateGasLimit)", "limit": "\(maxWorkItemAccumulateGas)"]) | ||
| throw GuaranteeingServiceError.invalidWorkPackage | ||
| } | ||
| // Check total refine gas | ||
| guard totalRefineGas < config.value.workPackageRefineGas else { | ||
| logger.debug("Work package total refine gas exceeds limit", | ||
| metadata: ["actual": "\(totalRefineGas)", "limit": "\(config.value.workPackageRefineGas)"]) | ||
| throw GuaranteeingServiceError.invalidWorkPackage | ||
| } | ||
|
|
||
| // Check total gas usage | ||
| let maxWorkPackageTotalGas = Gas(5_000_000_000) // Default maximum total gas | ||
| guard totalGas <= maxWorkPackageTotalGas else { | ||
| logger.debug("Work package total gas exceeds limit", | ||
| metadata: ["actual": "\(totalGas)", "limit": "\(maxWorkPackageTotalGas)"]) | ||
| // Check total accumulate gas | ||
| guard totalAccumulateGas < config.value.workReportAccumulationGas else { | ||
| logger.debug("Work package total accumulate gas exceeds limit", | ||
| metadata: ["actual": "\(totalAccumulateGas)", "limit": "\(config.value.workReportAccumulationGas)"]) | ||
| throw GuaranteeingServiceError.invalidWorkPackage |
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.
Both new guards use < when comparing the summed gas limits to config.value.workPackageRefineGas (GR) and config.value.workReportAccumulationGas (GA). GR and GA are the maximum gas budgets defined in the GP (see the ProtocolConfig comments), so a work package that exactly consumes those budgets is still valid. With the strict < check, any package whose totals equal the configured maxima is rejected, reducing allowable throughput for no reason. Change the conditions to reject only when totalRefineGas > config.value.workPackageRefineGas and totalAccumulateGas > config.value.workReportAccumulationGas (or equivalently switch the guards to <=).