Skip to content

Commit b72579f

Browse files
committed
Add Cache::remove_wasm
1 parent c3cde9b commit b72579f

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ and this project adheres to
3131
get reverted. ([#1513])
3232
- cosmwasm-std: Add new `WasmQuery::CodeInfo` to get the checksum of a code ID
3333
([#1561]).
34+
- cosmwasm-vm: Add `Cache::remove_wasm` to remove obsolete Wasm blobs and their
35+
compiled modules.
3436

3537
[#1436]: https://github.com/CosmWasm/cosmwasm/issues/1436
3638
[#1437]: https://github.com/CosmWasm/cosmwasm/issues/1437

packages/vm/src/cache.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::collections::HashSet;
2-
use std::fs::{File, OpenOptions};
2+
use std::fs::{self, File, OpenOptions};
33
use std::io::{Read, Write};
44
use std::marker::PhantomData;
55
use std::path::{Path, PathBuf};
@@ -57,6 +57,7 @@ pub struct CacheOptions {
5757
}
5858

5959
pub struct CacheInner {
60+
/// The directory in which the Wasm blobs are stored in the file system.
6061
wasm_path: PathBuf,
6162
/// Instances memory limit in bytes. Use a value that is divisible by the Wasm page size 65536,
6263
/// e.g. full MiBs.
@@ -161,6 +162,25 @@ where
161162
Ok(checksum)
162163
}
163164

165+
/// Removes the Wasm blob for the given checksum from disk and its
166+
/// compiled module from the file system cache.
167+
///
168+
/// The existence of the original code is required since the caller (wasmd)
169+
/// has to keep track of which entries we have here.
170+
pub fn remove_wasm(&self, checksum: &Checksum) -> VmResult<()> {
171+
let mut cache = self.inner.lock().unwrap();
172+
173+
// Remove compiled moduled from disk (if it exists).
174+
// Here we could also delete from memory caches but this is not really
175+
// necessary as they are pushed out from the LRU over time or disappear
176+
// when the node process restarts.
177+
cache.fs_cache.remove(checksum)?;
178+
179+
let path = &cache.wasm_path;
180+
remove_wasm_from_disk(path, checksum)?;
181+
Ok(())
182+
}
183+
164184
/// Retrieves a Wasm blob that was previously stored via save_wasm.
165185
/// When the cache is instantiated with the same base dir, this finds Wasm files on disc across multiple cache instances (i.e. node restarts).
166186
/// This function is public to allow a checksum to Wasm lookup in the blockchain.
@@ -364,6 +384,23 @@ fn load_wasm_from_disk(dir: impl Into<PathBuf>, checksum: &Checksum) -> VmResult
364384
Ok(wasm)
365385
}
366386

387+
/// Removes the Wasm blob for the given checksum from disk.
388+
///
389+
/// In contrast to the file system cache, the existence of the original
390+
/// code is required. So a non-existent file leads to an error as it
391+
/// indicates a bug.
392+
fn remove_wasm_from_disk(dir: impl Into<PathBuf>, checksum: &Checksum) -> VmResult<()> {
393+
let path = dir.into().join(checksum.to_hex());
394+
395+
if !path.exists() {
396+
return Err(VmError::cache_err("Wasm file does not exist"));
397+
}
398+
399+
fs::remove_file(path).map_err(|_e| VmError::cache_err("Error removing Wasm file from disk"))?;
400+
401+
Ok(())
402+
}
403+
367404
#[cfg(test)]
368405
mod tests {
369406
use super::*;
@@ -573,6 +610,37 @@ mod tests {
573610
}
574611
}
575612

613+
#[test]
614+
fn remove_wasm_works() {
615+
let cache: Cache<MockApi, MockStorage, MockQuerier> =
616+
unsafe { Cache::new(make_testing_options()).unwrap() };
617+
618+
// Store
619+
let checksum = cache.save_wasm(CONTRACT).unwrap();
620+
621+
// Exists
622+
cache.load_wasm(&checksum).unwrap();
623+
624+
// Remove
625+
cache.remove_wasm(&checksum).unwrap();
626+
627+
// Does not exist anymore
628+
match cache.load_wasm(&checksum).unwrap_err() {
629+
VmError::CacheErr { msg, .. } => {
630+
assert_eq!(msg, "Error opening Wasm file for reading")
631+
}
632+
e => panic!("Unexpected error: {:?}", e),
633+
}
634+
635+
// Removing again fails
636+
match cache.remove_wasm(&checksum).unwrap_err() {
637+
VmError::CacheErr { msg, .. } => {
638+
assert_eq!(msg, "Wasm file does not exist")
639+
}
640+
e => panic!("Unexpected error: {:?}", e),
641+
}
642+
}
643+
576644
#[test]
577645
fn get_instance_finds_cached_module() {
578646
let cache = unsafe { Cache::new(make_testing_options()).unwrap() };
@@ -988,6 +1056,23 @@ mod tests {
9881056
assert_eq!(code, loaded);
9891057
}
9901058

1059+
#[test]
1060+
fn remove_wasm_from_disk_works() {
1061+
let tmp_dir = TempDir::new().unwrap();
1062+
let path = tmp_dir.path();
1063+
let code = vec![12u8; 17];
1064+
let checksum = save_wasm_to_disk(path, &code).unwrap();
1065+
1066+
remove_wasm_from_disk(path, &checksum).unwrap();
1067+
1068+
// removing again fails
1069+
1070+
match remove_wasm_from_disk(path, &checksum).unwrap_err() {
1071+
VmError::CacheErr { msg } => assert_eq!(msg, "Wasm file does not exist"),
1072+
err => panic!("Unexpected error: {:?}", err),
1073+
}
1074+
}
1075+
9911076
#[test]
9921077
fn analyze_works() {
9931078
let cache: Cache<MockApi, MockStorage, MockQuerier> =

0 commit comments

Comments
 (0)