Skip to content

Commit d6a6180

Browse files
committed
feat: to/from-consensus-buff implementation and tests
1 parent 1f6c2f6 commit d6a6180

File tree

4 files changed

+338
-8
lines changed

4 files changed

+338
-8
lines changed

clarity/src/vm/analysis/type_checker/tests/mod.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use crate::vm::analysis::AnalysisDatabase;
2727
use crate::vm::ast::errors::ParseErrors;
2828
use crate::vm::ast::{build_ast, parse};
2929
use crate::vm::contexts::OwnedEnvironment;
30+
use crate::vm::execute_v2;
3031
use crate::vm::representations::SymbolicExpression;
3132
use crate::vm::types::{
3233
BufferLength, FixedFunction, FunctionType, PrincipalData, QualifiedContractIdentifier,
@@ -72,6 +73,92 @@ fn ascii_type(size: u32) -> TypeSignature {
7273
TypeSignature::SequenceType(StringType(ASCII(size.try_into().unwrap()))).into()
7374
}
7475

76+
#[test]
77+
fn test_to_consensus_buff() {
78+
let good = [
79+
(
80+
"(to-consensus-buff (if true (some u1) (some u2)))",
81+
"(optional (buff 18))",
82+
),
83+
(
84+
"(to-consensus-buff (if true (ok u1) (ok u2)))",
85+
"(optional (buff 18))",
86+
),
87+
(
88+
"(to-consensus-buff (if true (ok 1) (err u2)))",
89+
"(optional (buff 18))",
90+
),
91+
(
92+
"(to-consensus-buff (if true (ok 1) (err true)))",
93+
"(optional (buff 18))",
94+
),
95+
(
96+
"(to-consensus-buff (if true (ok false) (err true)))",
97+
"(optional (buff 2))",
98+
),
99+
(
100+
"(to-consensus-buff (if true (err u1) (err u2)))",
101+
"(optional (buff 18))",
102+
),
103+
("(to-consensus-buff none)", "(optional (buff 1))"),
104+
("(to-consensus-buff 0x00)", "(optional (buff 6))"),
105+
("(to-consensus-buff \"a\")", "(optional (buff 6))"),
106+
("(to-consensus-buff u\"ab\")", "(optional (buff 13))"),
107+
("(to-consensus-buff (list 1 2 3 4))", "(optional (buff 73))"),
108+
(
109+
"(to-consensus-buff { apple: u1, orange: 2, blue: true })",
110+
"(optional (buff 58))",
111+
),
112+
(
113+
"(define-private (my-func (x (buff 1048566)))
114+
(to-consensus-buff x))
115+
(my-func 0x001122334455)
116+
",
117+
"(optional (buff 1048571))",
118+
),
119+
];
120+
121+
let bad = [
122+
(
123+
"(to-consensus-buff)",
124+
CheckErrors::IncorrectArgumentCount(1, 0),
125+
),
126+
(
127+
"(to-consensus-buff 0x00 0x00)",
128+
CheckErrors::IncorrectArgumentCount(1, 2),
129+
),
130+
(
131+
"(define-private (my-func (x (buff 1048576)))
132+
(to-consensus-buff x))",
133+
CheckErrors::ValueTooLarge,
134+
),
135+
(
136+
"(define-private (my-func (x (buff 1048570)))
137+
(to-consensus-buff x))",
138+
CheckErrors::ValueTooLarge,
139+
),
140+
(
141+
"(define-private (my-func (x (buff 1048567)))
142+
(to-consensus-buff x))",
143+
CheckErrors::ValueTooLarge,
144+
),
145+
];
146+
147+
for (good_test, expected) in good.iter() {
148+
let type_result = type_check_helper(good_test).unwrap();
149+
assert_eq!(expected, &type_result.to_string());
150+
151+
assert!(
152+
type_result.admits(&execute_v2(good_test).unwrap().unwrap()),
153+
"The analyzed type must admit the evaluated type"
154+
);
155+
}
156+
157+
for (bad_test, expected) in bad.iter() {
158+
assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err);
159+
}
160+
}
161+
75162
#[test]
76163
fn test_get_block_info() {
77164
let good = [

clarity/src/vm/functions/conversions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ pub fn from_consensus_buff(
256256
// Perform the deserialization and check that it deserialized to the expected
257257
// type. A type mismatch at this point is an error that should be surfaced in
258258
// Clarity (as a none return).
259-
let result = match Value::try_deserialize_bytes(&input_bytes, &type_arg) {
259+
let result = match Value::try_deserialize_bytes_exact(&input_bytes, &type_arg) {
260260
Ok(value) => value,
261261
Err(_) => return Ok(Value::none()),
262262
};

clarity/src/vm/tests/simple_apply_eval.rs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,211 @@ fn test_keccak256() {
211211
.for_each(|(program, expectation)| assert_eq!(to_buffer(expectation), execute(program)));
212212
}
213213

214+
#[test]
215+
/// This test serializes two different values which do fit in
216+
/// the Clarity maximum value size, but whose serializations
217+
/// do not. These tests would _not_ pass typechecking: in fact,
218+
/// the code comes from `type_checker::tests::test_to_consensus_buff`
219+
/// failure cases.
220+
fn test_to_consensus_buff_too_big() {
221+
let buff_setup = "
222+
;; Make a buffer with repeated concatenation.
223+
(define-private (make-buff-10)
224+
0x11223344556677889900)
225+
(define-private (octo-buff (x (buff 100000)))
226+
(concat (concat (concat x x) (concat x x))
227+
(concat (concat x x) (concat x x))))
228+
(define-private (make-buff-80)
229+
(unwrap-panic (as-max-len? (octo-buff (make-buff-10)) u80)))
230+
(define-private (make-buff-640)
231+
(unwrap-panic (as-max-len? (octo-buff (make-buff-80)) u640)))
232+
(define-private (make-buff-5120)
233+
(unwrap-panic (as-max-len? (octo-buff (make-buff-640)) u5120)))
234+
(define-private (make-buff-40960)
235+
(unwrap-panic (as-max-len? (octo-buff (make-buff-5120)) u40960)))
236+
(define-private (make-buff-327680)
237+
(unwrap-panic (as-max-len? (octo-buff (make-buff-40960)) u327680)))
238+
239+
(define-private (make-buff-24567)
240+
(let ((x (make-buff-5120))
241+
(y (make-buff-640))
242+
(z (make-buff-80))
243+
(a 0x11223344556677))
244+
;; 4x + 6y + 3z + a = 24567
245+
(concat
246+
(concat
247+
;; 4x
248+
(concat (concat x x) (concat x x))
249+
;; 6y
250+
(concat (concat (concat y y) (concat y y)) (concat y y)))
251+
;; 3z + a
252+
(concat (concat z z) (concat z a)))))
253+
254+
;; (3 * 327680) + 40960 + 24567 = 1048567
255+
(define-private (make-buff-1048567)
256+
(let ((x (make-buff-327680))
257+
(y (make-buff-40960))
258+
(z (make-buff-24567)))
259+
(concat (concat (concat x x) (concat x y)) z)))
260+
261+
(define-private (make-buff-1048570)
262+
(concat (make-buff-1048567) 0x112233))
263+
";
264+
265+
// this program prints the length of the
266+
// constructed 1048570 buffer and then executes
267+
// to-consensus-buff on it. if the buffer wasn't the
268+
// expect length, just return (some buffer), which will
269+
// cause the test assertion to fail.
270+
let program_check_1048570 = format!(
271+
"{}
272+
(let ((a (make-buff-1048570)))
273+
(if (is-eq (len a) u1048570)
274+
(to-consensus-buff a)
275+
(some 0x00)))
276+
",
277+
buff_setup
278+
);
279+
280+
let result = vm_execute_v2(&program_check_1048570)
281+
.expect("Should execute")
282+
.expect("Should have return value");
283+
284+
assert!(result.expect_optional().is_none());
285+
286+
// this program prints the length of the
287+
// constructed 1048567 buffer and then executes
288+
// to-consensus-buff on it. if the buffer wasn't the
289+
// expect length, just return (some buffer), which will
290+
// cause the test assertion to fail.
291+
let program_check_1048567 = format!(
292+
"{}
293+
(let ((a (make-buff-1048567)))
294+
(if (is-eq (len a) u1048567)
295+
(to-consensus-buff a)
296+
(some 0x00)))
297+
",
298+
buff_setup
299+
);
300+
301+
let result = vm_execute_v2(&program_check_1048567)
302+
.expect("Should execute")
303+
.expect("Should have return value");
304+
305+
assert!(result.expect_optional().is_none());
306+
}
307+
308+
#[test]
309+
fn test_from_consensus_buff_type_checks() {
310+
let vectors = [
311+
(
312+
"(from-consensus-buff uint 0x10 0x00)",
313+
"Unchecked(IncorrectArgumentCount(2, 3))",
314+
),
315+
(
316+
"(from-consensus-buff uint 1)",
317+
"Unchecked(TypeValueError(SequenceType(BufferType(BufferLength(1048576))), Int(1)))",
318+
),
319+
(
320+
"(from-consensus-buff 2 0x10)",
321+
"Unchecked(InvalidTypeDescription)",
322+
),
323+
];
324+
325+
for (input, expected) in vectors.iter() {
326+
let result = vm_execute_v2(input).expect_err("Should raise an error");
327+
eprintln!("{}", result.to_string());
328+
}
329+
330+
for (input, expected) in vectors.iter() {
331+
let result = vm_execute_v2(input).expect_err("Should raise an error");
332+
assert_eq!(&result.to_string(), expected);
333+
}
334+
}
335+
336+
#[test]
337+
/// This test tries a bunch of buffers which either
338+
/// do not parse, or parse to the incorrect type
339+
fn test_from_consensus_buff_missed_expectations() {
340+
let vectors = [
341+
("0x0000000000000000000000000000000001", "uint"),
342+
("0x00ffffffffffffffffffffffffffffffff", "uint"),
343+
("0x0100000000000000000000000000000001", "int"),
344+
("0x010000000000000000000000000000000101", "uint"),
345+
("0x0200000004deadbeef", "(buff 2)"),
346+
("0x0200000004deadbeef", "(buff 3)"),
347+
("0x0200000004deadbeef", "(string-ascii 8)"),
348+
("0x03", "uint"),
349+
("0x04", "(optional int)"),
350+
("0x0700ffffffffffffffffffffffffffffffff", "(response uint int)"),
351+
("0x0800ffffffffffffffffffffffffffffffff", "(response int uint)"),
352+
("0x09", "(response int int)"),
353+
("0x0b0000000400000000000000000000000000000000010000000000000000000000000000000002000000000000000000000000000000000300fffffffffffffffffffffffffffffffc",
354+
"(list 3 int)"),
355+
("0x0c000000020362617a0906666f6f62617203", "{ bat: (optional int), foobar: bool }"),
356+
("0xff", "int"),
357+
];
358+
359+
for (buff_repr, type_repr) in vectors.iter() {
360+
let program = format!("(from-consensus-buff {} {})", type_repr, buff_repr);
361+
eprintln!("{}", program);
362+
let result_val = vm_execute_v2(&program)
363+
.expect("from-consensus-buff should succeed")
364+
.expect("from-consensus-buff should return")
365+
.expect_optional();
366+
assert!(
367+
result_val.is_none(),
368+
"from-consensus-buff should return none"
369+
);
370+
}
371+
}
372+
373+
#[test]
374+
fn test_to_from_consensus_buff_vectors() {
375+
let vectors = [
376+
("0x0000000000000000000000000000000001", "1", "int"),
377+
("0x00ffffffffffffffffffffffffffffffff", "-1", "int"),
378+
("0x0100000000000000000000000000000001", "u1", "uint"),
379+
("0x0200000004deadbeef", "0xdeadbeef", "(buff 8)"),
380+
("0x03", "true", "bool"),
381+
("0x04", "false", "bool"),
382+
("0x050011deadbeef11ababffff11deadbeef11ababffff", "'S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S", "principal"),
383+
("0x060011deadbeef11ababffff11deadbeef11ababffff0461626364", "'S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S.abcd", "principal"),
384+
("0x0700ffffffffffffffffffffffffffffffff", "(ok -1)", "(response int int)"),
385+
("0x0800ffffffffffffffffffffffffffffffff", "(err -1)", "(response int int)"),
386+
("0x09", "none", "(optional int)"),
387+
("0x0a00ffffffffffffffffffffffffffffffff", "(some -1)", "(optional int)"),
388+
("0x0b0000000400000000000000000000000000000000010000000000000000000000000000000002000000000000000000000000000000000300fffffffffffffffffffffffffffffffc",
389+
"(list 1 2 3 -4)", "(list 4 int)"),
390+
("0x0c000000020362617a0906666f6f62617203", "{ baz: none, foobar: true }", "{ baz: (optional int), foobar: bool }"),
391+
];
392+
393+
// do `from-consensus-buff` tests
394+
for (buff_repr, value_repr, type_repr) in vectors.iter() {
395+
let program = format!("(from-consensus-buff {} {})", type_repr, buff_repr);
396+
eprintln!("{}", program);
397+
let result_val = vm_execute_v2(&program)
398+
.expect("from-consensus-buff should succeed")
399+
.expect("from-consensus-buff should return")
400+
.expect_optional()
401+
.expect("from-consensus-buff should return (some value)");
402+
let expected_val = execute(&value_repr);
403+
assert_eq!(result_val, expected_val);
404+
}
405+
406+
// do `to-consensus-buff` tests
407+
for (buff_repr, value_repr, _) in vectors.iter() {
408+
let program = format!("(to-consensus-buff {})", value_repr);
409+
let result_buffer = vm_execute_v2(&program)
410+
.expect("to-consensus-buff should succeed")
411+
.expect("to-consensus-buff should return")
412+
.expect_optional()
413+
.expect("to-consensus-buff should return (some buff)");
414+
let expected_buff = execute(&buff_repr);
415+
assert_eq!(result_buffer, expected_buff);
416+
}
417+
}
418+
214419
#[test]
215420
fn test_secp256k1() {
216421
let secp256k1_evals = [

0 commit comments

Comments
 (0)