-
Notifications
You must be signed in to change notification settings - Fork 450
/
Copy pathlib.rs
executable file
·180 lines (156 loc) · 6.32 KB
/
lib.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
//! # Custom Allocator
//!
//! This example demonstrates how to opt-out of the ink! provided global memory allocator.
//!
//! We will use [`dlmalloc`](https://github.com/alexcrichton/dlmalloc-rs) instead.
//!
//! ## Warning!
//!
//! We **do not** recommend you opt-out of the provided allocator for production contract
//! deployments!
//!
//! If you don't handle allocations correctly you can introduce security vulnerabilities to your
//! contracts.
//!
//! You may also introduce performance issues. This is because the code of your allocator will
//! be included in the final contract binary, potentially increasing gas usage significantly.
//!
//! ## Why Change the Allocator?
//!
//! The default memory allocator was designed to have a tiny size footprint, and made some
//! compromises to achieve that, e.g it does not free/deallocate memory
//!
//! You may have a use case where you want to deallocate memory, or allocate it using a different
//! strategy.
//!
//! Providing your own allocator lets you choose the right tradeoffs for your use case.
#![cfg_attr(not(feature = "std"), no_std)]
// Since we opted out of the default allocator we must also bring our own out-of-memory (OOM)
// handler. The Rust compiler doesn't let us do this unless we add this unstable/nightly feature.
#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))]
// Here we set `dlmalloc` to be the global memory allocator.
//
// The [`GlobalAlloc`](https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html) trait is
// important to understand if you're swapping our your allocator.
#[cfg(not(feature = "std"))]
#[global_allocator]
static ALLOC: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc;
// As mentioned earlier, we need to provide our own OOM handler.
//
// We don't try and handle this and opt to abort contract execution instead.
#[cfg(not(feature = "std"))]
#[alloc_error_handler]
fn oom(_: core::alloc::Layout) -> ! {
core::arch::wasm32::unreachable()
}
#[ink::contract]
mod custom_allocator {
use ink::prelude::{
vec,
vec::Vec,
};
#[ink(storage)]
pub struct CustomAllocator {
/// Stores a single `bool` value on the storage.
///
/// # Note
///
/// We're using a `Vec` here as it allocates its elements onto the heap, as opposed to the
/// stack. This allows us to demonstrate that our new allocator actually works.
value: Vec<bool>,
}
impl CustomAllocator {
/// Constructor that initializes the `bool` value to the given `init_value`.
#[ink(constructor)]
pub fn new(init_value: bool) -> Self {
Self {
value: vec![init_value],
}
}
/// Creates a new flipper smart contract initialized to `false`.
#[ink(constructor)]
pub fn default() -> Self {
Self::new(Default::default())
}
/// A message that can be called on instantiated contracts.
/// This one flips the value of the stored `bool` from `true`
/// to `false` and vice versa.
#[ink(message)]
pub fn flip(&mut self) {
self.value[0] = !self.value[0];
}
/// Simply returns the current value of our `bool`.
#[ink(message)]
pub fn get(&self) -> bool {
self.value[0]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[ink::test]
fn default_works() {
let custom_allocator = CustomAllocator::default();
assert!(!custom_allocator.get());
}
#[ink::test]
fn it_works() {
let mut custom_allocator = CustomAllocator::new(false);
assert!(!custom_allocator.get());
custom_allocator.flip();
assert!(custom_allocator.get());
}
}
#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests {
use super::*;
use ink_e2e::build_message;
type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
/// We test that we can upload and instantiate the contract using its default constructor.
#[ink_e2e::test]
async fn default_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// Given
let constructor = CustomAllocatorRef::default();
// When
let contract_account_id = client
.instantiate("custom_allocator", &ink_e2e::alice(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;
// Then
let get = build_message::<CustomAllocatorRef>(contract_account_id.clone())
.call(|custom_allocator| custom_allocator.get());
let get_result = client.call_dry_run(&ink_e2e::alice(), &get, 0, None).await;
assert!(matches!(get_result.return_value(), false));
Ok(())
}
/// We test that we can read and write a value from the on-chain contract contract.
#[ink_e2e::test]
async fn it_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
// Given
let constructor = CustomAllocatorRef::new(false);
let contract_account_id = client
.instantiate("custom_allocator", &ink_e2e::bob(), constructor, 0, None)
.await
.expect("instantiate failed")
.account_id;
let get = build_message::<CustomAllocatorRef>(contract_account_id.clone())
.call(|custom_allocator| custom_allocator.get());
let get_result = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_result.return_value(), false));
// When
let flip = build_message::<CustomAllocatorRef>(contract_account_id.clone())
.call(|custom_allocator| custom_allocator.flip());
let _flip_result = client
.call(&ink_e2e::bob(), flip, 0, None)
.await
.expect("flip failed");
// Then
let get = build_message::<CustomAllocatorRef>(contract_account_id.clone())
.call(|custom_allocator| custom_allocator.get());
let get_result = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await;
assert!(matches!(get_result.return_value(), true));
Ok(())
}
}
}