Skip to content

Commit

Permalink
Merge pull request #401 from dusk-network/version-0-26-1-rc-0
Browse files Browse the repository at this point in the history
Optimise contract session to hold a base commit without a copy of contract index
  • Loading branch information
miloszm authored Nov 7, 2024
2 parents e28ba5f + f2be712 commit 237ceed
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 20 deletions.
14 changes: 13 additions & 1 deletion piecrust/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! A library for dealing with memories in trees.
mod bytecode;
mod commit;
mod memory;
mod metadata;
mod module;
Expand All @@ -28,6 +29,7 @@ use piecrust_uplink::ContractId;
use session::ContractDataEntry;
use tree::{Hash, NewContractIndex};

use crate::store::commit::CommitHulk;
use crate::store::tree::{
position_from_contract, BaseInfo, ContractIndexElement, ContractsMerkle,
PageTree,
Expand Down Expand Up @@ -235,7 +237,11 @@ impl ContractStore {

fn session_with_base(&self, base: Option<Hash>) -> ContractSession {
let base_commit = base.and_then(|hash| {
self.commit_store.lock().unwrap().get_commit(&hash).cloned() // todo: clone here
self.commit_store
.lock()
.unwrap()
.get_commit(&hash)
.map(|commit| commit.to_hulk())
});
ContractSession::new(
&self.root_dir,
Expand Down Expand Up @@ -502,6 +508,7 @@ impl Commit {
}
}

#[allow(dead_code)]
pub fn fast_clone<'a>(
&self,
contract_ids: impl Iterator<Item = &'a ContractId>,
Expand All @@ -519,6 +526,11 @@ impl Commit {
}
}

pub fn to_hulk(&self) -> CommitHulk {
CommitHulk::from_commit(self)
}

#[allow(dead_code)]
pub fn inclusion_proofs(
mut self,
contract_id: &ContractId,
Expand Down
195 changes: 195 additions & 0 deletions piecrust/src/store/commit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use crate::store::tree::{
position_from_contract, ContractIndexElement, ContractsMerkle, Hash,
NewContractIndex, PageTree,
};
use crate::store::{Commit, Memory};
use crate::PageOpening;
use piecrust_uplink::ContractId;
use std::cell::Ref;
use std::collections::BTreeSet;

#[derive(Debug, Clone)]
pub(crate) struct CommitHulk {
index: Option<*const NewContractIndex>,
index2: NewContractIndex,
contracts_merkle: ContractsMerkle,
maybe_hash: Option<Hash>,
}

impl CommitHulk {
pub fn from_commit(commit: &Commit) -> Self {
Self {
index: Some(&commit.index),
index2: NewContractIndex::new(),
contracts_merkle: commit.contracts_merkle.clone(),
maybe_hash: commit.maybe_hash,
}
}

pub fn new() -> Self {
Self {
index: None,
index2: NewContractIndex::new(),
contracts_merkle: ContractsMerkle::default(),
maybe_hash: None,
}
}

pub fn to_commit(&self) -> Commit {
let index = self.index.map(|p| unsafe { p.as_ref().unwrap() });
match index {
Some(p) => Commit {
index: p.clone(),
contracts_merkle: self.contracts_merkle.clone(),
maybe_hash: self.maybe_hash,
},
None => Commit {
index: NewContractIndex::new(),
contracts_merkle: self.contracts_merkle.clone(),
maybe_hash: self.maybe_hash,
},
}
}

pub fn fast_clone<'a>(
&self,
contract_ids: impl Iterator<Item = &'a ContractId>,
) -> Self {
let mut index2 = NewContractIndex::new();
for contract_id in contract_ids {
if let Some(a) = self.index_get(contract_id) {
index2.insert_contract_index(contract_id, a.clone());
}
}
Self {
index: None,
index2,
contracts_merkle: self.contracts_merkle.clone(),
maybe_hash: self.maybe_hash,
}
}

pub fn inclusion_proofs(
mut self,
contract_id: &ContractId,
) -> Option<impl Iterator<Item = (usize, PageOpening)>> {
let contract = self.remove_contract_index(contract_id)?;

let pos = position_from_contract(contract_id);

Some(contract.page_indices.into_iter().map(move |page_index| {
let tree_opening = self
.contracts_merkle
.opening(pos)
.expect("There must be a leaf for the contract");

let page_opening = contract
.tree
.opening(page_index as u64)
.expect("There must be a leaf for the page");

(
page_index,
PageOpening {
tree: tree_opening,
inner: page_opening,
},
)
}))
}

pub fn insert(&mut self, contract_id: ContractId, memory: &Memory) {
if self.index_get(&contract_id).is_none() {
self.insert_contract_index(
&contract_id,
ContractIndexElement {
tree: PageTree::new(memory.is_64()),
len: 0,
page_indices: BTreeSet::new(),
hash: None,
int_pos: None,
},
);
}
let (index, contracts_merkle) = self.get_mutables();
let element = index.get_mut(&contract_id, None).unwrap();

element.len = memory.current_len;

for (dirty_page, _, page_index) in memory.dirty_pages() {
element.page_indices.insert(*page_index);
let hash = Hash::new(dirty_page);
element.tree.insert(*page_index as u64, hash);
}

let root = element.tree.root();
let pos = position_from_contract(&contract_id);
let int_pos = contracts_merkle.insert(pos, *root);
element.hash = Some(*root);
element.int_pos = Some(int_pos);
}

// to satisfy borrow checker
fn get_mutables(
&mut self,
) -> (&mut NewContractIndex, &mut ContractsMerkle) {
(&mut self.index2, &mut self.contracts_merkle)
}

pub fn root(&self) -> Ref<Hash> {
tracing::trace!("calculating root started");
let ret = self.contracts_merkle.root();
tracing::trace!("calculating root finished");
ret
}

/*
index accessors
*/

pub fn remove_contract_index(
&mut self,
contract_id: &ContractId,
) -> Option<ContractIndexElement> {
self.index2.contracts_mut().remove(contract_id)
}

pub fn insert_contract_index(
&mut self,
contract_id: &ContractId,
element: ContractIndexElement,
) {
self.index2.contracts_mut().insert(*contract_id, element);
}

pub fn index_get(
&self,
contract_id: &ContractId,
) -> Option<&ContractIndexElement> {
let index = self.index.map(|p| unsafe { p.as_ref().unwrap() });
match index {
Some(p) => self
.index2
.get(contract_id, self.maybe_hash)
.or_else(move || p.get(contract_id, self.maybe_hash)),
None => self.index2.get(contract_id, self.maybe_hash),
}
}

pub fn index_contains_key(&self, contract_id: &ContractId) -> bool {
let index = self.index.map(|p| unsafe { p.as_ref().unwrap() });
match index {
Some(p) => {
self.index2.contains_key(contract_id)
|| p.contains_key(contract_id)
}
None => self.index2.contains_key(contract_id),
}
}
}
32 changes: 13 additions & 19 deletions piecrust/src/store/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ use dusk_wasmtime::Engine;
use piecrust_uplink::ContractId;

use crate::contract::ContractMetadata;
use crate::store::commit::CommitHulk;
use crate::store::tree::{Hash, PageOpening};
use crate::store::{
base_from_path, Bytecode, Call, Commit, Memory, Metadata, Module,
BASE_FILE, BYTECODE_DIR, ELEMENT_FILE, MAIN_DIR, MEMORY_DIR,
METADATA_EXTENSION, OBJECTCODE_EXTENSION, PAGE_SIZE,
base_from_path, Bytecode, Call, Memory, Metadata, Module, BASE_FILE,
BYTECODE_DIR, ELEMENT_FILE, MAIN_DIR, MEMORY_DIR, METADATA_EXTENSION,
OBJECTCODE_EXTENSION, PAGE_SIZE,
};
use crate::Error;

Expand All @@ -45,7 +46,7 @@ pub struct ContractSession {
contracts: BTreeMap<ContractId, ContractDataEntry>,
engine: Engine,

base: Option<Commit>,
base: Option<CommitHulk>,
root_dir: PathBuf,

call: mpsc::Sender<Call>,
Expand All @@ -65,7 +66,7 @@ impl ContractSession {
pub(crate) fn new<P: AsRef<Path>>(
root_dir: P,
engine: Engine,
base: Option<Commit>,
base: Option<CommitHulk>,
call: mpsc::Sender<Call>,
) -> Self {
Self {
Expand All @@ -92,7 +93,7 @@ impl ContractSession {
.base
.as_ref()
.map(|c| c.fast_clone(&mut self.contracts.keys()))
.unwrap_or(Commit::new());
.unwrap_or(CommitHulk::new());
for (contract, entry) in &self.contracts {
commit.insert(*contract, &entry.memory);
}
Expand All @@ -109,7 +110,7 @@ impl ContractSession {
contract: ContractId,
) -> Option<impl Iterator<Item = (usize, &[u8], PageOpening)>> {
tracing::trace!("memory_pages called commit cloning");
let mut commit = self.base.clone().unwrap_or(Commit::new());
let mut commit = self.base.clone().unwrap_or(CommitHulk::new());
for (contract, entry) in &self.contracts {
commit.insert(*contract, &entry.memory);
}
Expand Down Expand Up @@ -145,14 +146,9 @@ impl ContractSession {
let (replier, receiver) = mpsc::sync_channel(1);

let mut contracts = BTreeMap::new();
let mut base = self.base.as_ref().map(|c| Commit {
index: c.index.clone(),
contracts_merkle: c.contracts_merkle.clone(),
maybe_hash: c.maybe_hash,
});
let base = self.base.as_ref().map(|c| c.to_commit());

mem::swap(&mut self.contracts, &mut contracts);
mem::swap(&mut self.base, &mut base);

self.call
.send(Call::Commit {
Expand Down Expand Up @@ -249,7 +245,7 @@ impl ContractSession {
Vacant(entry) => match &self.base {
None => Ok(None),
Some(base_commit) => {
match base_commit.index.contains_key(&contract) {
match base_commit.index_contains_key(&contract) {
true => {
let base_dir = self.root_dir.join(MAIN_DIR);

Expand All @@ -269,9 +265,7 @@ impl ContractSession {
Module::from_file(&self.engine, module_path)?;
let metadata = Metadata::from_file(metadata_path)?;

let memory = match base_commit
.index
.get(&contract, base_commit.maybe_hash)
let memory = match base_commit.index_get(&contract)
{
Some(elem) => {
let page_indices =
Expand Down Expand Up @@ -336,7 +330,7 @@ impl ContractSession {
if self.contracts.contains_key(&contract_id) {
true
} else if let Some(base_commit) = &self.base {
base_commit.index.contains_key(&contract_id)
base_commit.index_contains_key(&contract_id)
} else {
false
}
Expand All @@ -363,7 +357,7 @@ impl ContractSession {
// If the position is already filled in the tree, the contract cannot be
// inserted.
if let Some(base) = self.base.as_ref() {
if base.index.contains_key(&contract_id) {
if base.index_contains_key(&contract_id) {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Existing contract '{contract_id}'"),
Expand Down
11 changes: 11 additions & 0 deletions piecrust/src/store/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ pub struct NewContractIndex {
inner_contracts: BTreeMap<ContractId, ContractIndexElement>,
}

impl NewContractIndex {
pub fn contracts(&self) -> &BTreeMap<ContractId, ContractIndexElement> {
&self.inner_contracts
}
pub fn contracts_mut(
&mut self,
) -> &mut BTreeMap<ContractId, ContractIndexElement> {
&mut self.inner_contracts
}
}

#[derive(Debug, Clone, Archive, Deserialize, Serialize)]
#[archive_attr(derive(CheckBytes))]
pub struct ContractsMerkle {
Expand Down

0 comments on commit 237ceed

Please sign in to comment.