@mainsail/evm 0.0.1-evm.52 → 0.0.1-evm.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +5922 -0
- package/Cargo.toml +34 -0
- package/bindings/Cargo.toml +33 -0
- package/bindings/build.rs +6 -0
- package/bindings/src/ctx.rs +667 -0
- package/bindings/src/lib.rs +2231 -0
- package/bindings/src/logger.rs +110 -0
- package/bindings/src/result.rs +542 -0
- package/bindings/src/utils.rs +71 -0
- package/core/Cargo.toml +36 -0
- package/core/src/account.rs +112 -0
- package/core/src/bytecode.rs +39 -0
- package/core/src/compression.rs +158 -0
- package/core/src/db.rs +3311 -0
- package/core/src/events.rs +9 -0
- package/core/src/historical.rs +544 -0
- package/core/src/legacy.rs +153 -0
- package/core/src/lib.rs +14 -0
- package/core/src/logger.rs +98 -0
- package/core/src/logs_bloom.rs +96 -0
- package/core/src/precompiles.rs +450 -0
- package/core/src/receipt.rs +153 -0
- package/core/src/state_changes.rs +122 -0
- package/core/src/state_commit.rs +615 -0
- package/core/src/state_root.rs +266 -0
- package/index.d.ts +1 -0
- package/index.js +52 -52
- package/package.json +5 -4
- package/scripts/postinstall.mjs +73 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
use std::collections::BTreeMap;
|
|
2
|
+
|
|
3
|
+
use alloy_sol_types::SolEvent;
|
|
4
|
+
use rayon::{
|
|
5
|
+
iter::{IntoParallelRefMutIterator, ParallelIterator},
|
|
6
|
+
slice::ParallelSliceMut,
|
|
7
|
+
};
|
|
8
|
+
use revm::{
|
|
9
|
+
context::result::ExecutionResult,
|
|
10
|
+
database::{DatabaseCommitExt, WrapDatabaseRef},
|
|
11
|
+
primitives::{Address, B256, map::HashMap},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
use crate::{
|
|
15
|
+
db::{CommitData, CommitKey, Error, GenesisInfo, PendingCommit, PersistentDB},
|
|
16
|
+
state_changes::{self, AccountMergeInfo, AccountUpdate},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
#[derive(Clone, Debug, Default)]
|
|
20
|
+
pub struct StateCommit {
|
|
21
|
+
pub key: CommitKey,
|
|
22
|
+
pub change_set: state_changes::StateChangeset,
|
|
23
|
+
pub results: BTreeMap<B256, (ExecutionResult, u64)>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn build_commit(pending_commit: &mut PendingCommit) -> Result<StateCommit, crate::db::Error> {
|
|
27
|
+
assert!(pending_commit.built_commit.is_none());
|
|
28
|
+
let mut state_builder = revm::database::State::builder()
|
|
29
|
+
.with_cached_prestate(std::mem::take(&mut pending_commit.cache))
|
|
30
|
+
.build();
|
|
31
|
+
|
|
32
|
+
state_builder.transition_state = Some(std::mem::take(&mut pending_commit.transitions));
|
|
33
|
+
state_builder
|
|
34
|
+
.merge_transitions(revm::database::states::bundle_state::BundleRetention::PlainState);
|
|
35
|
+
|
|
36
|
+
let bundle = state_builder.take_bundle();
|
|
37
|
+
let mut change_set = state_changes::bundle_into_change_set(bundle);
|
|
38
|
+
|
|
39
|
+
change_set.legacy_attributes = std::mem::take(&mut pending_commit.legacy_attributes);
|
|
40
|
+
change_set.legacy_cold_wallets = std::mem::take(&mut pending_commit.legacy_cold_wallets);
|
|
41
|
+
change_set.merged_legacy_cold_wallets =
|
|
42
|
+
std::mem::take(&mut pending_commit.merged_legacy_cold_wallets)
|
|
43
|
+
.into_iter()
|
|
44
|
+
.filter_map(|(key, legacy)| legacy.map(|v| (key, v)))
|
|
45
|
+
.collect();
|
|
46
|
+
|
|
47
|
+
let mut state_commit = StateCommit {
|
|
48
|
+
key: pending_commit.key,
|
|
49
|
+
change_set,
|
|
50
|
+
results: std::mem::take(&mut pending_commit.results),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
finalize(&mut state_commit);
|
|
54
|
+
|
|
55
|
+
Ok(state_commit)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub fn apply_rewards(
|
|
59
|
+
db: &mut PersistentDB,
|
|
60
|
+
pending: &mut PendingCommit,
|
|
61
|
+
rewards: HashMap<Address, u128>,
|
|
62
|
+
) -> Result<(), crate::db::Error> {
|
|
63
|
+
let mut state = revm::database::State::builder()
|
|
64
|
+
.with_bundle_update()
|
|
65
|
+
.with_cached_prestate(std::mem::take(&mut pending.cache))
|
|
66
|
+
.with_database(WrapDatabaseRef(&db))
|
|
67
|
+
.build();
|
|
68
|
+
|
|
69
|
+
state
|
|
70
|
+
.increment_balances(rewards)
|
|
71
|
+
.map_err(|err| crate::db::Error::State(format!("increment balances err={}", err)))?;
|
|
72
|
+
|
|
73
|
+
if let Some(transition_state) = state.transition_state.take() {
|
|
74
|
+
// println!("transition state {:#?}", transition_state);
|
|
75
|
+
pending
|
|
76
|
+
.transitions
|
|
77
|
+
.add_transitions(transition_state.transitions.into_iter());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pending.cache = std::mem::take(&mut state.cache);
|
|
81
|
+
// println!("cache {:#?}", pending.cache.accounts);
|
|
82
|
+
|
|
83
|
+
Ok(())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
pub fn commit_to_db(
|
|
87
|
+
db: &mut PersistentDB,
|
|
88
|
+
mut pending_commit: PendingCommit,
|
|
89
|
+
commit_data: Option<CommitData>,
|
|
90
|
+
) -> Result<Vec<AccountUpdate>, crate::db::Error> {
|
|
91
|
+
let genesis_info = db.genesis_info.clone();
|
|
92
|
+
let mut commit = match pending_commit.built_commit {
|
|
93
|
+
Some(commit) => commit,
|
|
94
|
+
None => build_commit(&mut pending_commit)?,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
commit_with_resize_retry(|| db.commit(&mut commit, &commit_data), || db.resize())?;
|
|
98
|
+
|
|
99
|
+
Ok(collect_dirty_accounts(commit, &genesis_info))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Maximum number of resize-and-retry attempts after an initial `DbFull` on commit.
|
|
103
|
+
const MAX_RESIZE_RETRIES: usize = 3;
|
|
104
|
+
|
|
105
|
+
fn commit_with_resize_retry(
|
|
106
|
+
mut try_commit: impl FnMut() -> Result<(), Error>,
|
|
107
|
+
mut resize: impl FnMut() -> Result<(), Error>,
|
|
108
|
+
) -> Result<(), Error> {
|
|
109
|
+
for _ in 0..=MAX_RESIZE_RETRIES {
|
|
110
|
+
match try_commit() {
|
|
111
|
+
Ok(()) => return Ok(()),
|
|
112
|
+
Err(Error::DbFull) => resize()?,
|
|
113
|
+
Err(err) => return Err(err),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
Err(Error::DbFull)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fn finalize(state: &mut StateCommit) {
|
|
120
|
+
state.change_set.accounts.par_sort_unstable_by_key(|a| a.0);
|
|
121
|
+
state.change_set.contracts.par_sort_unstable_by_key(|a| a.0);
|
|
122
|
+
|
|
123
|
+
state
|
|
124
|
+
.change_set
|
|
125
|
+
.storage
|
|
126
|
+
.par_iter_mut()
|
|
127
|
+
.for_each(|s| s.storage.par_sort_unstable_by_key(|slot| slot.0));
|
|
128
|
+
|
|
129
|
+
state
|
|
130
|
+
.change_set
|
|
131
|
+
.storage
|
|
132
|
+
.par_sort_unstable_by_key(|a| a.address);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
fn collect_dirty_accounts(
|
|
136
|
+
commit: StateCommit,
|
|
137
|
+
genesis_info: &Option<GenesisInfo>,
|
|
138
|
+
) -> Vec<AccountUpdate> {
|
|
139
|
+
let mut dirty_accounts = HashMap::with_capacity(commit.change_set.accounts.len());
|
|
140
|
+
|
|
141
|
+
for (address, account) in commit.change_set.accounts {
|
|
142
|
+
if let Some(account) = account {
|
|
143
|
+
dirty_accounts.insert(
|
|
144
|
+
address,
|
|
145
|
+
AccountUpdate {
|
|
146
|
+
address,
|
|
147
|
+
balance: account.balance,
|
|
148
|
+
nonce: account.nonce,
|
|
149
|
+
vote: None,
|
|
150
|
+
unvote: None,
|
|
151
|
+
username: None,
|
|
152
|
+
username_resigned: false,
|
|
153
|
+
merge_info: commit
|
|
154
|
+
.change_set
|
|
155
|
+
.merged_legacy_cold_wallets
|
|
156
|
+
.get(&address)
|
|
157
|
+
.map(|value| AccountMergeInfo {
|
|
158
|
+
legacy_address: value.1,
|
|
159
|
+
transaction_hash: value.0,
|
|
160
|
+
}),
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if let Some(info) = genesis_info {
|
|
167
|
+
for (receipt, _) in commit.results.values() {
|
|
168
|
+
match receipt {
|
|
169
|
+
ExecutionResult::Success { logs, .. } => {
|
|
170
|
+
for log in logs {
|
|
171
|
+
match log.address {
|
|
172
|
+
_ if log.address == info.validator_contract => {
|
|
173
|
+
// Attempt to decode the log as a Voted event
|
|
174
|
+
if let Ok(event) = crate::events::Voted::decode_log(&log) {
|
|
175
|
+
// println!(
|
|
176
|
+
// "Voted event (from={:?} to={:?})",
|
|
177
|
+
// event.data.voter, event.data.validator,
|
|
178
|
+
// );
|
|
179
|
+
|
|
180
|
+
dirty_accounts.get_mut(&event.voter).and_then(|account| {
|
|
181
|
+
account.vote = Some(event.validator);
|
|
182
|
+
account.unvote = None; // cancel out any previous unvote if one happened in same commit
|
|
183
|
+
Some(account)
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Attempt to decode the log as a Unvoted event
|
|
190
|
+
if let Ok(event) = crate::events::Unvoted::decode_log(&log) {
|
|
191
|
+
// println!(
|
|
192
|
+
// "Unvoted event (from={:?} removed vote={:?})",
|
|
193
|
+
// event.data.voter, event.data.validator,
|
|
194
|
+
// );
|
|
195
|
+
|
|
196
|
+
dirty_accounts.get_mut(&event.voter).and_then(|account| {
|
|
197
|
+
account.unvote = Some(event.validator);
|
|
198
|
+
account.vote = None; // cancel out any previous vote if one happened in same commit
|
|
199
|
+
Some(account)
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
_ if log.address == info.username_contract => {
|
|
206
|
+
// Attempt to decode log as a UsernameRegistered event
|
|
207
|
+
if let Ok(event) =
|
|
208
|
+
crate::events::UsernameRegistered::decode_log(&log)
|
|
209
|
+
{
|
|
210
|
+
dirty_accounts.get_mut(&event.addr).and_then(|account| {
|
|
211
|
+
account.username = Some(event.username.clone());
|
|
212
|
+
account.username_resigned = false; // cancel out any previous resignation if one happened in same commit
|
|
213
|
+
Some(account)
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Attempt to decode log as a UsernameResigned event
|
|
219
|
+
if let Ok(event) = crate::events::UsernameResigned::decode_log(&log)
|
|
220
|
+
{
|
|
221
|
+
dirty_accounts.get_mut(&event.addr).and_then(|account| {
|
|
222
|
+
account.username = None; // cancel out any previous registration if one happened in same commit
|
|
223
|
+
account.username_resigned = true;
|
|
224
|
+
Some(account)
|
|
225
|
+
});
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
_ => (), // ignore
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
//
|
|
234
|
+
}
|
|
235
|
+
ExecutionResult::Revert { .. } | ExecutionResult::Halt { .. } => (), // ignore
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
dirty_accounts.into_values().collect()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#[cfg(test)]
|
|
244
|
+
mod tests {
|
|
245
|
+
use std::collections::BTreeMap;
|
|
246
|
+
|
|
247
|
+
use super::commit_with_resize_retry;
|
|
248
|
+
use crate::{
|
|
249
|
+
db::{Error, GenesisInfo, PendingCommit, PersistentDB},
|
|
250
|
+
events,
|
|
251
|
+
state_changes::{AccountMergeInfo, AccountUpdate, StateChangeset},
|
|
252
|
+
state_commit::{StateCommit, apply_rewards, collect_dirty_accounts},
|
|
253
|
+
};
|
|
254
|
+
use crate::{
|
|
255
|
+
legacy::{LegacyAccountAttributes, LegacyAddress},
|
|
256
|
+
state_changes::StorageChangeset,
|
|
257
|
+
};
|
|
258
|
+
use alloy_primitives::{Address, Log, U256, address};
|
|
259
|
+
use alloy_primitives::{B256, b256};
|
|
260
|
+
use alloy_sol_types::SolEvent;
|
|
261
|
+
use bytes::Bytes;
|
|
262
|
+
use revm::{
|
|
263
|
+
context::result::{ExecutionResult, Output, ResultGas, SuccessReason},
|
|
264
|
+
primitives::HashMap,
|
|
265
|
+
};
|
|
266
|
+
use revm::{database::states::StorageSlot, state::AccountInfo};
|
|
267
|
+
|
|
268
|
+
#[test]
|
|
269
|
+
fn test_collect_dirty_accounts() {
|
|
270
|
+
let mut change_set = StateChangeset::default();
|
|
271
|
+
change_set.accounts.push((
|
|
272
|
+
address!("0000000000000000000000000000000000000001"),
|
|
273
|
+
Some(AccountInfo::from_balance(U256::from(1))),
|
|
274
|
+
));
|
|
275
|
+
change_set.accounts.push((
|
|
276
|
+
address!("0000000000000000000000000000000000000002"),
|
|
277
|
+
Some(AccountInfo::from_balance(U256::from(1))),
|
|
278
|
+
));
|
|
279
|
+
|
|
280
|
+
let genesis_info = GenesisInfo {
|
|
281
|
+
account: address!("0000000000000000000000000000000000000001"),
|
|
282
|
+
deployer_account: address!("0000000000000000000000000000000000000002"),
|
|
283
|
+
validator_contract: address!("0000000000000000000000000000000000000003"),
|
|
284
|
+
username_contract: address!("0000000000000000000000000000000000000004"),
|
|
285
|
+
initial_block_number: 0,
|
|
286
|
+
initial_supply: U256::from(1_000_000),
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
let storage = vec![
|
|
290
|
+
(
|
|
291
|
+
U256::from(1),
|
|
292
|
+
StorageSlot::new_changed(U256::ZERO, U256::from(1234)),
|
|
293
|
+
),
|
|
294
|
+
(
|
|
295
|
+
U256::from(2),
|
|
296
|
+
StorageSlot::new_changed(U256::ZERO, U256::from(5678)),
|
|
297
|
+
),
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
change_set.storage.push(StorageChangeset {
|
|
301
|
+
address: address!("0000000000000000000000000000000000000002"),
|
|
302
|
+
storage,
|
|
303
|
+
..Default::default()
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
change_set.legacy_attributes.insert(
|
|
307
|
+
address!("0000000000000000000000000000000000000001"),
|
|
308
|
+
LegacyAccountAttributes {
|
|
309
|
+
legacy_nonce: Some(5),
|
|
310
|
+
second_public_key: Some("".into()),
|
|
311
|
+
..Default::default()
|
|
312
|
+
},
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
let legacy_address: LegacyAddress =
|
|
316
|
+
"DJmvhhiQFSrEQCq9FUxvcLcpcBjx7K3yLt".try_into().unwrap();
|
|
317
|
+
change_set.legacy_cold_wallets.insert(
|
|
318
|
+
legacy_address.clone(),
|
|
319
|
+
crate::legacy::LegacyColdWallet {
|
|
320
|
+
address: legacy_address.clone(),
|
|
321
|
+
balance: U256::from(255),
|
|
322
|
+
legacy_attributes: LegacyAccountAttributes {
|
|
323
|
+
legacy_nonce: Some(3),
|
|
324
|
+
..Default::default()
|
|
325
|
+
},
|
|
326
|
+
..Default::default()
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
change_set.merged_legacy_cold_wallets.insert(
|
|
331
|
+
address!("0000000000000000000000000000000000000001"),
|
|
332
|
+
(
|
|
333
|
+
b256!("0000000000000000000000000000000000000000000000000000000000000001"),
|
|
334
|
+
legacy_address,
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
let mut results = BTreeMap::<B256, (ExecutionResult, u64)>::new();
|
|
339
|
+
|
|
340
|
+
results.insert(
|
|
341
|
+
b256!("0000000000000000000000000000000000000000000000000000000000000001"),
|
|
342
|
+
(
|
|
343
|
+
ExecutionResult::Success {
|
|
344
|
+
reason: SuccessReason::Stop,
|
|
345
|
+
gas: ResultGas::new_with_state_gas(30000, 30000, 0, 0),
|
|
346
|
+
logs: vec![
|
|
347
|
+
Log {
|
|
348
|
+
address: genesis_info.validator_contract,
|
|
349
|
+
data: events::Voted {
|
|
350
|
+
validator: address!("0000000000000000000000000000000000000002"),
|
|
351
|
+
voter: address!("0000000000000000000000000000000000000001"),
|
|
352
|
+
}
|
|
353
|
+
.encode_log_data(),
|
|
354
|
+
},
|
|
355
|
+
Log {
|
|
356
|
+
address: genesis_info.validator_contract,
|
|
357
|
+
data: events::Unvoted {
|
|
358
|
+
validator: address!("0000000000000000000000000000000000000004"),
|
|
359
|
+
voter: address!("0000000000000000000000000000000000000002"),
|
|
360
|
+
}
|
|
361
|
+
.encode_log_data(),
|
|
362
|
+
},
|
|
363
|
+
Log {
|
|
364
|
+
address: genesis_info.username_contract,
|
|
365
|
+
data: events::UsernameRegistered {
|
|
366
|
+
addr: address!("0000000000000000000000000000000000000001"),
|
|
367
|
+
username: "test".into(),
|
|
368
|
+
previousUsername: "".into(),
|
|
369
|
+
}
|
|
370
|
+
.encode_log_data(),
|
|
371
|
+
},
|
|
372
|
+
Log {
|
|
373
|
+
address: genesis_info.username_contract,
|
|
374
|
+
data: events::UsernameResigned {
|
|
375
|
+
addr: address!("0000000000000000000000000000000000000002"),
|
|
376
|
+
username: "resigned".into(),
|
|
377
|
+
}
|
|
378
|
+
.encode_log_data(),
|
|
379
|
+
},
|
|
380
|
+
Log {
|
|
381
|
+
address: genesis_info.validator_contract,
|
|
382
|
+
..Default::default()
|
|
383
|
+
},
|
|
384
|
+
Log {
|
|
385
|
+
address: genesis_info.username_contract,
|
|
386
|
+
..Default::default()
|
|
387
|
+
},
|
|
388
|
+
Log {
|
|
389
|
+
address: address!("0000000000000000000000000000000000000000"),
|
|
390
|
+
..Default::default()
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
output: Output::Create(
|
|
394
|
+
alloy_primitives::Bytes(Bytes::new()),
|
|
395
|
+
Some(address!("0000000000000000000000000000000000000001")),
|
|
396
|
+
),
|
|
397
|
+
},
|
|
398
|
+
0,
|
|
399
|
+
),
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
results.insert(
|
|
403
|
+
b256!("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
404
|
+
(
|
|
405
|
+
ExecutionResult::Revert {
|
|
406
|
+
gas: ResultGas::new_with_state_gas(30000, 30000, 0, 0),
|
|
407
|
+
logs: vec![],
|
|
408
|
+
output: alloy_primitives::Bytes(Bytes::new()),
|
|
409
|
+
},
|
|
410
|
+
0,
|
|
411
|
+
),
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
let state = StateCommit {
|
|
415
|
+
change_set,
|
|
416
|
+
results,
|
|
417
|
+
..Default::default()
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
let mut account_updates = collect_dirty_accounts(state, &Some(genesis_info));
|
|
421
|
+
account_updates.sort_by_key(|k| k.address);
|
|
422
|
+
|
|
423
|
+
assert_eq!(
|
|
424
|
+
account_updates,
|
|
425
|
+
vec![
|
|
426
|
+
AccountUpdate {
|
|
427
|
+
address: address!("0000000000000000000000000000000000000001"),
|
|
428
|
+
balance: U256::ONE,
|
|
429
|
+
nonce: 0,
|
|
430
|
+
vote: Some(address!("0000000000000000000000000000000000000002")),
|
|
431
|
+
unvote: None,
|
|
432
|
+
username: Some("test".into()),
|
|
433
|
+
username_resigned: false,
|
|
434
|
+
merge_info: Some(AccountMergeInfo {
|
|
435
|
+
legacy_address: "DJmvhhiQFSrEQCq9FUxvcLcpcBjx7K3yLt".try_into().unwrap(),
|
|
436
|
+
transaction_hash: b256!(
|
|
437
|
+
"0000000000000000000000000000000000000000000000000000000000000001"
|
|
438
|
+
)
|
|
439
|
+
})
|
|
440
|
+
},
|
|
441
|
+
AccountUpdate {
|
|
442
|
+
address: address!("0000000000000000000000000000000000000002"),
|
|
443
|
+
balance: U256::ONE,
|
|
444
|
+
nonce: 0,
|
|
445
|
+
vote: None,
|
|
446
|
+
unvote: Some(address!("0000000000000000000000000000000000000004")),
|
|
447
|
+
username: None,
|
|
448
|
+
username_resigned: true,
|
|
449
|
+
merge_info: None
|
|
450
|
+
}
|
|
451
|
+
]
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
#[test]
|
|
456
|
+
fn test_apply_rewards() {
|
|
457
|
+
let path = tempfile::Builder::new()
|
|
458
|
+
.prefix("evm.mdb")
|
|
459
|
+
.tempdir()
|
|
460
|
+
.unwrap();
|
|
461
|
+
|
|
462
|
+
let mut db = PersistentDB::new(crate::db::PersistentDBOptions::new(
|
|
463
|
+
path.path().to_path_buf(),
|
|
464
|
+
))
|
|
465
|
+
.expect("database");
|
|
466
|
+
let mut pending = PendingCommit::default();
|
|
467
|
+
|
|
468
|
+
let account1 = revm::primitives::address!("bd6f65c58a46427af4b257cbe231d0ed69ed5508");
|
|
469
|
+
let account2 = revm::primitives::address!("ad6f65c58a46427af4b257cbe231d0ed69ed5508");
|
|
470
|
+
|
|
471
|
+
let mut rewards = HashMap::<Address, u128>::default();
|
|
472
|
+
rewards.insert(account1, 1234);
|
|
473
|
+
rewards.insert(account2, 0);
|
|
474
|
+
|
|
475
|
+
let result = apply_rewards(&mut db, &mut pending, rewards);
|
|
476
|
+
assert!(result.is_ok());
|
|
477
|
+
|
|
478
|
+
let cache_account1 = pending.cache.accounts.get(&account1).expect("account1");
|
|
479
|
+
assert!(cache_account1.account.is_some());
|
|
480
|
+
assert_eq!(
|
|
481
|
+
cache_account1.status,
|
|
482
|
+
revm::database::AccountStatus::InMemoryChange
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
let cache_account2 = pending.cache.accounts.get(&account2).expect("account2");
|
|
486
|
+
assert!(cache_account2.account.is_none());
|
|
487
|
+
assert_eq!(
|
|
488
|
+
cache_account2.status,
|
|
489
|
+
revm::database::AccountStatus::LoadedNotExisting
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
let transition_account1 = pending
|
|
493
|
+
.transitions
|
|
494
|
+
.transitions
|
|
495
|
+
.get(&account1)
|
|
496
|
+
.expect("transition_account1");
|
|
497
|
+
assert!(transition_account1.info.is_some());
|
|
498
|
+
assert_eq!(
|
|
499
|
+
transition_account1.status,
|
|
500
|
+
revm::database::AccountStatus::InMemoryChange
|
|
501
|
+
);
|
|
502
|
+
assert_eq!(transition_account1.storage_was_destroyed, false);
|
|
503
|
+
|
|
504
|
+
let transition_account2 = pending.transitions.transitions.get(&account2);
|
|
505
|
+
assert_eq!(transition_account2, None);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
#[test]
|
|
509
|
+
fn commit_succeeds_without_resizing() {
|
|
510
|
+
let mut commits = 0;
|
|
511
|
+
let mut resizes = 0;
|
|
512
|
+
|
|
513
|
+
let result = commit_with_resize_retry(
|
|
514
|
+
|| {
|
|
515
|
+
commits += 1;
|
|
516
|
+
Ok(())
|
|
517
|
+
},
|
|
518
|
+
|| {
|
|
519
|
+
resizes += 1;
|
|
520
|
+
Ok(())
|
|
521
|
+
},
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
assert!(result.is_ok());
|
|
525
|
+
assert_eq!(commits, 1); // committed on the first attempt
|
|
526
|
+
assert_eq!(resizes, 0); // never had to grow the map
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
#[test]
|
|
530
|
+
fn commit_recovers_after_one_resize() {
|
|
531
|
+
let mut commits = 0;
|
|
532
|
+
let mut resizes = 0;
|
|
533
|
+
|
|
534
|
+
let result = commit_with_resize_retry(
|
|
535
|
+
|| {
|
|
536
|
+
commits += 1;
|
|
537
|
+
if commits == 1 {
|
|
538
|
+
Err(Error::DbFull)
|
|
539
|
+
} else {
|
|
540
|
+
Ok(())
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
|| {
|
|
544
|
+
resizes += 1;
|
|
545
|
+
Ok(())
|
|
546
|
+
},
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
assert!(result.is_ok());
|
|
550
|
+
assert_eq!(commits, 2); // one DbFull, then success
|
|
551
|
+
assert_eq!(resizes, 1);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
#[test]
|
|
555
|
+
fn commit_recovers_after_two_resizes() {
|
|
556
|
+
let mut commits = 0;
|
|
557
|
+
let mut resizes = 0;
|
|
558
|
+
|
|
559
|
+
let result = commit_with_resize_retry(
|
|
560
|
+
|| {
|
|
561
|
+
commits += 1;
|
|
562
|
+
if commits <= 2 {
|
|
563
|
+
Err(Error::DbFull)
|
|
564
|
+
} else {
|
|
565
|
+
Ok(())
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
|| {
|
|
569
|
+
resizes += 1;
|
|
570
|
+
Ok(())
|
|
571
|
+
},
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
assert!(result.is_ok());
|
|
575
|
+
assert_eq!(commits, 3); // two DbFull, then success
|
|
576
|
+
assert_eq!(resizes, 2);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
#[test]
|
|
580
|
+
fn commit_gives_up_after_max_retries() {
|
|
581
|
+
let mut commits = 0;
|
|
582
|
+
let mut resizes = 0;
|
|
583
|
+
|
|
584
|
+
let result = commit_with_resize_retry(
|
|
585
|
+
|| {
|
|
586
|
+
commits += 1;
|
|
587
|
+
Err(Error::DbFull) // never fits, no matter how often we grow
|
|
588
|
+
},
|
|
589
|
+
|| {
|
|
590
|
+
resizes += 1;
|
|
591
|
+
Ok(())
|
|
592
|
+
},
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
assert!(matches!(result, Err(Error::DbFull)));
|
|
596
|
+
assert_eq!(commits, 4); // initial attempt + MAX_RESIZE_RETRIES (3)
|
|
597
|
+
assert_eq!(resizes, 4); // a resize follows every DbFull
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
#[test]
|
|
601
|
+
fn commit_propagates_non_dbfull_error_without_resizing() {
|
|
602
|
+
let mut resizes = 0;
|
|
603
|
+
|
|
604
|
+
let result = commit_with_resize_retry(
|
|
605
|
+
|| Err(Error::Lock),
|
|
606
|
+
|| {
|
|
607
|
+
resizes += 1;
|
|
608
|
+
Ok(())
|
|
609
|
+
},
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
assert!(matches!(result, Err(Error::Lock)));
|
|
613
|
+
assert_eq!(resizes, 0); // non-DbFull errors return immediately
|
|
614
|
+
}
|
|
615
|
+
}
|