Skip to content

Commit cc4bf0e

Browse files
committed
Improve schema decoding errors
1 parent fc01519 commit cc4bf0e

File tree

4 files changed

+54
-31
lines changed

4 files changed

+54
-31
lines changed

backend-rust/.sqlx/query-8b160d1140d7823bcaf03711b9934621569d7e66c60aba8cb962054f82a8c2c6.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend-rust/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Database schema version: 2
2222

2323
### Fixed
2424

25+
- Fix button to display the schema decoding error at front-end by returning the error as an object.
2526
- Fix typo in `versions` endpoint.
2627
- Fix unit conversion for `avg_finalization_time` in `Query::block_metrics`.
2728
- Issue for `Query::transaction_metrics` producing an internal error when query period is beyond the genesis block.

backend-rust/src/migrations/m0001-initialize.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,7 @@ CREATE INDEX block_special_transaction_outcomes_idx
841841

842842
-- Function for generating a table where each row is a bucket.
843843
-- Used by metrics queries.
844+
-- This function is replaced with a function of the same name in migration file `m0002`.
844845
CREATE OR REPLACE FUNCTION date_bin_series(bucket_size interval, starting TIMESTAMPTZ, ending TIMESTAMPTZ)
845846
RETURNS TABLE(bucket_start TIMESTAMPTZ, bucket_end TIMESTAMPTZ) AS $$
846847
SELECT

backend-rust/src/transaction_event/smart_contracts.rs

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use concordium_rust_sdk::base::{
1111
},
1212
smart_contracts::ReceiveName,
1313
};
14+
use serde::Serialize;
1415

1516
#[derive(Enum, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1617
pub enum ContractVersion {
@@ -118,7 +119,7 @@ impl ContractInitialized {
118119
opt_event_schema.as_ref(),
119120
log,
120121
SmartContractSchemaNames::Event,
121-
);
122+
)?;
122123

123124
connection.edges.push(connection::Edge::new(index.to_string(), decoded_log));
124125
}
@@ -190,7 +191,7 @@ impl ContractUpdated {
190191
opt_receive_param_schema.as_ref(),
191192
&self.input_parameter,
192193
SmartContractSchemaNames::InputParameterReceiveFunction,
193-
);
194+
)?;
194195

195196
Ok(Some(decoded_input_parameter))
196197
}
@@ -250,7 +251,7 @@ impl ContractUpdated {
250251
opt_event_schema.as_ref(),
251252
log,
252253
SmartContractSchemaNames::Event,
253-
);
254+
)?;
254255

255256
connection.edges.push(connection::Edge::new(index.to_string(), decoded_log));
256257
}
@@ -331,7 +332,7 @@ impl ContractInterrupted {
331332
opt_event_schema.as_ref(),
332333
log,
333334
SmartContractSchemaNames::Event,
334-
);
335+
)?;
335336

336337
connection.edges.push(connection::Edge::new(index.to_string(), decoded_log));
337338
}
@@ -394,38 +395,55 @@ impl SmartContractSchemaNames {
394395
}
395396
}
396397

398+
/// Schema decoding error reported to the front-end.
399+
#[derive(Serialize)]
400+
struct SchemaDecodingError {
401+
error: String,
402+
}
403+
397404
fn decode_value_with_schema(
398405
opt_schema: Option<&Type>,
399406
value: &[u8],
400407
schema_name: SmartContractSchemaNames,
401-
) -> String {
408+
) -> Result<String, ApiError> {
402409
let Some(schema) = opt_schema else {
403410
// Note: There could be something better displayed than this string if no schema
404411
// is available for decoding at the frontend long-term.
405-
return format!(
406-
"No embedded {} schema in smart contract available for decoding",
407-
schema_name.kind()
408-
);
412+
return serde_json::to_string(&SchemaDecodingError {
413+
error: format!(
414+
"No embedded {} schema in smart contract available for decoding",
415+
schema_name.kind()
416+
),
417+
}).map_err(|_| ApiError::InternalError("Should be valid error string".to_string()));
409418
};
410419

411420
let mut cursor = Cursor::new(&value);
421+
412422
match schema.to_json(&mut cursor) {
413423
Ok(v) => {
414-
serde_json::to_string(&v).unwrap_or_else(|e| {
415-
// We don't return an error here since the query is correctly formed and
416-
// the CCDScan backend is working as expected.
417-
// A wrong/missing schema is a mistake by the smart contract
418-
// developer which in general cannot be fixed after the deployment of
419-
// the contract. We display the error message (instead of the decoded
420-
// value) in the block explorer to make the info visible to the smart
421-
// contract developer for debugging purposes here.
422-
format!(
423-
"Failed to deserialize {} with {} schema into string: {:?}",
424-
schema_name.value(),
425-
schema_name.kind(),
426-
e
427-
)
428-
})
424+
match serde_json::to_string(&v) {
425+
Ok(v) => Ok(v),
426+
Err(error) => {
427+
// We don't return an error here since the query is correctly formed and
428+
// the CCDScan backend is working as expected.
429+
// A wrong/missing schema is a mistake by the smart contract
430+
// developer which in general cannot be fixed after the deployment of
431+
// the contract. We display the error message (instead of the decoded
432+
// value) in the block explorer to make the info visible to the smart
433+
// contract developer for debugging purposes here.
434+
Ok(serde_json::to_string(&SchemaDecodingError {
435+
error: format!(
436+
"Failed to deserialize {} with {} schema into string: {:?}",
437+
schema_name.value(),
438+
schema_name.kind(),
439+
error
440+
),
441+
})
442+
.map_err(|_| {
443+
ApiError::InternalError("Should be valid error string".to_string())
444+
})?)
445+
}
446+
}
429447
}
430448
Err(e) => {
431449
// We don't return an error here since the query is correctly formed and
@@ -435,12 +453,15 @@ fn decode_value_with_schema(
435453
// the contract. We display the error message (instead of the decoded
436454
// value) in the block explorer to make the info visible to the smart
437455
// contract developer for debugging purposes here.
438-
format!(
439-
"Failed to deserialize {} with {} schema: {:?}",
440-
schema_name.value(),
441-
schema_name.kind(),
442-
e.display(true)
443-
)
456+
Ok(serde_json::to_string(&SchemaDecodingError {
457+
error: format!(
458+
"Failed to deserialize {} with {} schema: {:?}",
459+
schema_name.value(),
460+
schema_name.kind(),
461+
e.display(true)
462+
),
463+
})
464+
.map_err(|_| ApiError::InternalError("Should be valid error string".to_string()))?)
444465
}
445466
}
446467
}

0 commit comments

Comments
 (0)