Skip to content

Conversation

@qiweiii
Copy link
Contributor

@qiweiii qiweiii commented Dec 1, 2025

  • udpates
  • testvectors
  • conformance traces

Copy link

@github-actions github-actions bot left a 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.

Comment on lines +1077 to +1106
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 {
Copy link

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.

Comment on lines +513 to 533
// 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
Copy link

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 <=).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants