Skip to content

Commit cd5ffe3

Browse files
committed
WIP: split merkle updates
1 parent de15c72 commit cd5ffe3

File tree

28 files changed

+2109
-864
lines changed

28 files changed

+2109
-864
lines changed

Cargo.lock

Lines changed: 1075 additions & 514 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ tycho-storage = { path = "./storage", version = "0.2.7" }
135135
tycho-util = { path = "./util", version = "0.2.7" }
136136

137137
[patch.crates-io]
138-
everscale-types = { git = "https://github.com/broxus/everscale-types.git", rev = "b55da379aba126d4496c88bc7b8cc7af553ab37a" }
138+
everscale-types = { git = "https://github.com/broxus/everscale-types.git", rev = "9b0694bf66cfbbd1dad6e664435a3b9abe4e7e5a" }
139139

140140
[workspace.lints.rust]
141141
future_incompatible = "warn"

block-util/src/state/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub use self::min_ref_mc_state::{MinRefMcStateTracker, RefMcStateHandle};
2+
pub use self::shard_state_data::{split_shard, ShardStateData};
23
pub use self::shard_state_stuff::ShardStateStuff;
34

45
mod min_ref_mc_state;
6+
mod shard_state_data;
57
mod shard_state_stuff;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use std::collections::BTreeMap;
2+
use std::sync::Arc;
3+
4+
use anyhow::Result;
5+
use everscale_types::models::*;
6+
use everscale_types::prelude::*;
7+
8+
/// Parsed shard state accounts.
9+
#[derive(Clone)]
10+
#[repr(transparent)]
11+
pub struct ShardStateData {
12+
inner: Arc<Inner>,
13+
}
14+
15+
impl ShardStateData {
16+
pub fn from_root(root: Cell) -> Result<Self> {
17+
let accounts = root.parse::<ShardAccounts>()?;
18+
Self::from_accounts_and_root(accounts, root)
19+
}
20+
21+
pub fn from_accounts(accounts: ShardAccounts) -> Result<Self> {
22+
let root = CellBuilder::build_from(&accounts)?;
23+
Self::from_accounts_and_root(accounts, root)
24+
}
25+
26+
pub fn from_accounts_and_root(accounts: ShardAccounts, root: Cell) -> Result<Self> {
27+
Ok(Self {
28+
inner: Arc::new(Inner { accounts, root }),
29+
})
30+
}
31+
32+
pub fn root_cell(&self) -> &Cell {
33+
&self.inner.root
34+
}
35+
36+
pub fn accounts(&self) -> &ShardAccounts {
37+
&self.inner.accounts
38+
}
39+
}
40+
41+
#[doc(hidden)]
42+
pub struct Inner {
43+
accounts: ShardAccounts,
44+
root: Cell,
45+
}
46+
47+
pub fn split_shard(
48+
shard: &ShardIdent,
49+
accounts: &ShardAccounts,
50+
depth: u8,
51+
shards: &mut BTreeMap<u64, ShardAccounts>,
52+
) -> Result<()> {
53+
fn split_shard_impl(
54+
shard: &ShardIdent,
55+
accounts: &ShardAccounts,
56+
depth: u8,
57+
shards: &mut BTreeMap<u64, ShardAccounts>,
58+
builder: &mut CellBuilder,
59+
) -> Result<()> {
60+
let (left_shard_ident, right_shard_ident) = 'split: {
61+
if depth > 0 {
62+
if let Some((left, right)) = shard.split() {
63+
break 'split (left, right);
64+
}
65+
}
66+
shards.insert(shard.prefix(), accounts.clone());
67+
return Ok(());
68+
};
69+
70+
let (left_accounts, right_accounts) = {
71+
builder.clear_bits();
72+
let prefix_len = shard.prefix_len();
73+
if prefix_len > 0 {
74+
builder.store_uint(shard.prefix() >> (64 - prefix_len), prefix_len)?;
75+
}
76+
accounts.split_by_prefix(&builder.as_data_slice())?
77+
};
78+
79+
split_shard_impl(
80+
&left_shard_ident,
81+
&left_accounts,
82+
depth - 1,
83+
shards,
84+
builder,
85+
)?;
86+
split_shard_impl(
87+
&right_shard_ident,
88+
&right_accounts,
89+
depth - 1,
90+
shards,
91+
builder,
92+
)
93+
}
94+
95+
split_shard_impl(shard, accounts, depth, shards, &mut CellBuilder::new())
96+
}

block-util/src/state/shard_state_stuff.rs

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::collections::BTreeMap;
12
use std::sync::Arc;
23

34
use anyhow::Result;
45
use everscale_types::cell::Lazy;
56
use everscale_types::models::*;
67
use everscale_types::prelude::*;
78

9+
use crate::state::shard_state_data::ShardStateData;
810
use crate::state::{MinRefMcStateTracker, RefMcStateHandle};
911

1012
/// Parsed shard state.
@@ -26,25 +28,40 @@ impl ShardStateStuff {
2628
pub fn from_root(
2729
block_id: &BlockId,
2830
root: Cell,
31+
data_roots: BTreeMap<u64, Cell>,
2932
tracker: &MinRefMcStateTracker,
3033
) -> Result<Self> {
3134
let shard_state = root.parse::<Box<ShardStateUnsplit>>()?;
32-
Self::from_state_and_root(block_id, shard_state, root, tracker)
35+
36+
let shard_state_data = data_roots
37+
.into_iter()
38+
.map(|(k, cell)| ShardStateData::from_root(cell).map(|v| (k, v)))
39+
.collect::<Result<BTreeMap<u64, ShardStateData>>>()?;
40+
41+
Self::from_state_and_root(block_id, root, shard_state, shard_state_data, tracker)
3342
}
3443

35-
pub fn from_state(
44+
pub fn from_root_and_accounts(
3645
block_id: &BlockId,
37-
shard_state: Box<ShardStateUnsplit>,
46+
root: Cell,
47+
state_data: BTreeMap<u64, ShardAccounts>,
3848
tracker: &MinRefMcStateTracker,
3949
) -> Result<Self> {
40-
let root = CellBuilder::build_from(&shard_state)?;
41-
ShardStateStuff::from_state_and_root(block_id, shard_state, root, tracker)
50+
let shard_state = root.parse::<Box<ShardStateUnsplit>>()?;
51+
52+
let shard_state_data = state_data
53+
.into_iter()
54+
.map(|(k, acc)| ShardStateData::from_accounts(acc).map(|v| (k, v)))
55+
.collect::<Result<BTreeMap<_, _>>>()?;
56+
57+
Self::from_state_and_root(block_id, root, shard_state, shard_state_data, tracker)
4258
}
4359

4460
pub fn from_state_and_root(
4561
block_id: &BlockId,
46-
shard_state: Box<ShardStateUnsplit>,
4762
root: Cell,
63+
shard_state: Box<ShardStateUnsplit>,
64+
shard_state_data: BTreeMap<u64, ShardStateData>,
4865
tracker: &MinRefMcStateTracker,
4966
) -> Result<Self> {
5067
anyhow::ensure!(
@@ -68,38 +85,13 @@ impl ShardStateStuff {
6885
block_id: *block_id,
6986
shard_state_extra: shard_state.load_custom()?,
7087
shard_state,
88+
shard_state_data,
7189
root,
7290
handle,
7391
}),
7492
})
7593
}
7694

77-
pub fn deserialize_zerostate(
78-
zerostate_id: &BlockId,
79-
bytes: &[u8],
80-
tracker: &MinRefMcStateTracker,
81-
) -> Result<Self> {
82-
anyhow::ensure!(zerostate_id.seqno == 0, "given id has a non-zero seqno");
83-
84-
let file_hash = Boc::file_hash_blake(bytes);
85-
anyhow::ensure!(
86-
zerostate_id.file_hash.as_slice() == file_hash.as_slice(),
87-
"file_hash mismatch. Expected: {}, got: {}",
88-
hex::encode(file_hash),
89-
zerostate_id.file_hash,
90-
);
91-
92-
let root = Boc::decode(bytes)?;
93-
anyhow::ensure!(
94-
&zerostate_id.root_hash == root.repr_hash(),
95-
"root_hash mismatch for {zerostate_id}. Expected: {expected}, got: {got}",
96-
expected = zerostate_id.root_hash,
97-
got = root.repr_hash(),
98-
);
99-
100-
Self::from_root(zerostate_id, root, tracker)
101-
}
102-
10395
pub fn block_id(&self) -> &BlockId {
10496
&self.inner.block_id
10597
}
@@ -123,6 +115,22 @@ impl ShardStateStuff {
123115
&self.inner.root
124116
}
125117

118+
pub fn data_root_cells(&self) -> BTreeMap<u64, Cell> {
119+
self.inner
120+
.shard_state_data
121+
.iter()
122+
.map(|(k, v)| (*k, v.root_cell().clone()))
123+
.collect()
124+
}
125+
126+
pub fn load_accounts(&self) -> BTreeMap<u64, ShardAccounts> {
127+
self.inner
128+
.shard_state_data
129+
.iter()
130+
.map(|(k, v)| (*k, v.accounts().clone()))
131+
.collect()
132+
}
133+
126134
pub fn shards(&self) -> Result<&ShardHashes> {
127135
Ok(&self.state_extra()?.shards)
128136
}
@@ -167,6 +175,7 @@ unsafe impl arc_swap::RefCnt for ShardStateStuff {
167175
pub struct Inner {
168176
block_id: BlockId,
169177
shard_state: Box<ShardStateUnsplit>,
178+
shard_state_data: BTreeMap<u64, ShardStateData>,
170179
shard_state_extra: Option<McStateExtra>,
171180
handle: RefMcStateHandle,
172181
root: Cell,

cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ async-trait = { workspace = true }
2020
base64 = { workspace = true }
2121
bytes = { workspace = true }
2222
bytesize = { workspace = true }
23+
bumpalo = { workspace = true }
2324
clap = { workspace = true }
2425
clap-markdown = { workspace = true }
2526
dirs = { workspace = true }

cli/src/cmd/tools/gen_zerostate.rs

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
use std::collections::hash_map;
1+
use std::collections::{hash_map, BTreeMap};
22
use std::path::PathBuf;
33
use std::sync::OnceLock;
44

55
use anyhow::{Context, Result};
6+
use bumpalo::Bump;
67
use everscale_crypto::ed25519;
8+
use everscale_types::boc;
79
use everscale_types::cell::Lazy;
810
use everscale_types::models::*;
911
use everscale_types::num::Tokens;
1012
use everscale_types::prelude::*;
1113
use serde::{Deserialize, Serialize};
12-
use tycho_util::{FastHashMap, FastHashSet};
14+
use tycho_block_util::state::split_shard;
15+
use tycho_util::{FastHashMap, FastHashSet, FastHasherState};
1316

1417
use crate::util::{compute_storage_used, print_json};
1518

@@ -88,14 +91,37 @@ fn generate_zerostate(
8891
.add_required_accounts()
8992
.context("failed to add required accounts")?;
9093

91-
let state = config
94+
let bump = Bump::new();
95+
96+
let mut boc = boc::ser::BocHeader::<FastHasherState>::with_capacity(33);
97+
98+
let (state, accounts) = config
9299
.build_masterchain_state(now)
93100
.context("failed to build masterchain zerostate")?;
94101

95-
let boc = CellBuilder::build_from(&state).context("failed to serialize zerostate")?;
102+
let state_cell = CellBuilder::build_from(&state).context("failed to serialize zerostate")?;
103+
let root_hash = *state_cell.repr_hash();
104+
105+
boc.add_root(state_cell.as_ref());
106+
107+
for (prefix, shard_accounts) in &accounts {
108+
let prefix_cell = CellBuilder::build_from(prefix).context("failed to serialize prefix")?;
109+
110+
let cell = &*bump.alloc(prefix_cell);
111+
boc.add_root(cell.as_ref());
112+
113+
let shard_accounts_cell =
114+
CellBuilder::build_from(shard_accounts).context("failed to serialize accounts")?;
115+
116+
let cell = &*bump.alloc(shard_accounts_cell);
117+
boc.add_root(cell.as_ref());
118+
}
119+
120+
let boc_size = boc.compute_stats().total_size as usize;
121+
122+
let mut data = Vec::with_capacity(boc_size);
123+
boc.encode(&mut data);
96124

97-
let root_hash = *boc.repr_hash();
98-
let data = Boc::encode(&boc);
99125
let file_hash = Boc::file_hash_blake(&data);
100126

101127
std::fs::write(output_path, data).context("failed to write masterchain zerostate")?;
@@ -322,16 +348,20 @@ impl ZerostateConfig {
322348
Ok(())
323349
}
324350

325-
fn build_masterchain_state(self, now: u32) -> Result<ShardStateUnsplit> {
351+
fn build_masterchain_state(
352+
self,
353+
now: u32,
354+
) -> Result<(ShardStateUnsplit, BTreeMap<u64, ShardAccounts>)> {
326355
let mut state = make_shard_state(self.global_id, ShardIdent::MASTERCHAIN, now);
327356

328357
let config = BlockchainConfig {
329358
address: self.params.get::<ConfigParam0>()?.unwrap(),
330359
params: self.params.clone(),
331360
};
332361

362+
let mut accounts = ShardAccounts::new();
363+
let mut splitted_accounts = BTreeMap::new();
333364
{
334-
let mut accounts = ShardAccounts::new();
335365
let mut libraries = FastHashMap::<HashBytes, (Cell, FastHashSet<HashBytes>)>::default();
336366
for (account, mut account_state) in self.accounts {
337367
let balance = match account_state.as_mut() {
@@ -383,7 +413,21 @@ impl ZerostateConfig {
383413
update_config_account(&mut accounts, &config)?;
384414

385415
assert_eq!(state.total_balance, accounts.root_extra().balance);
386-
state.accounts = Lazy::new(&accounts)?;
416+
417+
// Split to 16 virtual shards
418+
split_shard(
419+
&ShardIdent::MASTERCHAIN,
420+
&accounts,
421+
4,
422+
&mut splitted_accounts,
423+
)?;
424+
425+
let accounts_roots = splitted_accounts
426+
.iter()
427+
.map(|(k, v)| (*k, *CellBuilder::build_from(v).unwrap().repr_hash()))
428+
.collect::<BTreeMap<_, _>>();
429+
430+
state.accounts = Dict::try_from_btree(&accounts_roots)?;
387431

388432
// Build lib dict
389433
let mut libs = Dict::new();
@@ -465,7 +509,7 @@ impl ZerostateConfig {
465509
global_balance: state.total_balance.clone(),
466510
})?);
467511

468-
Ok(state)
512+
Ok((state, splitted_accounts))
469513
}
470514
}
471515

@@ -485,9 +529,22 @@ impl Default for ZerostateConfig {
485529
}
486530

487531
fn make_shard_state(global_id: i32, shard_ident: ShardIdent, now: u32) -> ShardStateUnsplit {
532+
let accounts = ShardAccounts::new();
533+
let mut splitted_accounts = BTreeMap::new();
534+
535+
split_shard(&shard_ident, &accounts, 4, &mut splitted_accounts).unwrap();
536+
537+
let accounts_roots = splitted_accounts
538+
.into_iter()
539+
.map(|(k, v)| (k, *CellBuilder::build_from(v).unwrap().repr_hash()))
540+
.collect::<BTreeMap<_, _>>();
541+
542+
let accounts = Dict::try_from_btree(&accounts_roots).unwrap();
543+
488544
ShardStateUnsplit {
489545
global_id,
490546
shard_ident,
547+
accounts,
491548
gen_utime: now,
492549
min_ref_mc_seqno: u32::MAX,
493550
..Default::default()

0 commit comments

Comments
 (0)