From ffbe6d94a487ce5d49d5228044b249e1e5857a0c Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Fri, 7 Feb 2025 14:41:43 +0100 Subject: [PATCH] Improve schema decoding errors --- ...34621569d7e66c60aba8cb962054f82a8c2c6.json | 2 +- backend-rust/CHANGELOG.md | 2 + .../src/migrations/m0001-initialize.sql | 1 + .../src/transaction_event/smart_contracts.rs | 80 ++++++++++++------- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/backend-rust/.sqlx/query-8b160d1140d7823bcaf03711b9934621569d7e66c60aba8cb962054f82a8c2c6.json b/backend-rust/.sqlx/query-8b160d1140d7823bcaf03711b9934621569d7e66c60aba8cb962054f82a8c2c6.json index 33e5c081c..28ac310ef 100644 --- a/backend-rust/.sqlx/query-8b160d1140d7823bcaf03711b9934621569d7e66c60aba8cb962054f82a8c2c6.json +++ b/backend-rust/.sqlx/query-8b160d1140d7823bcaf03711b9934621569d7e66c60aba8cb962054f82a8c2c6.json @@ -6,7 +6,7 @@ { "ordinal": 0, "name": "hash", - "type_info": "Bpchar" + "type_info": "Varchar" }, { "ordinal": 1, diff --git a/backend-rust/CHANGELOG.md b/backend-rust/CHANGELOG.md index e27bbe6af..173e7d999 100644 --- a/backend-rust/CHANGELOG.md +++ b/backend-rust/CHANGELOG.md @@ -16,12 +16,14 @@ Database schema version: 2 ### Changed + - Make the `Query::transaction_metrics` use fixed buckets making it consistent with behavior of the old .NET backend. - Change the log level of when starting the preprocessing of a block into DEBUG instead of INFO. - Query `Token::token_events` and `Query::tokens` now outputs the events in the order of newest->oldest, instead of oldest->newest. ### Fixed +- Change type of schema decoding error to an object to enable button to display the object at front-end. - 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. diff --git a/backend-rust/src/migrations/m0001-initialize.sql b/backend-rust/src/migrations/m0001-initialize.sql index 16fd4d1d4..129ce4d48 100644 --- a/backend-rust/src/migrations/m0001-initialize.sql +++ b/backend-rust/src/migrations/m0001-initialize.sql @@ -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 diff --git a/backend-rust/src/transaction_event/smart_contracts.rs b/backend-rust/src/transaction_event/smart_contracts.rs index 6e6fdf0ea..ed7435b06 100644 --- a/backend-rust/src/transaction_event/smart_contracts.rs +++ b/backend-rust/src/transaction_event/smart_contracts.rs @@ -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 { @@ -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)); } @@ -190,7 +192,7 @@ impl ContractUpdated { opt_receive_param_schema.as_ref(), &self.input_parameter, SmartContractSchemaNames::InputParameterReceiveFunction, - ); + )?; Ok(Some(decoded_input_parameter)) } @@ -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)); } @@ -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)); } @@ -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 { 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 @@ -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()))?) } } }