@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
package/core/Cargo.toml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "mainsail_evm_core"
|
|
3
|
+
rust-version.workspace = true
|
|
4
|
+
edition.workspace = true
|
|
5
|
+
license.workspace = true
|
|
6
|
+
authors.workspace = true
|
|
7
|
+
version.workspace = true
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
anyhow = { workspace = true }
|
|
11
|
+
ethers-contract = { workspace = true }
|
|
12
|
+
ethers-core = { workspace = true }
|
|
13
|
+
ethers-providers = { workspace = true }
|
|
14
|
+
revm = { workspace = true }
|
|
15
|
+
alloy-sol-types = { workspace = true }
|
|
16
|
+
alloy-primitives = { workspace = true }
|
|
17
|
+
alloy-provider = { workspace = true }
|
|
18
|
+
serde = { workspace = true }
|
|
19
|
+
serde_json = { workspace = true }
|
|
20
|
+
bincode = { workspace = true }
|
|
21
|
+
thiserror = { workspace = true }
|
|
22
|
+
tokio = { workspace = true }
|
|
23
|
+
#heed = { version = "0.20.0", features = [] }
|
|
24
|
+
#heed = { path = "../../../../heed/heed", features = [] }
|
|
25
|
+
heed = { git = "https://github.com/ArkEcosystem/heed.git", rev = "7a7a030113664e004b082e6805431381d9e9140a", features = [
|
|
26
|
+
] }
|
|
27
|
+
blst = { version = "0.3.16" }
|
|
28
|
+
rayon = "1.10.0"
|
|
29
|
+
derive_more = { version = "2" }
|
|
30
|
+
bytes = { version = "1.0" }
|
|
31
|
+
bs58 = { version = "0.5.1" }
|
|
32
|
+
sha2 = { version = "0.10.8" }
|
|
33
|
+
zstd = { version = "0.13.3" }
|
|
34
|
+
|
|
35
|
+
[dev-dependencies]
|
|
36
|
+
tempfile = "3"
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
use alloy_primitives::{B256, U256};
|
|
2
|
+
use revm::{primitives::Address, state::AccountInfo};
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
|
|
5
|
+
use crate::legacy::LegacyAccountAttributes;
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
8
|
+
pub struct AccountInfoExtended {
|
|
9
|
+
pub address: Address,
|
|
10
|
+
pub info: AccountInfo,
|
|
11
|
+
pub legacy_attributes: LegacyAccountAttributes,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl AccountInfoExtended {
|
|
15
|
+
pub fn into_parts(self) -> (Address, AccountInfo, Option<LegacyAccountAttributes>) {
|
|
16
|
+
(
|
|
17
|
+
self.address,
|
|
18
|
+
self.info,
|
|
19
|
+
if self.legacy_attributes.is_empty() {
|
|
20
|
+
None
|
|
21
|
+
} else {
|
|
22
|
+
Some(self.legacy_attributes)
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[derive(Default, Debug, Serialize, Deserialize)]
|
|
29
|
+
pub(crate) struct StoredAccountInfo {
|
|
30
|
+
pub balance: U256,
|
|
31
|
+
pub nonce: u64,
|
|
32
|
+
pub code_hash: B256,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl StoredAccountInfo {
|
|
36
|
+
pub fn new(balance: U256, nonce: u64, code_hash: B256) -> Self {
|
|
37
|
+
Self {
|
|
38
|
+
balance,
|
|
39
|
+
nonce,
|
|
40
|
+
code_hash,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl From<StoredAccountInfo> for AccountInfo {
|
|
46
|
+
fn from(stored: StoredAccountInfo) -> Self {
|
|
47
|
+
AccountInfo {
|
|
48
|
+
balance: stored.balance,
|
|
49
|
+
nonce: stored.nonce,
|
|
50
|
+
code_hash: stored.code_hash,
|
|
51
|
+
account_id: None,
|
|
52
|
+
code: None,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[cfg(test)]
|
|
58
|
+
mod tests {
|
|
59
|
+
use crate::{account::AccountInfoExtended, legacy::LegacyAccountAttributes};
|
|
60
|
+
use alloy_primitives::{U256, address, b256};
|
|
61
|
+
use revm::state::AccountInfo;
|
|
62
|
+
|
|
63
|
+
#[test]
|
|
64
|
+
fn test_account_info_parts() {
|
|
65
|
+
let info = AccountInfo {
|
|
66
|
+
balance: U256::ONE,
|
|
67
|
+
nonce: 1,
|
|
68
|
+
code_hash: b256!("0000000000000000000000000000000000000000000000000000000000000001"),
|
|
69
|
+
account_id: None,
|
|
70
|
+
code: None,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let attributes = LegacyAccountAttributes {
|
|
74
|
+
legacy_nonce: Some(0),
|
|
75
|
+
second_public_key: Some("key".into()),
|
|
76
|
+
multi_signature: None,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
let account_info = AccountInfoExtended {
|
|
80
|
+
address: address!("0000000000000000000000000000000000000001"),
|
|
81
|
+
info: info.clone(),
|
|
82
|
+
legacy_attributes: attributes.clone(),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
let (address, info_part, legacy_attributes) = account_info.into_parts();
|
|
86
|
+
|
|
87
|
+
assert_eq!(
|
|
88
|
+
address,
|
|
89
|
+
address!("0000000000000000000000000000000000000001")
|
|
90
|
+
);
|
|
91
|
+
assert_eq!(info, info_part);
|
|
92
|
+
assert_eq!(legacy_attributes, Some(attributes));
|
|
93
|
+
|
|
94
|
+
let account_info = AccountInfoExtended {
|
|
95
|
+
address: address!("0000000000000000000000000000000000000001"),
|
|
96
|
+
info: info.clone(),
|
|
97
|
+
legacy_attributes: LegacyAccountAttributes {
|
|
98
|
+
legacy_nonce: None,
|
|
99
|
+
second_public_key: None,
|
|
100
|
+
multi_signature: None,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
let (address, info_part, legacy_attributes) = account_info.into_parts();
|
|
105
|
+
assert_eq!(
|
|
106
|
+
address,
|
|
107
|
+
address!("0000000000000000000000000000000000000001")
|
|
108
|
+
);
|
|
109
|
+
assert_eq!(info, info_part);
|
|
110
|
+
assert_eq!(legacy_attributes, None);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
use alloy_primitives::Bytes;
|
|
2
|
+
use revm::{bytecode::BytecodeDecodeError, state::Bytecode};
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
|
|
5
|
+
#[derive(Default, Debug, Serialize, Deserialize)]
|
|
6
|
+
pub struct StoredBytecode {
|
|
7
|
+
pub raw: Bytes,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
impl From<Bytecode> for StoredBytecode {
|
|
11
|
+
fn from(code: Bytecode) -> Self {
|
|
12
|
+
Self {
|
|
13
|
+
raw: code.original_bytes(),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl TryFrom<StoredBytecode> for Bytecode {
|
|
19
|
+
type Error = BytecodeDecodeError;
|
|
20
|
+
|
|
21
|
+
fn try_from(stored: StoredBytecode) -> Result<Self, Self::Error> {
|
|
22
|
+
Bytecode::new_raw_checked(stored.raw)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[cfg(test)]
|
|
27
|
+
mod tests {
|
|
28
|
+
use crate::bytecode::StoredBytecode;
|
|
29
|
+
use alloy_primitives::Bytes;
|
|
30
|
+
use revm::state::Bytecode;
|
|
31
|
+
|
|
32
|
+
#[test]
|
|
33
|
+
fn test_bytecode() {
|
|
34
|
+
let raw_bytecode = Bytecode::new_raw(Bytes::from_static(&[1, 2, 3, 4]));
|
|
35
|
+
let stored = StoredBytecode::from(raw_bytecode);
|
|
36
|
+
|
|
37
|
+
assert_eq!(stored.raw, Bytes::from_static(&[1, 2, 3, 4]));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
use std::{borrow::Cow, ops::Deref};
|
|
2
|
+
|
|
3
|
+
// First byte of every stored value tags how the remainder is encoded:
|
|
4
|
+
// TAG_RAW: [0][raw bincode] (uncompressed)
|
|
5
|
+
// TAG_ZSTD: [1][orig_len: u32 LE][zstd payload] (compressed)
|
|
6
|
+
const TAG_RAW: u8 = 0;
|
|
7
|
+
const TAG_ZSTD: u8 = 1;
|
|
8
|
+
|
|
9
|
+
const ZSTD_LEVEL: i32 = 3;
|
|
10
|
+
|
|
11
|
+
// Compression is only attempted for serialized values at least this large. The frequently-written
|
|
12
|
+
// values are poor candidates for two independent reasons:
|
|
13
|
+
// - small size (accounts ~70 B, proofs ~120 B): too little context, and zstd's frame overhead
|
|
14
|
+
// plus the 4-byte length header outweigh any gain;
|
|
15
|
+
// - high-entropy content: keccak code hashes and BLS signatures are effectively random, so there
|
|
16
|
+
// is no redundancy to exploit at any size.
|
|
17
|
+
// The threshold skips the first case cheaply; the "keep raw unless actually smaller" check on the
|
|
18
|
+
// result handles the second. Larger structured values (headers with a sparse logs bloom,
|
|
19
|
+
// ABI-padded calldata, receipt logs, bytecode) still compress and are kept only when it shrinks them.
|
|
20
|
+
const MIN_COMPRESS_LEN: usize = 256;
|
|
21
|
+
|
|
22
|
+
#[derive(Debug)]
|
|
23
|
+
pub struct CompactBincode<T>(pub T);
|
|
24
|
+
impl<'a, T: serde::Serialize + 'a> heed::BytesEncode<'a> for CompactBincode<T> {
|
|
25
|
+
type EItem = CompactBincode<&'a T>;
|
|
26
|
+
|
|
27
|
+
fn bytes_encode(item: &'a Self::EItem) -> Result<Cow<'a, [u8]>, heed::BoxedError> {
|
|
28
|
+
let raw = bincode::serialize(&item.0)?;
|
|
29
|
+
|
|
30
|
+
if raw.len() >= MIN_COMPRESS_LEN {
|
|
31
|
+
let compressed = zstd::bulk::compress(&raw, ZSTD_LEVEL)?;
|
|
32
|
+
|
|
33
|
+
// The compressed layout carries an extra 4-byte original-length header; only keep it
|
|
34
|
+
// when the result is genuinely smaller than storing raw (both forms share the 1-byte
|
|
35
|
+
// tag, so it cancels out of the comparison).
|
|
36
|
+
if compressed.len() + 4 < raw.len() {
|
|
37
|
+
let mut out = Vec::with_capacity(1 + 4 + compressed.len());
|
|
38
|
+
out.push(TAG_ZSTD);
|
|
39
|
+
out.extend_from_slice(&(raw.len() as u32).to_le_bytes());
|
|
40
|
+
out.extend_from_slice(&compressed);
|
|
41
|
+
return Ok(Cow::Owned(out));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Store raw. A value is therefore never persisted larger than its bincode encoding plus the
|
|
46
|
+
// single tag byte.
|
|
47
|
+
let mut out = Vec::with_capacity(1 + raw.len());
|
|
48
|
+
out.push(TAG_RAW);
|
|
49
|
+
out.extend_from_slice(&raw);
|
|
50
|
+
Ok(Cow::Owned(out))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl<'a, T: serde::de::DeserializeOwned + 'a> heed::BytesDecode<'a> for CompactBincode<T> {
|
|
55
|
+
type DItem = CompactBincode<T>;
|
|
56
|
+
|
|
57
|
+
fn bytes_decode(bytes: &'_ [u8]) -> Result<Self::DItem, heed::BoxedError> {
|
|
58
|
+
let (&tag, payload) = bytes
|
|
59
|
+
.split_first()
|
|
60
|
+
.ok_or("CompressedBincode: empty value")?;
|
|
61
|
+
|
|
62
|
+
let deserialized = match tag {
|
|
63
|
+
TAG_ZSTD => {
|
|
64
|
+
if payload.len() < 4 {
|
|
65
|
+
return Err("CompressedBincode: truncated zstd header".into());
|
|
66
|
+
}
|
|
67
|
+
let (len_bytes, compressed) = payload.split_at(4);
|
|
68
|
+
let orig_len = u32::from_le_bytes(len_bytes.try_into().unwrap()) as usize;
|
|
69
|
+
let decompressed = zstd::bulk::decompress(compressed, orig_len)?;
|
|
70
|
+
bincode::deserialize(&decompressed)?
|
|
71
|
+
}
|
|
72
|
+
TAG_RAW => bincode::deserialize(payload)?,
|
|
73
|
+
other => return Err(format!("CompressedBincode: unknown tag {other}").into()),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
Ok(CompactBincode(deserialized))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl<T> Deref for CompactBincode<T> {
|
|
81
|
+
type Target = T;
|
|
82
|
+
|
|
83
|
+
fn deref(&self) -> &Self::Target {
|
|
84
|
+
&self.0
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#[cfg(test)]
|
|
89
|
+
mod tests {
|
|
90
|
+
use heed::{BytesDecode, BytesEncode};
|
|
91
|
+
|
|
92
|
+
use super::*;
|
|
93
|
+
|
|
94
|
+
fn encode(value: &Vec<u8>) -> Vec<u8> {
|
|
95
|
+
let item = CompactBincode(value);
|
|
96
|
+
<CompactBincode<Vec<u8>> as BytesEncode>::bytes_encode(&item)
|
|
97
|
+
.unwrap()
|
|
98
|
+
.into_owned()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn decode(bytes: &[u8]) -> Vec<u8> {
|
|
102
|
+
<CompactBincode<Vec<u8>> as BytesDecode>::bytes_decode(bytes)
|
|
103
|
+
.unwrap()
|
|
104
|
+
.0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#[test]
|
|
108
|
+
fn small_value_is_stored_raw() {
|
|
109
|
+
let value = vec![1u8, 2, 3, 4, 5];
|
|
110
|
+
let encoded = encode(&value);
|
|
111
|
+
assert_eq!(encoded[0], TAG_RAW);
|
|
112
|
+
assert_eq!(decode(&encoded), value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#[test]
|
|
116
|
+
fn large_compressible_value_is_stored_zstd_and_smaller() {
|
|
117
|
+
let value = vec![7u8; 4096];
|
|
118
|
+
let encoded = encode(&value);
|
|
119
|
+
assert_eq!(encoded[0], TAG_ZSTD);
|
|
120
|
+
assert!(encoded.len() < value.len());
|
|
121
|
+
assert_eq!(decode(&encoded), value);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#[test]
|
|
125
|
+
fn output_never_exceeds_raw_plus_tag() {
|
|
126
|
+
// A large, hard-to-compress value: compression is attempted but must fall back to raw
|
|
127
|
+
// rather than store something bigger.
|
|
128
|
+
let value: Vec<u8> = (0..2048u32)
|
|
129
|
+
.map(|i| (i.wrapping_mul(2_654_435_761) >> 13) as u8)
|
|
130
|
+
.collect();
|
|
131
|
+
let raw_len = bincode::serialize(&value).unwrap().len();
|
|
132
|
+
|
|
133
|
+
let encoded = encode(&value);
|
|
134
|
+
assert!(
|
|
135
|
+
encoded.len() <= raw_len + 1,
|
|
136
|
+
"encoded {} raw {}",
|
|
137
|
+
encoded.len(),
|
|
138
|
+
raw_len
|
|
139
|
+
);
|
|
140
|
+
assert_eq!(decode(&encoded), value);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#[test]
|
|
144
|
+
fn empty_input_is_an_error_not_a_panic() {
|
|
145
|
+
assert!(<CompactBincode<Vec<u8>> as BytesDecode>::bytes_decode(&[]).is_err());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#[test]
|
|
149
|
+
fn truncated_zstd_header_is_an_error_not_a_panic() {
|
|
150
|
+
// TAG_ZSTD with fewer than the four orig_len header bytes must error, not panic on the slice.
|
|
151
|
+
assert!(<CompactBincode<Vec<u8>> as BytesDecode>::bytes_decode(&[TAG_ZSTD, 1, 2]).is_err());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#[test]
|
|
155
|
+
fn unknown_tag_is_an_error_not_a_panic() {
|
|
156
|
+
assert!(<CompactBincode<Vec<u8>> as BytesDecode>::bytes_decode(&[9, 0, 0]).is_err());
|
|
157
|
+
}
|
|
158
|
+
}
|