diff --git a/e2e/src/main.rs b/e2e/src/main.rs index 28015a832..0cea1cbe3 100644 --- a/e2e/src/main.rs +++ b/e2e/src/main.rs @@ -7,6 +7,7 @@ pub mod tests { use std::num::NonZeroU16; use avail_core::{AppExtrinsic, AppId, Keccak256}; + use avail_subxt::api; use avail_subxt::{ api::runtime_types::avail_core::header::extension::HeaderExtension, avail::{Cells, GDataProof, GRawScalar, GRow, Rows}, @@ -303,6 +304,33 @@ pub mod tests { Ok(()) } + #[async_std::test] + pub async fn empty_commitments_test() -> Result<()> { + let client = establish_a_connection().await.unwrap(); + let alice = dev::alice(); + // other than DA tx + let call = api::tx().system().remark(b"Hi".to_vec()); + let block_hash = tx::send_then_finalized(&client, &call, &alice, AppId(0)) + .await? + .block_hash(); + + // query_rows should fail for block with empty commitments + let row_indexes = Rows::truncate_from(vec![0]); + let rows = client + .rpc_methods() + .query_rows(row_indexes, block_hash) + .await; + assert!(rows.is_err()); + + // query_proof should fail for block with empty commitments + let cell = Cell { row: 0, col: 0 }; + let cells = Cells::try_from(vec![cell.clone()]).unwrap(); + + let proof = client.rpc_methods().query_proof(cells, block_hash).await; + assert!(proof.is_err()); + Ok(()) + } + #[async_std::test] pub async fn rpc_query_block_length_test() -> Result<()> { let client = establish_a_connection().await.unwrap(); diff --git a/pallets/system/src/header_builder/version_select.rs b/pallets/system/src/header_builder/version_select.rs index baf130c35..46d5350a3 100644 --- a/pallets/system/src/header_builder/version_select.rs +++ b/pallets/system/src/header_builder/version_select.rs @@ -88,6 +88,10 @@ pub fn build_extension( seed: Seed, version: HeaderVersion, ) -> HeaderExtension { + // Blocks with non-DA extrinsics will have empty commitments + if submitted.is_empty() { + return get_empty_header(data_root, version); + } let build_extension_start = Instant::now(); // Build the grid diff --git a/rpc/kate-rpc/src/lib.rs b/rpc/kate-rpc/src/lib.rs index 3656d0112..2ae05523b 100644 --- a/rpc/kate-rpc/src/lib.rs +++ b/rpc/kate-rpc/src/lib.rs @@ -1,5 +1,8 @@ use avail_base::metrics::avail::KateRpcMetrics; -use avail_core::{data_proof::ProofResponse, traits::ExtendedHeader, AppId, OpaqueExtrinsic}; +use avail_core::{ + data_proof::ProofResponse, header::HeaderExtension, traits::ExtendedHeader, AppId, + OpaqueExtrinsic, +}; use da_control::kate::{GDataProof, GRow}; use da_runtime::apis::{DataAvailApi, KateApi as RTKateApi}; use kate::com::Cell; @@ -112,7 +115,7 @@ type Api<'a, C, B> = ApiRef<'a, >::Api>; impl Kate where Block: BlockT, - ::Header: ExtendedHeader, + ::Header: ExtendedHeader, Client: Send + Sync + 'static, Client: HeaderBackend + ProvideRuntimeApi + BlockBackend, Client::Api: DataAvailApi, @@ -128,20 +131,21 @@ where u32, BlockLength, Opaques, + ::Header, )> { let at = self.at_or_best(at); let block = self.get_finalized_block(Some(at))?.block; let number: u32 = (*block.header().number()) .try_into() .map_err(|_| ErrorCode::InvalidParams)?; - let (_, extrinsics) = block.deconstruct(); + let (header, extrinsics) = block.deconstruct(); let api = self.client.runtime_api(); let block_len = api .block_length(at) .map_err(|e| internal_err!("Length of best block({at:?}): {e:?}"))?; - Ok((api, at, number, block_len, extrinsics)) + Ok((api, at, number, block_len, extrinsics, header)) } fn at_or_best(&self, at: Option<::Hash>) -> ::Hash { @@ -180,14 +184,21 @@ where impl KateApiServer for Kate where Block: BlockT, - ::Header: ExtendedHeader, + ::Header: ExtendedHeader, Client: Send + Sync + 'static, Client: HeaderBackend + ProvideRuntimeApi + BlockBackend, Client::Api: DataAvailApi + RTKateApi, { async fn query_rows(&self, rows: Rows, at: Option>) -> RpcResult> { - let (api, at, number, block_len, extrinsics) = self.scope(at)?; - + let (api, at, number, block_len, extrinsics, header) = self.scope(at)?; + + match header.extension() { + HeaderExtension::V3(ext) => { + if ext.commitment.commitment.is_empty() { + return Err(internal_err!("Requested block {at} has empty commitments")); + } + }, + }; let execution_start = Instant::now(); let grid_rows = api .rows(at, number, extrinsics, block_len, rows.into()) @@ -203,7 +214,7 @@ where app_id: AppId, at: Option>, ) -> RpcResult>> { - let (api, at, number, block_len, extrinsics) = self.scope(at)?; + let (api, at, number, block_len, extrinsics, _) = self.scope(at)?; let execution_start = Instant::now(); let app_data = api @@ -230,8 +241,14 @@ where ); } - let (api, at, number, block_len, extrinsics) = self.scope(at)?; - + let (api, at, number, block_len, extrinsics, header) = self.scope(at)?; + match header.extension() { + HeaderExtension::V3(ext) => { + if ext.commitment.commitment.is_empty() { + return Err(internal_err!("Requested block {at} has empty commitments")); + } + }, + }; let execution_start = Instant::now(); let cells = cells .into_iter() @@ -269,7 +286,7 @@ where at: Option>, ) -> RpcResult { // Calculate proof for block and tx index - let (api, at, number, _, extrinsics) = self.scope(at)?; + let (api, at, number, _, extrinsics, _) = self.scope(at)?; let execution_start = Instant::now(); let proof = api diff --git a/rpc/kate-rpc/src/metrics.rs b/rpc/kate-rpc/src/metrics.rs index 1fd5a3b8c..8bcc7d27f 100644 --- a/rpc/kate-rpc/src/metrics.rs +++ b/rpc/kate-rpc/src/metrics.rs @@ -1,6 +1,6 @@ use crate::{Cells, HashOf, Kate, KateApiServer, ProofResponse, Rows}; -use avail_core::{traits::ExtendedHeader, AppId, OpaqueExtrinsic}; +use avail_core::{header::HeaderExtension, traits::ExtendedHeader, AppId, OpaqueExtrinsic}; use da_runtime::apis::DataAvailApi; use crate::RTKateApi; @@ -60,7 +60,7 @@ where impl KateApiMetricsServer for Kate where Block: BlockT, - ::Header: ExtendedHeader, + ::Header: ExtendedHeader, Client: Send + Sync + 'static, Client: HeaderBackend + ProvideRuntimeApi + BlockBackend, Client::Api: DataAvailApi + RTKateApi,