This fix addresses the silent zero-cost bug where missing XDR resource fields were incorrectly reported as zero cost.
let instructions = parsed["resources"]["instructions"].as_u64().unwrap_or(0) as u32;
let read_bytes = parsed["resources"]["disk_read_bytes"].as_u64().unwrap_or(0) as u32;
let write_bytes = parsed["resources"]["write_bytes"].as_u64().unwrap_or(0) as u32;Parsing relied on serde_json::Value directly with .unwrap_or(0) fallback, which:
- Silently reported zero for missing fields
- Silently wrapped when values exceed u32::MAX
#[derive(serde::Deserialize, Debug)]
struct Resources {
instructions: u64,
disk_read_bytes: u64,
write_bytes: u64,
}
#[derive(serde::Deserialize, Debug)]
struct TransactionData {
#[serde(alias = "resources")]
resources: Resources,
}
impl TransactionData {
fn parse_json(json_str: &str) -> Result<Self> {
let parsed_json: serde_json::Value =
serde_json::from_str(json_str).context("Failed to parse JSON")?;
serde_json::from_value(parsed_json).context("Failed to deserialize transaction data")
}
}
let tx_data: TransactionData = serde_json::from_value(parsed_json)
.context("Failed to deserialize transaction data")?;
let instructions = tx_data.resources.instructions;
let read_bytes = tx_data.resources.disk_read_bytes;
let write_bytes = tx_data.resources.write_bytes;- Typed Struct Deserialization: Uses
serde::Deserializeon structured types instead of indexing a JSON object - Field Validation:
serdeautomatically validates presence and type of all three fields - No Silent Failures: Missing fields cause
serde_json::from_valueto fail with clear error messages - u32 Truncation Protection: Values are naturally
u64but can be checked/truncated appropriately if needed - Alias Support: Adds
#[serde(alias = "resources")]for protocol compatibility
fixtures/transaction_data_valid.json- Valid test data for checking parsing workscargo-budget-report/fixtures/transaction_data_valid.json- Same file for consistency
transaction_data_parsing_deserializes_successfully()transaction_data_parsing_fails_on_missing_field()transaction_data_parsing_fails_on_non_numeric_field()
Each test:
- Validates correct parsing of well-formed data
- Ensures missing fields cause meaningful error reports
- Verifies non-numeric values are properly rejected
- Protocol Upgrades:
#[serde(alias = "resources")]supports field renames - Nested Differences: Deserialization fails if field nesting changes
- Missing Fields: Clear error messages instead of zero values
- Out-of-Range Values: Prevents silent wrapping (transition to appropriate types if needed)
- Non-Numeric Values: Properly rejects invalid types
The fixture JSON uses field names matching Protocol 22:
{
"resources": {
"instructions": 1000,
"disk_read_bytes": 2048,
"write_bytes": 3072
}
}This matches the actual output from XDR decoding in Protocol 22.