cosmwasm_vm/backend.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
use std::fmt::Debug;
use std::ops::AddAssign;
use std::string::FromUtf8Error;
use thiserror::Error;
use cosmwasm_std::{Binary, ContractResult, SystemResult};
#[cfg(feature = "iterator")]
use cosmwasm_std::{Order, Record};
/// A structure that represents gas cost to be deducted from the remaining gas.
/// This is always needed when computations are performed outside of
/// Wasm execution, such as calling crypto APIs or calls into the blockchain.
///
/// All values are measured in [CosmWasm gas].
///
/// [CosmWasm gas]: https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct GasInfo {
/// The gas cost of a computation that was executed already but not yet charged.
///
/// This could be renamed to `internally_used` for consistency because it is used inside
/// of the `cosmwasm_vm`.
pub cost: u64,
/// Gas that was used and charged externally. This is needed to
/// adjust the VM's gas limit but does not affect the gas usage.
///
/// Since this is measured in [CosmWasm gas], the caller may need
/// to convert from Cosmos SDK gas in cases where an SDK gas meter
/// is used.
///
/// [CosmWasm gas]: https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md
pub externally_used: u64,
}
impl GasInfo {
pub fn new(cost: u64, externally_used: u64) -> Self {
GasInfo {
cost,
externally_used,
}
}
pub fn with_cost(amount: u64) -> Self {
GasInfo {
cost: amount,
externally_used: 0,
}
}
pub fn with_externally_used(amount: u64) -> Self {
GasInfo {
cost: 0,
externally_used: amount,
}
}
/// Creates a gas information with no cost for the caller and with zero externally used gas.
///
/// Caution: when using this you need to make sure no gas was metered externally to keep the gas values in sync.
pub fn free() -> Self {
GasInfo {
cost: 0,
externally_used: 0,
}
}
}
impl AddAssign for GasInfo {
fn add_assign(&mut self, other: Self) {
*self = GasInfo {
cost: self.cost + other.cost,
externally_used: self.externally_used + other.externally_used,
};
}
}
/// Holds all external dependencies of the contract.
/// Designed to allow easy dependency injection at runtime.
/// This cannot be copied or cloned since it would behave differently
/// for mock storages and a bridge storage in the VM.
pub struct Backend<A: BackendApi, S: Storage, Q: Querier> {
pub api: A,
pub storage: S,
pub querier: Q,
}
/// Access to the VM's backend storage, i.e. the chain
pub trait Storage {
/// Returns Err on error.
/// Returns Ok(None) when key does not exist.
/// Returns Ok(Some(Vec<u8>)) when key exists.
///
/// Note: Support for differentiating between a non-existent key and a key with empty value
/// is not great yet and might not be possible in all backends. But we're trying to get there.
fn get(&self, key: &[u8]) -> BackendResult<Option<Vec<u8>>>;
/// Allows iteration over a set of key/value pairs, either forwards or backwards.
/// Returns an iterator ID that is unique within the Storage instance.
///
/// The bound `start` is inclusive and `end` is exclusive.
///
/// If `start` is lexicographically greater than or equal to `end`, an empty range is described, mo matter of the order.
///
/// This call must not change data in the storage, but creating and storing a new iterator can be a mutating operation on
/// the Storage implementation.
/// The implementation must ensure that iterator IDs are assigned in a deterministic manner as this is
/// environment data that is injected into the contract.
#[cfg(feature = "iterator")]
fn scan(
&mut self,
start: Option<&[u8]>,
end: Option<&[u8]>,
order: Order,
) -> BackendResult<u32>;
/// Returns the next element of the iterator with the given ID.
///
/// If the ID is not found, a BackendError::IteratorDoesNotExist is returned.
///
/// This call must not change data in the storage, but incrementing an iterator can be a mutating operation on
/// the Storage implementation.
#[cfg(feature = "iterator")]
fn next(&mut self, iterator_id: u32) -> BackendResult<Option<Record>>;
/// Returns the next value of the iterator with the given ID.
/// Since the iterator is incremented, the corresponding key will never be accessible.
///
/// If the ID is not found, a BackendError::IteratorDoesNotExist is returned.
///
/// The default implementation uses [`Storage::next`] and discards the key.
/// More efficient implementations might be possible depending on the storage.
#[cfg(feature = "iterator")]
fn next_value(&mut self, iterator_id: u32) -> BackendResult<Option<Vec<u8>>> {
let (result, gas_info) = self.next(iterator_id);
let result = result.map(|record| record.map(|(_, v)| v));
(result, gas_info)
}
/// Returns the next key of the iterator with the given ID.
/// Since the iterator is incremented, the corresponding value will never be accessible.
///
/// If the ID is not found, a BackendError::IteratorDoesNotExist is returned.
///
/// The default implementation uses [`Storage::next`] and discards the value.
/// More efficient implementations might be possible depending on the storage.
#[cfg(feature = "iterator")]
fn next_key(&mut self, iterator_id: u32) -> BackendResult<Option<Vec<u8>>> {
let (result, gas_info) = self.next(iterator_id);
let result = result.map(|record| record.map(|(k, _)| k));
(result, gas_info)
}
fn set(&mut self, key: &[u8], value: &[u8]) -> BackendResult<()>;
/// Removes a database entry at `key`.
///
/// The current interface does not allow to differentiate between a key that existed
/// before and one that didn't exist. See https://github.com/CosmWasm/cosmwasm/issues/290
fn remove(&mut self, key: &[u8]) -> BackendResult<()>;
}
/// Callbacks to system functions defined outside of the wasm modules.
/// This is a trait to allow Mocks in the test code.
///
/// Currently it just supports address conversion, we could add eg. crypto functions here.
/// These should all be pure (stateless) functions. If you need state, you probably want
/// to use the Querier.
pub trait BackendApi: Clone + Send {
fn addr_validate(&self, input: &str) -> BackendResult<()>;
fn addr_canonicalize(&self, human: &str) -> BackendResult<Vec<u8>>;
fn addr_humanize(&self, canonical: &[u8]) -> BackendResult<String>;
}
pub trait Querier {
/// This is all that must be implemented for the Querier.
/// This allows us to pass through binary queries from one level to another without
/// knowing the custom format, or we can decode it, with the knowledge of the allowed
/// types.
///
/// The gas limit describes how much [CosmWasm gas] this particular query is allowed
/// to consume when measured separately from the rest of the contract.
/// The returned gas info (in BackendResult) can exceed the gas limit in cases
/// where the query could not be aborted exactly at the limit.
///
/// [CosmWasm gas]: https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md
fn query_raw(
&self,
request: &[u8],
gas_limit: u64,
) -> BackendResult<SystemResult<ContractResult<Binary>>>;
}
/// A result type for calling into the backend. Such a call can cause
/// non-negligible computational cost in both success and failure case and
/// must always have gas information attached.
pub type BackendResult<T> = (core::result::Result<T, BackendError>, GasInfo);
/// This aims to be similar to the `?` operator, but for a [`BackendResult`].
///
/// The first argument is a result. If it is Ok, return the value.
/// If it is Err, end the current function with a `return BackendResult::Err`.
///
/// The second argument is the gas value that will be used in the error case.
/// It should be the sum of all gas used in the calling function.
macro_rules! unwrap_or_return_with_gas {
($result: expr $(,)?, $gas_total: expr $(,)?) => {{
let result: core::result::Result<_, _> = $result; // just a type check
let gas: GasInfo = $gas_total; // just a type check
match result {
Ok(v) => v,
Err(e) => return (Err(e), gas),
}
}};
}
pub(crate) use unwrap_or_return_with_gas;
#[derive(Error, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum BackendError {
#[error("Panic in FFI call")]
ForeignPanic {},
#[error("Bad argument")]
BadArgument {},
#[error("VM received invalid UTF-8 data from backend")]
InvalidUtf8 {},
#[error("Iterator with ID {id} does not exist")]
IteratorDoesNotExist { id: u32 },
#[error("Ran out of gas during call into backend")]
OutOfGas {},
#[error("Unknown error during call into backend: {msg}")]
Unknown { msg: String },
// This is the only error case of BackendError that is reported back to the contract.
#[error("User error during call into backend: {msg}")]
UserErr { msg: String },
}
impl BackendError {
pub fn foreign_panic() -> Self {
BackendError::ForeignPanic {}
}
pub fn bad_argument() -> Self {
BackendError::BadArgument {}
}
pub fn iterator_does_not_exist(iterator_id: u32) -> Self {
BackendError::IteratorDoesNotExist { id: iterator_id }
}
pub fn out_of_gas() -> Self {
BackendError::OutOfGas {}
}
pub fn unknown(msg: impl Into<String>) -> Self {
BackendError::Unknown { msg: msg.into() }
}
pub fn user_err(msg: impl Into<String>) -> Self {
BackendError::UserErr { msg: msg.into() }
}
}
impl From<FromUtf8Error> for BackendError {
fn from(_original: FromUtf8Error) -> Self {
BackendError::InvalidUtf8 {}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gas_info_with_cost_works() {
let gas_info = GasInfo::with_cost(21);
assert_eq!(gas_info.cost, 21);
assert_eq!(gas_info.externally_used, 0);
}
#[test]
fn gas_info_with_externally_used_works() {
let gas_info = GasInfo::with_externally_used(65);
assert_eq!(gas_info.cost, 0);
assert_eq!(gas_info.externally_used, 65);
}
#[test]
fn gas_info_free_works() {
let gas_info = GasInfo::free();
assert_eq!(gas_info.cost, 0);
assert_eq!(gas_info.externally_used, 0);
}
#[test]
fn gas_info_implements_add_assign() {
let mut a = GasInfo::new(0, 0);
a += GasInfo::new(0, 0);
assert_eq!(
a,
GasInfo {
cost: 0,
externally_used: 0
}
);
let mut a = GasInfo::new(0, 0);
a += GasInfo::new(12, 0);
assert_eq!(
a,
GasInfo {
cost: 12,
externally_used: 0
}
);
let mut a = GasInfo::new(10, 0);
a += GasInfo::new(3, 0);
assert_eq!(
a,
GasInfo {
cost: 13,
externally_used: 0
}
);
let mut a = GasInfo::new(0, 0);
a += GasInfo::new(0, 7);
assert_eq!(
a,
GasInfo {
cost: 0,
externally_used: 7
}
);
let mut a = GasInfo::new(0, 8);
a += GasInfo::new(0, 9);
assert_eq!(
a,
GasInfo {
cost: 0,
externally_used: 17
}
);
let mut a = GasInfo::new(100, 200);
a += GasInfo::new(1, 2);
assert_eq!(
a,
GasInfo {
cost: 101,
externally_used: 202
}
);
}
// constructors
#[test]
fn backend_err_foreign_panic() {
let error = BackendError::foreign_panic();
match error {
BackendError::ForeignPanic { .. } => {}
e => panic!("Unexpected error: {e:?}"),
}
}
#[test]
fn backend_err_bad_argument() {
let error = BackendError::bad_argument();
match error {
BackendError::BadArgument { .. } => {}
e => panic!("Unexpected error: {e:?}"),
}
}
#[test]
fn iterator_does_not_exist_works() {
let error = BackendError::iterator_does_not_exist(15);
match error {
BackendError::IteratorDoesNotExist { id, .. } => assert_eq!(id, 15),
e => panic!("Unexpected error: {e:?}"),
}
}
#[test]
fn backend_err_out_of_gas() {
let error = BackendError::out_of_gas();
match error {
BackendError::OutOfGas { .. } => {}
e => panic!("Unexpected error: {e:?}"),
}
}
#[test]
fn backend_err_unknown() {
let error = BackendError::unknown("broken");
match error {
BackendError::Unknown { msg, .. } => assert_eq!(msg, "broken"),
e => panic!("Unexpected error: {e:?}"),
}
}
#[test]
fn backend_err_user_err() {
let error = BackendError::user_err("invalid input");
match error {
BackendError::UserErr { msg, .. } => assert_eq!(msg, "invalid input"),
e => panic!("Unexpected error: {e:?}"),
}
}
// conversions
#[test]
fn convert_from_fromutf8error() {
let error: BackendError = String::from_utf8(vec![0x80]).unwrap_err().into();
match error {
BackendError::InvalidUtf8 { .. } => {}
e => panic!("Unexpected error: {e:?}"),
}
}
}