diff --git a/src/kernel_sdk/CHANGES.md b/src/kernel_sdk/CHANGES.md index 6ce736bc0a07e93c66692409dc2541e61b80d3b8..afa4ef52cc5dcc6bf423bc2ad6ef2d34882e2ea4 100644 --- a/src/kernel_sdk/CHANGES.md +++ b/src/kernel_sdk/CHANGES.md @@ -12,6 +12,7 @@ - Add `KERNEL_BOOT_PATH` constant to `tezos-smart-rollup-host`. - `tezos-smart-rollup-installer-config`: add `eval_config_instr` and `upgrade_reveal_flow` functions to simplify kernel upgrades. +- Update `Runtime::store_write` to write the whole slice; previously errored on slices longer than 2KB. ### Installer client/kernel diff --git a/src/kernel_sdk/host/src/runtime.rs b/src/kernel_sdk/host/src/runtime.rs index 533c5040470888d94b1a79f9c03fbf5f1c2e46ce..d86b2925aad7a13bd421ed8094d82a7c45445337 100644 --- a/src/kernel_sdk/host/src/runtime.rs +++ b/src/kernel_sdk/host/src/runtime.rs @@ -124,8 +124,11 @@ pub trait Runtime { #[cfg(feature = "alloc")] fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError>; - /// Write the bytes given by `src` to storage at `path`, starting - /// `at_offset`. Fails if `src.len() > MAX_FILE_CHUNK_SIZE`. + /// Write the bytes given by `src` to storage at `path`, starting `at_offset`. + /// + /// Contrary to `store_write_all`, this does not replace the value (if any) + /// previously stored under `path`. This allows for splicing/patching values + /// directly in storage, without having to read the entire value from disk. fn store_write( &mut self, path: &T, @@ -133,10 +136,10 @@ pub trait Runtime { at_offset: usize, ) -> Result<(), RuntimeError>; - /// Write the bytes given by `src` to storage at `path`, starting from `0`. - /// Contrary to `store_write`, it can write more than `MAX_FILE_CHUNK_SIZE`. - /// Note that if any value exists at `path`, it will be removed - /// beforehand. + /// Write the bytes given by `src` to storage at `path`. + /// + /// Contrary to `store_write`, this replaces the value (if any) that + /// was previously stored at `path`. #[cfg(feature = "proto-nairobi")] fn store_write_all( &mut self, @@ -403,22 +406,43 @@ where fn store_write( &mut self, path: &T, - src: &[u8], - at_offset: usize, + mut src: &[u8], + mut at_offset: usize, ) -> Result<(), RuntimeError> { - let result_code = unsafe { - SmartRollupCore::store_write( - self, - path.as_ptr(), - path.size(), - at_offset, - src.as_ptr(), - src.len(), - ) + use tezos_smart_rollup_core::MAX_FILE_CHUNK_SIZE; + + let write = |bytes: &[u8], offset| { + let result_code = unsafe { + SmartRollupCore::store_write( + self, + path.as_ptr(), + path.size(), + offset, + bytes.as_ptr(), + bytes.len(), + ) + }; + match Error::wrap(result_code) { + Ok(_) => Ok(()), + Err(e) => Err(RuntimeError::HostErr(e)), + } }; - match Error::wrap(result_code) { - Ok(_) => Ok(()), - Err(e) => Err(RuntimeError::HostErr(e)), + + if src.len() <= MAX_FILE_CHUNK_SIZE { + return write(src, at_offset); + } + + while src.len() > MAX_FILE_CHUNK_SIZE { + write(&src[..MAX_FILE_CHUNK_SIZE], at_offset)?; + at_offset += MAX_FILE_CHUNK_SIZE; + src = &src[MAX_FILE_CHUNK_SIZE..]; + } + + // Don't do final extra write of zero bytes + if !src.is_empty() { + write(src, at_offset) + } else { + Ok(()) } } @@ -428,7 +452,6 @@ where path: &T, value: &[u8], ) -> Result<(), RuntimeError> { - use tezos_smart_rollup_core::MAX_FILE_CHUNK_SIZE; // Removing the value first prevents some cases where a value already // exists in the storage at the given path and is bigger than the one // being written. Keeping the old value would lead to a situation where @@ -443,16 +466,7 @@ where // exists, hence there is no need to check the value exists beforehand. Runtime::store_delete_value(self, path)?; - // To be consistent with `store_write` that allocates the path even if - // the written value is empty, as nothing will be written otherwise. - if value.is_empty() { - Runtime::store_write(self, path, &[0; 0], 0)?; - } - - for (i, chunk) in value.chunks(MAX_FILE_CHUNK_SIZE).enumerate() { - Runtime::store_write(self, path, chunk, i * MAX_FILE_CHUNK_SIZE)?; - } - Ok(()) + Runtime::store_write(self, path, value, 0) } fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { diff --git a/src/kernel_sdk/installer-config/src/binary/preimage.rs b/src/kernel_sdk/installer-config/src/binary/preimage.rs index e48589927ada90fb872b2656305340642e493680..f4a6a354b2367bcfe89a851649a1c7adf8a9878e 100644 --- a/src/kernel_sdk/installer-config/src/binary/preimage.rs +++ b/src/kernel_sdk/installer-config/src/binary/preimage.rs @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: MIT -use tezos_smart_rollup_core::MAX_FILE_CHUNK_SIZE; use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; use tezos_smart_rollup_encoding::dac::pages::reveal_loop; use tezos_smart_rollup_encoding::dac::pages::V0SliceContentPage; @@ -57,18 +56,9 @@ fn append_content( ) -> Result { let content = content.as_ref(); - let mut size_written = 0; - while size_written < content.len() { - let num_to_write = usize::min(MAX_FILE_CHUNK_SIZE, content.len() - size_written); - let bytes_to_write = &content[size_written..(size_written + num_to_write)]; - - Runtime::store_write(host, reveal_to, bytes_to_write, kernel_size + size_written) - .map_err(|_| "Failed to write kernel content page")?; - - size_written += num_to_write; - } - - Ok(size_written) + host.store_write(reveal_to, content, kernel_size) + .map_err(|_| "Failed to write kernel content page")?; + Ok(content.len()) } #[cfg(test)]