Skip to content

Commit 36ca9e7

Browse files
authored
Merge pull request #566 from CosmWasm/replace-range
Replace Storage::range with ::scan and ::next
2 parents 3f7cd30 + 30489a6 commit 36ca9e7

File tree

10 files changed

+196
-343
lines changed

10 files changed

+196
-343
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# CHANGELOG
22

3+
## 0.12.0 (unreleased)
4+
5+
**cosmwasm-vm**
6+
7+
- Remove `Storage::range` and `StorageIterator`. The storage implementation is
8+
now responsible for maintaining iterators internally and make them accessible
9+
via the new `Storage::scan` and `Storage::next` methods.
10+
- Add `FfiError::IteratorDoesNotExist`. Looking at this, `FfiError` should
11+
probably be renamed to something that includes before, on and behind the FFI
12+
boundary to Go.
13+
- `MockStorage` now implementes the new `Storage` trait and has an additional
14+
`MockStorage::all` for getting all elements of an iterator in tests.
15+
316
## 0.11.1 (2020-10-12)
417

518
**cosmwasm-std**

contracts/burner/tests/integration.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use cosmwasm_std::{
2121
coins, BankMsg, ContractResult, HumanAddr, InitResponse, MigrateResponse, Order,
2222
};
2323
use cosmwasm_vm::testing::{init, migrate, mock_env, mock_info, mock_instance, MOCK_CONTRACT_ADDR};
24-
use cosmwasm_vm::StorageIterator;
2524

2625
use burner::msg::{InitMsg, MigrateMsg};
2726
use cosmwasm_vm::Storage;
@@ -55,13 +54,8 @@ fn migrate_cleans_up_data() {
5554
storage.set(b"foo", b"bar").0.unwrap();
5655
storage.set(b"key2", b"data2").0.unwrap();
5756
storage.set(b"key3", b"cool stuff").0.unwrap();
58-
let cnt = storage
59-
.range(None, None, Order::Ascending)
60-
.0
61-
.unwrap()
62-
.elements()
63-
.unwrap()
64-
.len();
57+
let iter_id = storage.scan(None, None, Order::Ascending).0.unwrap();
58+
let cnt = storage.all(iter_id).0.unwrap().len();
6559
assert_eq!(3, cnt);
6660
Ok(())
6761
})
@@ -89,13 +83,8 @@ fn migrate_cleans_up_data() {
8983

9084
// check there is no data in storage
9185
deps.with_storage(|storage| {
92-
let cnt = storage
93-
.range(None, None, Order::Ascending)
94-
.0
95-
.unwrap()
96-
.elements()
97-
.unwrap()
98-
.len();
86+
let iter_id = storage.scan(None, None, Order::Ascending).0.unwrap();
87+
let cnt = storage.all(iter_id).0.unwrap().len();
9988
assert_eq!(0, cnt);
10089
Ok(())
10190
})

packages/vm/src/context.rs

Lines changed: 6 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
//! Internal details to be used by instance.rs only
2-
#[cfg(feature = "iterator")]
3-
use std::collections::HashMap;
4-
#[cfg(feature = "iterator")]
5-
use std::convert::TryInto;
2+
63
use std::ffi::c_void;
7-
#[cfg(not(feature = "iterator"))]
8-
use std::marker::PhantomData;
94
use std::ptr::NonNull;
105

116
use wasmer_runtime_core::{
@@ -17,8 +12,6 @@ use wasmer_runtime_core::{
1712
use crate::backends::decrease_gas_left;
1813
use crate::errors::{VmError, VmResult};
1914
use crate::ffi::GasInfo;
20-
#[cfg(feature = "iterator")]
21-
use crate::traits::StorageIterator;
2215
use crate::traits::{Querier, Storage};
2316

2417
/** context data **/
@@ -70,17 +63,13 @@ impl GasState {
7063
}
7164
}
7265

73-
struct ContextData<'a, S: Storage, Q: Querier> {
66+
struct ContextData<S: Storage, Q: Querier> {
7467
gas_state: GasState,
7568
storage: Option<S>,
7669
storage_readonly: bool,
7770
querier: Option<Q>,
7871
/// A non-owning link to the wasmer instance
7972
wasmer_instance: Option<NonNull<WasmerInstance>>,
80-
#[cfg(feature = "iterator")]
81-
iterators: HashMap<u32, Box<dyn StorageIterator + 'a>>,
82-
#[cfg(not(feature = "iterator"))]
83-
iterators: PhantomData<&'a mut ()>,
8473
}
8574

8675
pub fn setup_context<S: Storage, Q: Querier>(gas_limit: u64) -> (*mut c_void, fn(*mut c_void)) {
@@ -97,10 +86,6 @@ fn create_unmanaged_context_data<S: Storage, Q: Querier>(gas_limit: u64) -> *mut
9786
storage_readonly: true,
9887
querier: None,
9988
wasmer_instance: None,
100-
#[cfg(feature = "iterator")]
101-
iterators: HashMap::new(),
102-
#[cfg(not(feature = "iterator"))]
103-
iterators: PhantomData::default(),
10489
};
10590
let heap_data = Box::new(data); // move from stack to heap
10691
Box::into_raw(heap_data) as *mut c_void // give up ownership
@@ -109,43 +94,22 @@ fn create_unmanaged_context_data<S: Storage, Q: Querier>(gas_limit: u64) -> *mut
10994
fn destroy_unmanaged_context_data<S: Storage, Q: Querier>(ptr: *mut c_void) {
11095
if !ptr.is_null() {
11196
// obtain ownership and drop instance of ContextData when box gets out of scope
112-
let mut dying = unsafe { Box::from_raw(ptr as *mut ContextData<S, Q>) };
113-
// Ensure all iterators are dropped before the storage
114-
destroy_iterators(&mut dying);
97+
let _dying = unsafe { Box::from_raw(ptr as *mut ContextData<S, Q>) };
11598
}
11699
}
117100

118101
/// Get a mutable reference to the context's data. Ownership remains in the Context.
119-
// NOTE: This is actually not really implemented safely at the moment. I did this as a
120-
// nicer and less-terrible version of the previous solution to the following issue:
121-
//
122-
// +--->> Go pointer
123-
// |
124-
// Ctx ->> ContextData +-> iterators: Box<dyn Iterator + 'a> --+
125-
// | |
126-
// +-> storage: impl Storage <<------------+
127-
// |
128-
// +-> querier: impl Querier
129-
//
130-
// -> : Ownership
131-
// ->> : Mutable borrow
132-
//
133-
// As you can see, there's a cyclical reference here... changing this function to return the same lifetime as it
134-
// returns (and adjusting a few other functions to only have one lifetime instead of two) triggers an error
135-
// elsewhere where we try to add iterators to the context. That's not legal according to Rust's rules, and it
136-
// complains that we're trying to borrow ctx mutably twice. This needs a better solution because this function
137-
// probably triggers unsoundness.
138102
fn get_context_data_mut<'a, 'b, S: Storage, Q: Querier>(
139103
ctx: &'a mut Ctx,
140-
) -> &'b mut ContextData<'b, S, Q> {
104+
) -> &'b mut ContextData<S, Q> {
141105
unsafe {
142106
let ptr = ctx.data as *mut ContextData<S, Q>;
143107
ptr.as_mut()
144108
.expect("The pointer ctx.data was null in get_context_data_mut; this is a bug.")
145109
}
146110
}
147111

148-
fn get_context_data<'a, 'b, S: Storage, Q: Querier>(ctx: &'a Ctx) -> &'b ContextData<'b, S, Q> {
112+
fn get_context_data<'a, 'b, S: Storage, Q: Querier>(ctx: &'a Ctx) -> &'b ContextData<S, Q> {
149113
unsafe {
150114
let ptr = ctx.data as *mut ContextData<S, Q>;
151115
ptr.as_ref()
@@ -164,25 +128,12 @@ pub fn set_wasmer_instance<S: Storage, Q: Querier>(
164128
}
165129
}
166130

167-
#[cfg(feature = "iterator")]
168-
fn destroy_iterators<S: Storage, Q: Querier>(context: &mut ContextData<S, Q>) {
169-
context.iterators.clear();
170-
}
171-
172-
#[cfg(not(feature = "iterator"))]
173-
fn destroy_iterators<S: Storage, Q: Querier>(_context: &mut ContextData<S, Q>) {}
174-
175131
/// Returns the original storage and querier as owned instances, and closes any remaining
176132
/// iterators. This is meant to be called when recycling the instance.
177133
pub(crate) fn move_out_of_context<S: Storage, Q: Querier>(
178134
source: &mut Ctx,
179135
) -> (Option<S>, Option<Q>) {
180-
let mut b = get_context_data_mut::<S, Q>(source);
181-
// Destroy all existing iterators which are (in contrast to the storage)
182-
// not reused between different instances.
183-
// This is also important because the iterators are pointers to Go memory which should not be stored long term
184-
// Paragraphs 5-7: https://golang.org/cmd/cgo/#hdr-Passing_pointers
185-
destroy_iterators(&mut b);
136+
let b = get_context_data_mut::<S, Q>(source);
186137
(b.storage.take(), b.querier.take())
187138
}
188139

@@ -270,28 +221,6 @@ pub fn set_storage_readonly<S: Storage, Q: Querier>(ctx: &mut Ctx, new_value: bo
270221
context_data.storage_readonly = new_value;
271222
}
272223

273-
/// Add the iterator to the context's data. A new ID is assigned and returned.
274-
/// IDs are guaranteed to be in the range [0, 2**31-1], i.e. fit in the non-negative part if type i32.
275-
#[cfg(feature = "iterator")]
276-
#[must_use = "without the returned iterator ID, the iterator cannot be accessed"]
277-
pub fn add_iterator<'a, S: Storage, Q: Querier>(
278-
ctx: &mut Ctx,
279-
iter: Box<dyn StorageIterator + 'a>,
280-
) -> u32 {
281-
let b = get_context_data_mut::<S, Q>(ctx);
282-
let last_id: u32 = b
283-
.iterators
284-
.len()
285-
.try_into()
286-
.expect("Found more iterator IDs than supported");
287-
let new_id = last_id + 1;
288-
if new_id > (i32::MAX as u32) {
289-
panic!("Iterator ID exceeded i32::MAX. This must not happen.");
290-
}
291-
b.iterators.insert(new_id, iter);
292-
new_id
293-
}
294-
295224
pub(crate) fn with_func_from_context<S, Q, Args, Rets, Callback, CallbackData>(
296225
ctx: &mut Ctx,
297226
name: &str,
@@ -346,31 +275,11 @@ where
346275
}
347276
}
348277

349-
#[cfg(feature = "iterator")]
350-
pub(crate) fn with_iterator_from_context<'a, 'b, S: 'b, Q: 'b, F, T>(
351-
ctx: &'a mut Ctx,
352-
iterator_id: u32,
353-
func: F,
354-
) -> VmResult<T>
355-
where
356-
S: Storage,
357-
Q: Querier,
358-
F: FnOnce(&'b mut (dyn StorageIterator + 'b)) -> VmResult<T>,
359-
{
360-
let b = get_context_data_mut::<S, Q>(ctx);
361-
match b.iterators.get_mut(&iterator_id) {
362-
Some(iterator) => func(iterator),
363-
None => Err(VmError::iterator_does_not_exist(iterator_id)),
364-
}
365-
}
366-
367278
#[cfg(test)]
368279
mod test {
369280
use super::*;
370281
use crate::backends::{compile, decrease_gas_left, set_gas_left};
371282
use crate::errors::VmError;
372-
#[cfg(feature = "iterator")]
373-
use crate::testing::MockIterator;
374283
use crate::testing::{MockQuerier, MockStorage};
375284
use crate::traits::Storage;
376285
use cosmwasm_std::{
@@ -536,29 +445,6 @@ mod test {
536445
assert_eq!(is_storage_readonly::<MS, MQ>(ctx), true);
537446
}
538447

539-
#[test]
540-
#[cfg(feature = "iterator")]
541-
fn add_iterator_works() {
542-
let mut instance = make_instance();
543-
let ctx = instance.context_mut();
544-
leave_default_data(ctx);
545-
546-
assert_eq!(get_context_data_mut::<MS, MQ>(ctx).iterators.len(), 0);
547-
let id1 = add_iterator::<MS, MQ>(ctx, Box::new(MockIterator::empty()));
548-
let id2 = add_iterator::<MS, MQ>(ctx, Box::new(MockIterator::empty()));
549-
let id3 = add_iterator::<MS, MQ>(ctx, Box::new(MockIterator::empty()));
550-
assert_eq!(get_context_data_mut::<MS, MQ>(ctx).iterators.len(), 3);
551-
assert!(get_context_data_mut::<MS, MQ>(ctx)
552-
.iterators
553-
.contains_key(&id1));
554-
assert!(get_context_data_mut::<MS, MQ>(ctx)
555-
.iterators
556-
.contains_key(&id2));
557-
assert!(get_context_data_mut::<MS, MQ>(ctx)
558-
.iterators
559-
.contains_key(&id3));
560-
}
561-
562448
#[test]
563449
fn with_func_from_context_works() {
564450
let mut instance = make_instance();
@@ -690,36 +576,4 @@ mod test {
690576
})
691577
.unwrap();
692578
}
693-
694-
#[test]
695-
#[cfg(feature = "iterator")]
696-
fn with_iterator_from_context_works() {
697-
let mut instance = make_instance();
698-
let ctx = instance.context_mut();
699-
leave_default_data(ctx);
700-
701-
let id = add_iterator::<MS, MQ>(ctx, Box::new(MockIterator::empty()));
702-
with_iterator_from_context::<MS, MQ, _, ()>(ctx, id, |iter| {
703-
assert!(iter.next().0.unwrap().is_none());
704-
Ok(())
705-
})
706-
.expect("must not error");
707-
}
708-
709-
#[test]
710-
#[cfg(feature = "iterator")]
711-
fn with_iterator_from_context_errors_for_non_existent_iterator_id() {
712-
let mut instance = make_instance();
713-
let ctx = instance.context_mut();
714-
leave_default_data(ctx);
715-
716-
let missing_id = 42u32;
717-
let result = with_iterator_from_context::<MS, MQ, _, ()>(ctx, missing_id, |_iter| {
718-
panic!("this should not be called");
719-
});
720-
match result.unwrap_err() {
721-
VmError::IteratorDoesNotExist { id, .. } => assert_eq!(id, missing_id),
722-
err => panic!("Unexpected error: {:?}", err),
723-
}
724-
}
725579
}

packages/vm/src/errors/vm_error.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ pub enum VmError {
4343
},
4444
#[snafu(display("Hash doesn't match stored data"))]
4545
IntegrityErr { backtrace: snafu::Backtrace },
46-
#[snafu(display("Iterator with ID {} does not exist", id))]
47-
IteratorDoesNotExist {
48-
id: u32,
49-
backtrace: snafu::Backtrace,
50-
},
5146
#[snafu(display("Error parsing into type {}: {}", target, msg))]
5247
ParseErr {
5348
/// the target type that was attempted
@@ -128,11 +123,6 @@ impl VmError {
128123
IntegrityErr {}.build()
129124
}
130125

131-
#[cfg(feature = "iterator")]
132-
pub(crate) fn iterator_does_not_exist(iterator_id: u32) -> Self {
133-
IteratorDoesNotExist { id: iterator_id }.build()
134-
}
135-
136126
pub(crate) fn parse_err<T: Into<String>, M: Display>(target: T, msg: M) -> Self {
137127
ParseErr {
138128
target: target.into(),
@@ -308,16 +298,6 @@ mod test {
308298
}
309299
}
310300

311-
#[test]
312-
#[cfg(feature = "iterator")]
313-
fn iterator_does_not_exist_works() {
314-
let error = VmError::iterator_does_not_exist(15);
315-
match error {
316-
VmError::IteratorDoesNotExist { id, .. } => assert_eq!(id, 15),
317-
e => panic!("Unexpected error: {:?}", e),
318-
}
319-
}
320-
321301
#[test]
322302
fn parse_err_works() {
323303
let error = VmError::parse_err("Book", "Missing field: title");

0 commit comments

Comments
 (0)