Skip to content

Commit 5effd15

Browse files
committed
Merge branch 'compute-size-and-assert-valid-size-indicator'
Add a derivable function to the `TasmObject`: `compute_size_and_assert_valid_size_indicator`. This function returns code that returns the size of the BFieldCodec encoded struct in memory. Furthermore, all size indicators in the structure are verified. This function is intended to be called on pre-loaded objects that already exist in memory, as the program execution starts. The functionality comes neatly wrapped in a snippet: `VerifyNdSiIntegrity`.
2 parents fdb2cfd + 57b7d5b commit 5effd15

25 files changed

+5686
-2111
lines changed

derive_tasm_object/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ description = "Derive macro for TasmObject"
77
[dependencies]
88
quote = "1.0"
99
syn = { version = "^2", features = ["full", "derive"] }
10+
proc-macro2 = { version = "1" }
1011

1112
[lib]
1213
proc-macro = true

derive_tasm_object/src/lib.rs

Lines changed: 131 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,129 @@ struct ParseResult {
5353
ignored_fields: Vec<syn::Field>,
5454
}
5555

56+
fn generate_integral_size_indicators_code(parse_result: &ParseResult) -> proc_macro2::TokenStream {
57+
let mut integral_size_indicators_code = quote! {};
58+
for field_type in parse_result.field_types.iter() {
59+
// INVARIANT: accumulated_size *field_start
60+
integral_size_indicators_code = quote! {
61+
#integral_size_indicators_code
62+
// _ accumulated_size *field_start
63+
if let Some(static_length) = <#field_type as crate::triton_vm::twenty_first::math::bfield_codec::BFieldCodec>::static_length() {
64+
let addi_len = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::AddI(crate::BFieldElement::new(static_length as u64)));
65+
[
66+
// _ accumulated_size *field
67+
addi_len.clone(),
68+
swap1.clone(),
69+
addi_len.clone(),
70+
swap1.clone(),
71+
// _ accumulated_size' *next_field
72+
].to_vec()
73+
} else {
74+
[
75+
[
76+
// _ accumulated_size *indicated_field_size
77+
read_mem1.clone(),
78+
// _ accumulated_size indicated_field_size (*field-2)
79+
80+
pushmax.clone(),
81+
dup2.clone(),
82+
lt.clone(),
83+
// _ accumulated_size indicated_field_size (*field-2) (MAX > indicated_field_size)
84+
85+
assert.clone(),
86+
// _ accumulated_size indicated_field_size (*field-2)
87+
88+
addi2.clone(),
89+
// _ accumulated_size indicated_field_size *field
90+
91+
dup0.clone(),
92+
// _ accumulated_size indicated_field_size *field *field
93+
].to_vec(),
94+
<#field_type as crate::tasm_lib::structure::tasm_object::TasmObject>::compute_size_and_assert_valid_size_indicator(library),
95+
// _ accumulated_size indicated_field_size *field computed_size
96+
[
97+
dup2.clone(),
98+
// _ accumulated_size indicated_field_size *field computed_size indicated_field_size
99+
eq.clone(),
100+
// _ accumulated_size indicated_field_size *field (computed_size == indicated_field_size)
101+
assert.clone(),
102+
// _ accumulated_size indicated_field_size *field
103+
104+
dup1.clone(),
105+
add.clone(),
106+
// _ accumulated_size indicated_field_size *next_field
107+
108+
swap2.clone(),
109+
// _ *next_field indicated_field_size accumulated_size
110+
111+
/* Add one for size-indicator on this struct */
112+
add.clone(),
113+
addi1.clone(),
114+
// _ *next_field accumulated_size'
115+
116+
swap1.clone(),
117+
// _ accumulated_size' *next_field
118+
].to_vec(),
119+
].concat()
120+
},
121+
};
122+
}
123+
124+
integral_size_indicators_code = quote! {
125+
let push0 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Push(0u64.into()));
126+
let pushmax = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Push(Self::MAX_OFFSET.into()));
127+
let dup0 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Dup(crate::triton_vm::op_stack::OpStackElement::ST0));
128+
let dup1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Dup(crate::triton_vm::op_stack::OpStackElement::ST1));
129+
let dup2 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Dup(crate::triton_vm::op_stack::OpStackElement::ST2));
130+
let swap1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Swap(crate::triton_vm::op_stack::OpStackElement::ST1));
131+
let swap2 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Swap(crate::triton_vm::op_stack::OpStackElement::ST2));
132+
let lt = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Lt);
133+
let assert = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Assert);
134+
let eq = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Eq);
135+
let add = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Add);
136+
let read_mem1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::ReadMem(crate::triton_vm::op_stack::NumberOfWords::N1));
137+
let addi1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::AddI(crate::BFieldElement::new(1u64)));
138+
let addi2 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::AddI(crate::BFieldElement::new(2u64)));
139+
let pop1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Pop(crate::triton_vm::op_stack::NumberOfWords::N1));
140+
let hint_acc_size = [
141+
crate::triton_vm::instruction::LabelledInstruction::TypeHint(
142+
crate::triton_vm::instruction::TypeHint {
143+
starting_index: 1,
144+
length: 1,
145+
type_name: std::option::Option::<std::string::String>::None,
146+
variable_name: std::string::String::from("acc_size"),
147+
}
148+
)
149+
].to_vec();
150+
let hint_field_ptr = [
151+
crate::triton_vm::instruction::LabelledInstruction::TypeHint(
152+
crate::triton_vm::instruction::TypeHint {
153+
starting_index: 0,
154+
length: 1,
155+
type_name: std::option::Option::<std::string::String>::None,
156+
variable_name: std::string::String::from("field_ptr"),
157+
}
158+
)
159+
].to_vec();
160+
[
161+
[
162+
push0.clone(),
163+
swap1.clone(),
164+
].to_vec(),
165+
hint_acc_size,
166+
hint_field_ptr,
167+
#integral_size_indicators_code
168+
[
169+
// _ acc_size *EOF
170+
pop1.clone(),
171+
// _ acc_size
172+
].to_vec(),
173+
].concat()
174+
};
175+
176+
integral_size_indicators_code
177+
}
178+
56179
fn impl_derive_tasm_object_macro(ast: DeriveInput) -> TokenStream {
57180
let parse_result = generate_parse_result(&ast);
58181

@@ -181,50 +304,17 @@ fn impl_derive_tasm_object_macro(ast: DeriveInput) -> TokenStream {
181304
}
182305
};
183306

184-
let encoding_length = if let Some(tokens) = get_current_field_start_with_jump.clone().last() {
185-
quote! {
186-
let first_field_with_jump_distance = { #tokens };
187-
let add = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Add);
188-
let dup0 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Dup(crate::triton_vm::op_stack::OpStackElement::ST0));
189-
let swap1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Swap(crate::triton_vm::op_stack::OpStackElement::ST1));
190-
let push_neg_1 = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Push(crate::triton_vm::prelude::BFieldElement::new(crate::triton_vm::prelude::BFieldElement::P-1)));
191-
let mul = crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Mul);
192-
193-
// Get `*field` and `size` for last field of encoding, which is the 1st in the definition.
194-
// Then add them to a pointer one past the end of the object. Then subtract `*object`
195-
// of the object to get final size.
196-
let extract_encoding_size = [
197-
// _ *object *field size
198-
add.clone(),
199-
// _ *object (*field+size)
200-
swap1,
201-
// _ (*field+size) *object
202-
push_neg_1,
203-
// _ (*field+size) *object -1
204-
mul,
205-
// _ (*field+size) (-*object)
206-
add
207-
// _ encoding_size
208-
].to_vec();
209-
210-
[
211-
[dup0].to_vec(),
212-
first_field_with_jump_distance,
213-
extract_encoding_size].concat()
214-
}
215-
} else {
216-
quote! {
217-
[
218-
crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Pop(crate::triton_vm::op_stack::NumberOfWords::N1)),
219-
crate::triton_vm::instruction::LabelledInstruction::Instruction(crate::triton_vm::instruction::AnInstruction::Push(crate::triton_vm::prelude::BFieldElement::new(0u64))),
220-
].to_vec()
221-
}
222-
};
307+
let integral_size_indicators_code = generate_integral_size_indicators_code(&parse_result);
223308

224309
let name = &ast.ident;
310+
let name_as_string = ast.ident.to_string();
225311
let gen = quote! {
226312
impl #impl_generics crate::tasm_lib::structure::tasm_object::TasmObject
227313
for #name #ty_generics #new_where_clause {
314+
fn label_friendly_name() -> String {
315+
#name_as_string.to_owned()
316+
}
317+
228318
fn get_field( field_name : &str ) -> Vec<crate::triton_vm::instruction::LabelledInstruction> {
229319
let field_getter = match field_name {
230320
#( #just_field_clauses ,)*
@@ -283,8 +373,8 @@ fn impl_derive_tasm_object_macro(ast: DeriveInput) -> TokenStream {
283373
}
284374
}
285375

286-
fn get_encoding_length() -> Vec<crate::triton_vm::instruction::LabelledInstruction> {
287-
#encoding_length
376+
fn compute_size_and_assert_valid_size_indicator(library: &mut crate::tasm_lib::Library) -> Vec<crate::triton_vm::instruction::LabelledInstruction> {
377+
#integral_size_indicators_code
288378
}
289379

290380
fn decode_iter<Itr: Iterator<Item=crate::BFieldElement>>(
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"name": "tasmlib_structure_verify_nd_si_integrity___ProofCollectionLookalike",
4+
"benchmark_result": {
5+
"clock_cycle_count": 205,
6+
"hash_table_height": 234,
7+
"u32_table_height": 150,
8+
"op_stack_table_height": 136,
9+
"ram_table_height": 16
10+
},
11+
"case": "CommonCase"
12+
},
13+
{
14+
"name": "tasmlib_structure_verify_nd_si_integrity___ProofCollectionLookalike",
15+
"benchmark_result": {
16+
"clock_cycle_count": 289,
17+
"hash_table_height": 234,
18+
"u32_table_height": 212,
19+
"op_stack_table_height": 196,
20+
"ram_table_height": 22
21+
},
22+
"case": "WorstCase"
23+
}
24+
]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"name": "tasmlib_structure_verify_nd_si_integrity___TransactionKernelLookalike",
4+
"benchmark_result": {
5+
"clock_cycle_count": 270,
6+
"hash_table_height": 384,
7+
"u32_table_height": 240,
8+
"op_stack_table_height": 176,
9+
"ram_table_height": 15
10+
},
11+
"case": "CommonCase"
12+
},
13+
{
14+
"name": "tasmlib_structure_verify_nd_si_integrity___TransactionKernelLookalike",
15+
"benchmark_result": {
16+
"clock_cycle_count": 236,
17+
"hash_table_height": 384,
18+
"u32_table_height": 212,
19+
"op_stack_table_height": 156,
20+
"ram_table_height": 14
21+
},
22+
"case": "WorstCase"
23+
}
24+
]

tasm-lib/src/hashing/algebraic_hasher/sample_scalars_static_length_kmalloc.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@ impl SampleScalarsStaticLengthKMalloc {
3333
.try_into()
3434
.unwrap()
3535
}
36-
37-
pub(crate) fn scalars_kmalloc_name(&self) -> String {
38-
let num_elements_to_sample = self.num_elements_to_sample;
39-
let extra_capacity = self.extra_capacity;
40-
format!("scalars_kmalloc_{num_elements_to_sample}_{extra_capacity}")
41-
}
4236
}
4337

4438
impl BasicSnippet for SampleScalarsStaticLengthKMalloc {
@@ -60,6 +54,10 @@ impl BasicSnippet for SampleScalarsStaticLengthKMalloc {
6054
fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
6155
assert_eq!(10, tip5::RATE, "Code assumes Tip5's RATE is 10");
6256
assert_eq!(3, EXTENSION_DEGREE, "Code assumes extension degree 3");
57+
assert!(
58+
self.extra_capacity + self.num_elements_to_sample > 0,
59+
"Must allocate positive number of words"
60+
);
6361
let num_squeezes =
6462
SampleScalarsStaticLengthDynMalloc::num_squeezes(self.num_elements_to_sample);
6563

@@ -73,8 +71,7 @@ impl BasicSnippet for SampleScalarsStaticLengthKMalloc {
7371
let entrypoint = self.entrypoint();
7472
let squeeze_repeatedly_static_number =
7573
library.import(Box::new(SqueezeRepeatedlyStaticNumber { num_squeezes }));
76-
let scalars_pointer =
77-
library.pub_kmalloc(self.num_words_to_allocate(), self.scalars_kmalloc_name());
74+
let scalars_pointer = library.kmalloc(self.num_words_to_allocate());
7875

7976
triton_asm!(
8077
{entrypoint}:
@@ -182,8 +179,8 @@ pub(crate) mod tests {
182179

183180
#[proptest]
184181
fn verify_agreement_with_tip5_sample_scalars(
185-
#[strategy(0_usize..500)] num_elements_to_sample: usize,
186-
#[strategy(0_usize..500)] extra_capacity: usize,
182+
#[strategy(1_usize..500)] num_elements_to_sample: usize,
183+
#[strategy(1_usize..500)] extra_capacity: usize,
187184
#[strategy(arb())] mut sponge: Tip5,
188185
) {
189186
let snippet = SampleScalarsStaticLengthKMalloc {

tasm-lib/src/library.rs

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ pub struct Library {
2929
/// Imported dependencies.
3030
seen_snippets: HashMap<String, Vec<LabelledInstruction>>,
3131

32-
/// Known, and thus shareable, (static) memory allocations. Includes both their address and
33-
/// their size.
34-
pub_allocations: HashMap<String, (BFieldElement, u32)>,
35-
3632
/// The number of statically allocated words
3733
num_allocated_words: u32,
3834
}
@@ -51,7 +47,6 @@ impl Library {
5147
pub fn new() -> Self {
5248
Self {
5349
seen_snippets: HashMap::default(),
54-
pub_allocations: HashMap::default(),
5550
num_allocated_words: 0,
5651
}
5752
}
@@ -89,6 +84,13 @@ impl Library {
8984
dep_entrypoint
9085
}
9186

87+
/// Import code that does not implement the `Snippet` trait
88+
///
89+
/// If possible, you should use the [`import`](Self::import) method as
90+
/// it gives better protections and allows you to test functions in
91+
/// isolation. This method is intended to add function to the assembly
92+
/// that you have defined inline and where a function call is needed due to
93+
/// e.g. a dynamic counter.
9294
pub fn explicit_import(&mut self, name: &str, body: &[LabelledInstruction]) -> String {
9395
if !self.seen_snippets.contains_key(name) {
9496
self.seen_snippets.insert(name.to_owned(), body.to_vec());
@@ -123,6 +125,7 @@ impl Library {
123125
/// Statically allocate `num_words` words of memory. Panics if more static
124126
/// memory is required than what the capacity allows for.
125127
pub fn kmalloc(&mut self, num_words: u32) -> BFieldElement {
128+
assert!(num_words > 0, "must allocate a positive number of words");
126129
let address = STATIC_MEMORY_FIRST_ADDRESS
127130
- bfe!(self.num_allocated_words)
128131
- BFieldElement::new(num_words as u64 - 1);
@@ -133,24 +136,6 @@ impl Library {
133136

134137
address
135138
}
136-
137-
/// Statically allocate `num_words` words of memory and give it a name.
138-
/// Allows sharing the allocation with other snippets.
139-
pub fn pub_kmalloc(&mut self, num_words: u32, name: String) -> BFieldElement {
140-
let address = self.kmalloc(num_words);
141-
if let Some((addr, size)) = self
142-
.pub_allocations
143-
.insert(name.clone(), (address, num_words))
144-
{
145-
panic!("Public kmalloc for \"{name}\" overwrote previous allocation: ({addr}, {size})");
146-
};
147-
address
148-
}
149-
150-
/// Get the address and size of a public allocation.
151-
pub fn get_pub_allocation(&self, name: &str) -> (BFieldElement, u32) {
152-
self.pub_allocations[name]
153-
}
154139
}
155140

156141
#[derive(Debug)]
@@ -489,12 +474,5 @@ mod tests {
489474

490475
let third_free_address = lib.kmalloc(1000);
491476
assert_eq!(-BFieldElement::new(1009), third_free_address);
492-
493-
let fourth_free_address = lib.pub_kmalloc(10_000, "my_thing".to_string());
494-
assert_eq!(-BFieldElement::new(11_009), fourth_free_address);
495-
496-
let (address, size) = lib.get_pub_allocation("my_thing");
497-
assert_eq!(fourth_free_address, address);
498-
assert_eq!(10_000, size);
499477
}
500478
}

tasm-lib/src/list/swap_unchecked.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl Algorithm for SwapUnchecked {
174174
) -> AlgorithmInitialState {
175175
let mut rng: StdRng = SeedableRng::from_seed(seed);
176176
let list_pointer = BFieldElement::new(rng.gen());
177-
let list_length = rng.gen_range(0..200);
177+
let list_length = rng.gen_range(1..200);
178178
let a = rng.gen_range(0..list_length);
179179
let b = rng.gen_range(0..list_length);
180180
self.initial_state(list_pointer, list_length, a, b)

tasm-lib/src/neptune.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
pub mod mutator_set;
2+
3+
#[cfg(test)]
4+
pub(crate) mod neptune_like_types_for_tests;

0 commit comments

Comments
 (0)