Skip to content

Commit

Permalink
Improve schema decoding errors
Browse files Browse the repository at this point in the history
  • Loading branch information
DOBEN committed Feb 7, 2025
1 parent fc01519 commit ae2087d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 31 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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
80 changes: 50 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,8 @@ use concordium_rust_sdk::base::{
},
smart_contracts::ReceiveName,
};
use serde::Serialize;
use serde_json::to_string;

#[derive(Enum, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ContractVersion {
Expand Down Expand Up @@ -118,7 +120,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 +192,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 +252,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 +333,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 +396,53 @@ 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 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(_) => {
// 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(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())
})?)
}
}
}
Err(e) => {
// We don't return an error here since the query is correctly formed and
Expand All @@ -435,12 +452,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(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 ae2087d

Please sign in to comment.