Skip to content

Commit

Permalink
feat: add stdlib smt collection
Browse files Browse the repository at this point in the history
This commit introduces a `collections` module for the `stdlib`. It
contains, initially, functions to support Sparse Merkle Tree
functionality. Initially, the `smt::get` is available; it will fetch the
value of a key from a Sparse Merkle tree.

It adds a `adv.smtget` that will push to the advice stack information
about a Sparse Merkle tree keyed leaf.
  • Loading branch information
vlopes11 committed Apr 12, 2023
1 parent 5b3eb67 commit 600f15f
Show file tree
Hide file tree
Showing 24 changed files with 865 additions and 107 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## 0.6.0 (TBD)

#### Assembly
- Added new instructions: `adv.smtget`.

#### Stdlib
- Added new module: `collections::smt` with `smt::get`.

## 0.5.0 (2023-03-29)

#### CLI
Expand Down
1 change: 1 addition & 0 deletions assembly/src/assembler/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ impl Assembler {
Instruction::AdvMem(a, n) => adv_ops::adv_mem(span, *a, *n),
Instruction::AdvExt2Inv => span.add_decorator(Decorator::Advice(Ext2Inv)),
Instruction::AdvExt2INTT => span.add_decorator(Decorator::Advice(Ext2INTT)),
Instruction::AdvSmtGet => span.add_decorator(Decorator::Advice(SmtGet)),

// ----- cryptographic instructions ---------------------------------------------------
Instruction::Hash => crypto_ops::hash(span),
Expand Down
9 changes: 8 additions & 1 deletion assembly/src/parsers/adv_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use super::{
// INSTRUCTION PARSERS
// ================================================================================================

/// Returns `AdvU64Div`, `AdvKeyval`, or `AdvMem` instruction node.
/// Returns `AdvU64Div`, `AdvKeyval`, `AdvMem`, `AdvExt2Inv`, `AdvExt2INTT`, or `AdvSmtGet`
/// instruction node.
///
/// # Errors
/// Returns an error if:
Expand Down Expand Up @@ -55,6 +56,12 @@ pub fn parse_adv_inject(op: &Token) -> Result<Node, ParsingError> {
}
Ok(Instruction(AdvExt2INTT))
}
"smtget" => {
if op.num_parts() > 2 {
return Err(ParsingError::extra_param(op));
}
Ok(Instruction(AdvSmtGet))
}
_ => Err(ParsingError::invalid_op(op)),
}
}
2 changes: 2 additions & 0 deletions assembly/src/parsers/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ pub enum Instruction {
AdvMem(u32, u32),
AdvExt2Inv,
AdvExt2INTT,
AdvSmtGet,

// ----- cryptographic operations -------------------------------------------------------------
Hash,
Expand Down Expand Up @@ -536,6 +537,7 @@ impl fmt::Display for Instruction {
Self::AdvMem(start_addr, num_words) => write!(f, "adv.mem.{start_addr}.{num_words}"),
Self::AdvExt2Inv => write!(f, "adv.ext2inv"),
Self::AdvExt2INTT => write!(f, "adv.ext2intt"),
Self::AdvSmtGet => write!(f, "adv.smtget"),

// ----- cryptographic operations -----------------------------------------------------
Self::Hash => write!(f, "hash"),
Expand Down
1 change: 1 addition & 0 deletions assembly/src/parsers/serde/deserialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ impl Deserializable for Instruction {
OpCode::AdvLoadW => Ok(Instruction::AdvLoadW),
OpCode::AdvExt2Inv => Ok(Instruction::AdvExt2Inv),
OpCode::AdvExt2INTT => Ok(Instruction::AdvExt2INTT),
OpCode::AdvSmtGet => Ok(Instruction::AdvSmtGet),

// ----- cryptographic operations -----------------------------------------------------
OpCode::Hash => Ok(Instruction::Hash),
Expand Down
25 changes: 13 additions & 12 deletions assembly/src/parsers/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,22 +257,23 @@ pub enum OpCode {
AdvMem = 227,
AdvExt2Inv = 228,
AdvExt2INTT = 229,
AdvSmtGet = 230,

// ----- cryptographic operations -------------------------------------------------------------
Hash = 230,
HMerge = 231,
HPerm = 232,
MTreeGet = 233,
MTreeSet = 234,
MTreeMerge = 235,
FriExt2Fold4 = 236,
Hash = 231,
HMerge = 232,
HPerm = 233,
MTreeGet = 234,
MTreeSet = 235,
MTreeMerge = 236,
FriExt2Fold4 = 237,

// ----- exec / call --------------------------------------------------------------------------
ExecLocal = 237,
ExecImported = 238,
CallLocal = 239,
CallImported = 240,
SysCall = 241,
ExecLocal = 238,
ExecImported = 239,
CallLocal = 240,
CallImported = 241,
SysCall = 242,

// ----- control flow -------------------------------------------------------------------------
IfElse = 253,
Expand Down
1 change: 1 addition & 0 deletions assembly/src/parsers/serde/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ impl Serializable for Instruction {
Self::AdvLoadW => OpCode::AdvLoadW.write_into(target),
Self::AdvExt2Inv => OpCode::AdvExt2Inv.write_into(target),
Self::AdvExt2INTT => OpCode::AdvExt2INTT.write_into(target),
Self::AdvSmtGet => OpCode::AdvSmtGet.write_into(target),

// ----- cryptographic operations -----------------------------------------------------
Self::Hash => OpCode::Hash.write_into(target),
Expand Down
3 changes: 2 additions & 1 deletion assembly/src/parsers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,12 @@ fn test_ast_parsing_adv_ops() {

#[test]
fn test_ast_parsing_adv_injection() {
let source = "begin adv.u64div adv.keyval adv.mem.1.1 end";
let source = "begin adv.u64div adv.keyval adv.mem.1.1 adv.smtget end";
let nodes: Vec<Node> = vec![
Node::Instruction(Instruction::AdvU64Div),
Node::Instruction(Instruction::AdvKeyval),
Node::Instruction(Instruction::AdvMem(1, 1)),
Node::Instruction(Instruction::AdvSmtGet),
];

assert_program_output(source, BTreeMap::new(), nodes);
Expand Down
3 changes: 2 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ pub use ::crypto::{Word, ONE, WORD_SIZE, ZERO};
pub mod crypto {
pub mod merkle {
pub use ::crypto::merkle::{
MerkleError, MerklePath, MerklePathSet, MerkleStore, MerkleTree, NodeIndex, SimpleSmt,
EmptySubtreeRoots, MerkleError, MerklePath, MerklePathSet, MerkleStore, MerkleTree,
NodeIndex, SimpleSmt,
};
}

Expand Down
17 changes: 17 additions & 0 deletions core/src/operations/decorators/advice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ pub enum AdviceInjector {
/// routine interpolates ( using inverse NTT ) the evaluations into a polynomial in
/// coefficient form and pushes the result into the advice stack.
Ext2INTT,

/// Pushes the value and depth flags of a leaf indexed by `key` on a Sparse Merkle tree with
/// the provided `root`.
///
/// The Sparse Merkle tree is tiered, meaning it will have leaf depths in `{16, 32, 48, 64}`.
/// The depth flags define the tier on which the leaf is located.
///
/// The operand stack is expected to be arranged as follows:
/// - key, 4 elements.
/// - root of the Sparse Merkle tree, 4 elements.
///
/// After a successful operation, the advice stack will look as follows:
/// - boolean flag set to `1` if the depth is `16` or `32`.
/// - boolean flag set to `1` if the depth is `16` or `48`.
/// - value word; will be zeroed if the tree don't contain a mapped value for the key.
SmtGet,
}

impl fmt::Display for AdviceInjector {
Expand All @@ -63,6 +79,7 @@ impl fmt::Display for AdviceInjector {
Self::Memory(start_addr, num_words) => write!(f, "mem({start_addr}, {num_words})"),
Self::Ext2Inv => write!(f, "ext2_inv"),
Self::Ext2INTT => write!(f, "ext2_intt"),
Self::SmtGet => write!(f, "smt_get"),
}
}
}
1 change: 1 addition & 0 deletions processor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ winter-prover = { package = "winter-prover", version = "0.6", default-features =
logtest = { version = "2.0", default-features = false }
miden-assembly = { package = "miden-assembly", path = "../assembly", version = "0.6", default-features = false }
rand-utils = { package = "winter-rand-utils", version = "0.6" }
test-utils = { package = "miden-test-utils", path = "../test-utils", version = "0.1" }
winter-fri = { package = "winter-fri", version = "0.6" }
winter-utils = { package = "winter-utils", version = "0.6" }
15 changes: 14 additions & 1 deletion processor/src/advice/mem_provider.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
AdviceInputs, AdviceProvider, AdviceSource, BTreeMap, ExecutionError, Felt, IntoBytes,
MerklePath, MerkleStore, NodeIndex, Vec, Word,
MerklePath, MerkleStore, NodeIndex, StarkField, Vec, Word,
};

// MEMORY ADVICE PROVIDER
Expand Down Expand Up @@ -122,6 +122,19 @@ impl AdviceProvider for MemAdviceProvider {
.map_err(ExecutionError::MerkleStoreLookupFailed)
}

fn get_leaf_depth(
&self,
root: Word,
tree_depth: &Felt,
index: &Felt,
) -> Result<u8, ExecutionError> {
let tree_depth = u8::try_from(tree_depth.as_int())
.map_err(|_| ExecutionError::InvalidTreeDepth { depth: *tree_depth })?;
self.store
.get_leaf_depth(root, tree_depth, index.as_int())
.map_err(ExecutionError::MerkleStoreLookupFailed)
}

fn update_merkle_node(
&mut self,
root: Word,
Expand Down
26 changes: 25 additions & 1 deletion processor/src/advice/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{ExecutionError, Felt, InputError, Word};
use super::{ExecutionError, Felt, InputError, StarkField, Word};
use vm_core::{
crypto::merkle::{MerklePath, MerkleStore, NodeIndex},
utils::{
Expand Down Expand Up @@ -124,6 +124,21 @@ pub trait AdviceProvider {
index: &Felt,
) -> Result<MerklePath, ExecutionError>;

/// Reconstructs a path from the root until a leaf or empty node and returns its depth.
///
/// For more information, check [MerkleStore::get_leaf_depth].
///
/// # Errors
/// Will return an error if:
/// - The provided `tree_depth` doesn't fit `u8`.
/// - The conditions of [MerkleStore::get_leaf_depth] aren't met.
fn get_leaf_depth(
&self,
root: Word,
tree_depth: &Felt,
index: &Felt,
) -> Result<u8, ExecutionError>;

/// Updates a node at the specified depth and index in a Merkle tree with the specified root;
/// returns the Merkle path from the updated node to the new root.
///
Expand Down Expand Up @@ -211,6 +226,15 @@ where
T::get_merkle_path(self, root, depth, index)
}

fn get_leaf_depth(
&self,
root: Word,
tree_depth: &Felt,
index: &Felt,
) -> Result<u8, ExecutionError> {
T::get_leaf_depth(self, root, tree_depth, index)
}

fn update_merkle_node(
&mut self,
root: Word,
Expand Down
Loading

0 comments on commit 600f15f

Please sign in to comment.