|
1 | 1 | use std::collections::HashSet;
|
2 |
| -use std::fs::{File, OpenOptions}; |
| 2 | +use std::fs::{self, File, OpenOptions}; |
3 | 3 | use std::io::{Read, Write};
|
4 | 4 | use std::marker::PhantomData;
|
5 | 5 | use std::path::{Path, PathBuf};
|
@@ -57,6 +57,7 @@ pub struct CacheOptions {
|
57 | 57 | }
|
58 | 58 |
|
59 | 59 | pub struct CacheInner {
|
| 60 | + /// The directory in which the Wasm blobs are stored in the file system. |
60 | 61 | wasm_path: PathBuf,
|
61 | 62 | /// Instances memory limit in bytes. Use a value that is divisible by the Wasm page size 65536,
|
62 | 63 | /// e.g. full MiBs.
|
@@ -161,6 +162,25 @@ where
|
161 | 162 | Ok(checksum)
|
162 | 163 | }
|
163 | 164 |
|
| 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 | + |
164 | 184 | /// Retrieves a Wasm blob that was previously stored via save_wasm.
|
165 | 185 | /// When the cache is instantiated with the same base dir, this finds Wasm files on disc across multiple cache instances (i.e. node restarts).
|
166 | 186 | /// 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
|
364 | 384 | Ok(wasm)
|
365 | 385 | }
|
366 | 386 |
|
| 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 | + |
367 | 404 | #[cfg(test)]
|
368 | 405 | mod tests {
|
369 | 406 | use super::*;
|
@@ -573,6 +610,37 @@ mod tests {
|
573 | 610 | }
|
574 | 611 | }
|
575 | 612 |
|
| 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 | + |
576 | 644 | #[test]
|
577 | 645 | fn get_instance_finds_cached_module() {
|
578 | 646 | let cache = unsafe { Cache::new(make_testing_options()).unwrap() };
|
@@ -988,6 +1056,23 @@ mod tests {
|
988 | 1056 | assert_eq!(code, loaded);
|
989 | 1057 | }
|
990 | 1058 |
|
| 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 | + |
991 | 1076 | #[test]
|
992 | 1077 | fn analyze_works() {
|
993 | 1078 | let cache: Cache<MockApi, MockStorage, MockQuerier> =
|
|
0 commit comments