Skip to content

Commit

Permalink
Merge pull request #484 from Concordium/improve-schema-decoding-errors
Browse files Browse the repository at this point in the history
Improve schema decoding errors
  • Loading branch information
DOBEN authored Feb 7, 2025
2 parents e5ed005 + cc4bf0e commit 1bd199e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 30 deletions.
1 change: 1 addition & 0 deletions backend-rust/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Database schema version: 2

### Fixed

- Fix button to display the schema decoding error at front-end by returning the error as an object.
- Fix typo in `versions` endpoint.
- Fix unit conversion for `avg_finalization_time` in `Query::block_metrics`.
- Issue for `Query::transaction_metrics` producing an internal error when query period is beyond the genesis block.
Expand Down
1 change: 1 addition & 0 deletions backend-rust/src/migrations/m0001-initialize.sql
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ CREATE INDEX block_special_transaction_outcomes_idx

-- Function for generating a table where each row is a bucket.
-- Used by metrics queries.
-- This function is replaced with a function of the same name in migration file `m0002`.
CREATE OR REPLACE FUNCTION date_bin_series(bucket_size interval, starting TIMESTAMPTZ, ending TIMESTAMPTZ)
RETURNS TABLE(bucket_start TIMESTAMPTZ, bucket_end TIMESTAMPTZ) AS $$
SELECT
Expand Down
81 changes: 51 additions & 30 deletions backend-rust/src/transaction_event/smart_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use concordium_rust_sdk::base::{
},
smart_contracts::ReceiveName,
};
use serde::Serialize;

#[derive(Enum, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ContractVersion {
Expand Down Expand Up @@ -118,7 +119,7 @@ impl ContractInitialized {
opt_event_schema.as_ref(),
log,
SmartContractSchemaNames::Event,
);
)?;

connection.edges.push(connection::Edge::new(index.to_string(), decoded_log));
}
Expand Down Expand Up @@ -190,7 +191,7 @@ impl ContractUpdated {
opt_receive_param_schema.as_ref(),
&self.input_parameter,
SmartContractSchemaNames::InputParameterReceiveFunction,
);
)?;

Ok(Some(decoded_input_parameter))
}
Expand Down Expand Up @@ -250,7 +251,7 @@ impl ContractUpdated {
opt_event_schema.as_ref(),
log,
SmartContractSchemaNames::Event,
);
)?;

connection.edges.push(connection::Edge::new(index.to_string(), decoded_log));
}
Expand Down Expand Up @@ -331,7 +332,7 @@ impl ContractInterrupted {
opt_event_schema.as_ref(),
log,
SmartContractSchemaNames::Event,
);
)?;

connection.edges.push(connection::Edge::new(index.to_string(), decoded_log));
}
Expand Down Expand Up @@ -394,38 +395,55 @@ impl SmartContractSchemaNames {
}
}

/// Schema decoding error reported to the front-end.
#[derive(Serialize)]
struct SchemaDecodingError {
error: String,
}

fn decode_value_with_schema(
opt_schema: Option<&Type>,
value: &[u8],
schema_name: SmartContractSchemaNames,
) -> String {
) -> Result<String, ApiError> {
let Some(schema) = opt_schema else {
// Note: There could be something better displayed than this string if no schema
// is available for decoding at the frontend long-term.
return format!(
"No embedded {} schema in smart contract available for decoding",
schema_name.kind()
);
return serde_json::to_string(&SchemaDecodingError {
error: format!(
"No embedded {} schema in smart contract available for decoding",
schema_name.kind()
),
}).map_err(|_| ApiError::InternalError("Should be valid error string".to_string()));
};

let mut cursor = Cursor::new(&value);

match schema.to_json(&mut cursor) {
Ok(v) => {
serde_json::to_string(&v).unwrap_or_else(|e| {
// We don't return an error here since the query is correctly formed and
// the CCDScan backend is working as expected.
// A wrong/missing schema is a mistake by the smart contract
// developer which in general cannot be fixed after the deployment of
// the contract. We display the error message (instead of the decoded
// value) in the block explorer to make the info visible to the smart
// contract developer for debugging purposes here.
format!(
"Failed to deserialize {} with {} schema into string: {:?}",
schema_name.value(),
schema_name.kind(),
e
)
})
match serde_json::to_string(&v) {
Ok(v) => Ok(v),
Err(error) => {
// We don't return an error here since the query is correctly formed and
// the CCDScan backend is working as expected.
// A wrong/missing schema is a mistake by the smart contract
// developer which in general cannot be fixed after the deployment of
// the contract. We display the error message (instead of the decoded
// value) in the block explorer to make the info visible to the smart
// contract developer for debugging purposes here.
Ok(serde_json::to_string(&SchemaDecodingError {
error: format!(
"Failed to deserialize {} with {} schema into string: {:?}",
schema_name.value(),
schema_name.kind(),
error
),
})
.map_err(|_| {
ApiError::InternalError("Should be valid error string".to_string())
})?)
}
}
}
Err(e) => {
// We don't return an error here since the query is correctly formed and
Expand All @@ -435,12 +453,15 @@ fn decode_value_with_schema(
// the contract. We display the error message (instead of the decoded
// value) in the block explorer to make the info visible to the smart
// contract developer for debugging purposes here.
format!(
"Failed to deserialize {} with {} schema: {:?}",
schema_name.value(),
schema_name.kind(),
e.display(true)
)
Ok(serde_json::to_string(&SchemaDecodingError {
error: format!(
"Failed to deserialize {} with {} schema: {:?}",
schema_name.value(),
schema_name.kind(),
e.display(true)
),
})
.map_err(|_| ApiError::InternalError("Should be valid error string".to_string()))?)
}
}
}
Expand Down

0 comments on commit 1bd199e

Please sign in to comment.