1use alloc::{boxed::Box, sync::Arc, vec::Vec};
2use core::error::Error;
3
4use miden_air::RowIndex;
5use miette::Diagnostic;
6use vm_core::{
7 debuginfo::{SourceFile, SourceManager, SourceSpan},
8 mast::{BasicBlockNode, DecoratorId, MastForest, MastNodeExt, MastNodeId},
9 stack::MIN_STACK_DEPTH,
10 utils::to_hex,
11};
12use winter_prover::{ProverError, math::FieldElement};
13
14use super::{
15 Digest, Felt, QuadFelt, Word,
16 crypto::MerkleError,
17 system::{FMP_MAX, FMP_MIN},
18};
19use crate::MemoryError;
20
21#[derive(Debug, thiserror::Error, Diagnostic)]
25pub enum ExecutionError {
26 #[error("value for key {} not present in the advice map", to_hex(Felt::elements_as_bytes(.key)))]
27 #[diagnostic()]
28 AdviceMapKeyNotFound {
29 #[label]
30 label: SourceSpan,
31 #[source_code]
32 source_file: Option<Arc<SourceFile>>,
33 key: Word,
34 },
35 #[error("value for key {} already present in the advice map when loading MAST forest", to_hex(Felt::elements_as_bytes(.key)))]
36 #[diagnostic(help(
37 "previous values at key were '{prev_values:?}'. Operation would have replaced them with '{new_values:?}'",
38 ))]
39 AdviceMapKeyAlreadyPresent {
40 key: Word,
41 prev_values: Vec<Felt>,
42 new_values: Vec<Felt>,
43 },
44 #[error("advice stack read failed at clock cycle {row}")]
45 #[diagnostic()]
46 AdviceStackReadFailed {
47 #[label]
48 label: SourceSpan,
49 #[source_code]
50 source_file: Option<Arc<SourceFile>>,
51 row: RowIndex,
52 },
53 #[error("illegal use of instruction {0} while inside a syscall")]
55 CallInSyscall(&'static str),
56 #[error("instruction `caller` used outside of kernel context")]
58 CallerNotInSyscall,
59 #[error("external node with mast root {0} resolved to an external node")]
60 CircularExternalNode(Digest),
61 #[error("exceeded the allowed number of max cycles {0}")]
62 CycleLimitExceeded(u32),
63 #[error("decorator id {decorator_id} does not exist in MAST forest")]
64 DecoratorNotFoundInForest { decorator_id: DecoratorId },
65 #[error("division by zero at clock cycle {clk}")]
66 #[diagnostic()]
67 DivideByZero {
68 #[label]
69 label: SourceSpan,
70 #[source_code]
71 source_file: Option<Arc<SourceFile>>,
72 clk: RowIndex,
73 },
74 #[error("failed to execute the dynamic code block provided by the stack with root 0x{hex}; the block could not be found",
75 hex = to_hex(.digest.as_bytes())
76 )]
77 #[diagnostic()]
78 DynamicNodeNotFound {
79 #[label]
80 label: SourceSpan,
81 #[source_code]
82 source_file: Option<Arc<SourceFile>>,
83 digest: Digest,
84 },
85 #[error("error during processing of event in on_event handler")]
86 #[diagnostic()]
87 EventError {
88 #[label]
89 label: SourceSpan,
90 #[source_code]
91 source_file: Option<Arc<SourceFile>>,
92 #[source]
93 error: Box<dyn Error + Send + Sync + 'static>,
94 },
95 #[error("failed to execute Ext2Intt operation: {0}")]
96 Ext2InttError(Ext2InttError),
97 #[error("assertion failed at clock cycle {clk} with error {}",
98 match err_msg {
99 Some(msg) => format!("message: {msg}"),
100 None => format!("code: {err_code}"),
101 }
102 )]
103 #[diagnostic()]
104 FailedAssertion {
105 #[label]
106 label: SourceSpan,
107 #[source_code]
108 source_file: Option<Arc<SourceFile>>,
109 clk: RowIndex,
110 err_code: Felt,
111 err_msg: Option<Arc<str>>,
112 },
113 #[error("failed to execute the program for internal reason: {0}")]
114 FailedToExecuteProgram(&'static str),
115 #[error(
116 "Updating FMP register from {0} to {1} failed because {1} is outside of {FMP_MIN}..{FMP_MAX}"
117 )]
118 InvalidFmpValue(Felt, Felt),
119 #[error("FRI domain segment value cannot exceed 3, but was {0}")]
120 InvalidFriDomainSegment(u64),
121 #[error("degree-respecting projection is inconsistent: expected {0} but was {1}")]
122 InvalidFriLayerFolding(QuadFelt, QuadFelt),
123 #[error(
124 "when returning from a call or dyncall, stack depth must be {MIN_STACK_DEPTH}, but was {depth}"
125 )]
126 #[diagnostic()]
127 InvalidStackDepthOnReturn {
128 #[label("when returning from this call site")]
129 label: SourceSpan,
130 #[source_code]
131 source_file: Option<Arc<SourceFile>>,
132 depth: usize,
133 },
134 #[error(
135 "provided merkle tree {depth} is out of bounds and cannot be represented as an unsigned 8-bit integer"
136 )]
137 #[diagnostic()]
138 InvalidMerkleTreeDepth {
139 #[label]
140 label: SourceSpan,
141 #[source_code]
142 source_file: Option<Arc<SourceFile>>,
143 depth: Felt,
144 },
145 #[error("provided node index {index} is out of bounds for a merkle tree node at depth {depth}")]
146 #[diagnostic()]
147 InvalidMerkleTreeNodeIndex {
148 #[label]
149 label: SourceSpan,
150 #[source_code]
151 source_file: Option<Arc<SourceFile>>,
152 depth: Felt,
153 index: Felt,
154 },
155 #[error("attempted to calculate integer logarithm with zero argument at clock cycle {clk}")]
156 #[diagnostic()]
157 LogArgumentZero {
158 #[label]
159 label: SourceSpan,
160 #[source_code]
161 source_file: Option<Arc<SourceFile>>,
162 clk: RowIndex,
163 },
164 #[error("malformed signature key: {key_type}")]
165 #[diagnostic(help("the secret key associated with the provided public key is malformed"))]
166 MalformedSignatureKey {
167 #[label]
168 label: SourceSpan,
169 #[source_code]
170 source_file: Option<Arc<SourceFile>>,
171 key_type: &'static str,
172 },
173 #[error(
174 "MAST forest in host indexed by procedure root {root_digest} doesn't contain that root"
175 )]
176 MalformedMastForestInHost {
177 #[label]
178 label: SourceSpan,
179 #[source_code]
180 source_file: Option<Arc<SourceFile>>,
181 root_digest: Digest,
182 },
183 #[error("node id {node_id} does not exist in MAST forest")]
184 MastNodeNotFoundInForest { node_id: MastNodeId },
185 #[error(transparent)]
186 #[diagnostic(transparent)]
187 MemoryError(MemoryError),
188 #[error("no MAST forest contains the procedure with root digest {root_digest}")]
189 NoMastForestWithProcedure {
190 #[label]
191 label: SourceSpan,
192 #[source_code]
193 source_file: Option<Arc<SourceFile>>,
194 root_digest: Digest,
195 },
196 #[error("merkle path verification failed for value {value} at index {index} in the Merkle tree with root {root} (error {err})",
197 value = to_hex(Felt::elements_as_bytes(value)),
198 root = to_hex(root.as_bytes()),
199 err = match err_msg {
200 Some(msg) => format!("message: {msg}"),
201 None => format!("code: {err_code}"),
202 }
203 )]
204 MerklePathVerificationFailed {
205 #[label]
206 label: SourceSpan,
207 #[source_code]
208 source_file: Option<Arc<SourceFile>>,
209 value: Word,
210 index: Felt,
211 root: Digest,
212 err_code: Felt,
213 err_msg: Option<Arc<str>>,
214 },
215 #[error("failed to lookup value in Merkle store")]
216 MerkleStoreLookupFailed {
217 #[label]
218 label: SourceSpan,
219 #[source_code]
220 source_file: Option<Arc<SourceFile>>,
221 #[source]
222 err: MerkleError,
223 },
224 #[error("advice provider Merkle store backend merge failed")]
225 MerkleStoreMergeFailed {
226 #[label]
227 label: SourceSpan,
228 #[source_code]
229 source_file: Option<Arc<SourceFile>>,
230 #[source]
231 err: MerkleError,
232 },
233 #[error("advice provider Merkle store backend update failed")]
234 MerkleStoreUpdateFailed {
235 #[label]
236 label: SourceSpan,
237 #[source_code]
238 source_file: Option<Arc<SourceFile>>,
239 #[source]
240 err: MerkleError,
241 },
242 #[error("if statement expected a binary value on top of the stack, but got {value}")]
243 #[diagnostic()]
244 NotBinaryValueIf {
245 #[label]
246 label: SourceSpan,
247 #[source_code]
248 source_file: Option<Arc<SourceFile>>,
249 value: Felt,
250 },
251 #[error("operation expected a binary value, but got {value}")]
252 #[diagnostic()]
253 NotBinaryValueOp {
254 #[label]
255 label: SourceSpan,
256 #[source_code]
257 source_file: Option<Arc<SourceFile>>,
258 value: Felt,
259 },
260 #[error("loop condition must be a binary value, but got {value}")]
261 #[diagnostic(help(
262 "this could happen either when first entering the loop, or any subsequent iteration"
263 ))]
264 NotBinaryValueLoop {
265 #[label]
266 label: SourceSpan,
267 #[source_code]
268 source_file: Option<Arc<SourceFile>>,
269 value: Felt,
270 },
271 #[error("operation expected a u32 value, but got {value} (error code: {err_code})")]
272 NotU32Value {
273 #[label]
274 label: SourceSpan,
275 #[source_code]
276 source_file: Option<Arc<SourceFile>>,
277 value: Felt,
278 err_code: Felt,
279 },
280 #[error("stack should have at most {MIN_STACK_DEPTH} elements at the end of program execution, but had {} elements", MIN_STACK_DEPTH + .0)]
281 OutputStackOverflow(usize),
282 #[error("a program has already been executed in this process")]
283 ProgramAlreadyExecuted,
284 #[error("proof generation failed")]
285 ProverError(#[source] ProverError),
286 #[error("smt node {node_hex} not found", node_hex = to_hex(Felt::elements_as_bytes(node)))]
287 SmtNodeNotFound {
288 #[label]
289 label: SourceSpan,
290 #[source_code]
291 source_file: Option<Arc<SourceFile>>,
292 node: Word,
293 },
294 #[error("expected pre-image length of node {node_hex} to be a multiple of 8 but was {preimage_len}",
295 node_hex = to_hex(Felt::elements_as_bytes(node)),
296 )]
297 SmtNodePreImageNotValid {
298 #[label]
299 label: SourceSpan,
300 #[source_code]
301 source_file: Option<Arc<SourceFile>>,
302 node: Word,
303 preimage_len: usize,
304 },
305 #[error("syscall failed: procedure with root {hex} was not found in the kernel",
306 hex = to_hex(proc_root.as_bytes())
307 )]
308 SyscallTargetNotInKernel {
309 #[label]
310 label: SourceSpan,
311 #[source_code]
312 source_file: Option<Arc<SourceFile>>,
313 proc_root: Digest,
314 },
315 #[error("failed to execute arithmetic circuit evaluation operation: {error}")]
316 #[diagnostic()]
317 AceChipError {
318 #[label("this call failed")]
319 label: SourceSpan,
320 #[source_code]
321 source_file: Option<Arc<SourceFile>>,
322 error: AceError,
323 },
324}
325
326impl From<Ext2InttError> for ExecutionError {
327 fn from(value: Ext2InttError) -> Self {
328 Self::Ext2InttError(value)
329 }
330}
331
332impl ExecutionError {
333 pub fn advice_map_key_not_found(
334 key: Word,
335 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
336 ) -> Self {
337 let (label, source_file) = err_ctx.label_and_source_file();
338 Self::AdviceMapKeyNotFound { label, source_file, key }
339 }
340
341 pub fn advice_stack_read_failed(
342 row: RowIndex,
343 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
344 ) -> Self {
345 let (label, source_file) = err_ctx.label_and_source_file();
346 Self::AdviceStackReadFailed { label, source_file, row }
347 }
348
349 pub fn divide_by_zero(clk: RowIndex, err_ctx: &ErrorContext<'_, impl MastNodeExt>) -> Self {
350 let (label, source_file) = err_ctx.label_and_source_file();
351 Self::DivideByZero { clk, label, source_file }
352 }
353
354 pub fn dynamic_node_not_found(
355 digest: Digest,
356 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
357 ) -> Self {
358 let (label, source_file) = err_ctx.label_and_source_file();
359
360 Self::DynamicNodeNotFound { label, source_file, digest }
361 }
362
363 pub fn event_error(
364 error: Box<dyn Error + Send + Sync + 'static>,
365 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
366 ) -> Self {
367 let (label, source_file) = err_ctx.label_and_source_file();
368
369 Self::EventError { label, source_file, error }
370 }
371
372 pub fn failed_assertion(
373 clk: RowIndex,
374 err_code: Felt,
375 err_msg: Option<Arc<str>>,
376 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
377 ) -> Self {
378 let (label, source_file) = err_ctx.label_and_source_file();
379
380 Self::FailedAssertion {
381 label,
382 source_file,
383 clk,
384 err_code,
385 err_msg,
386 }
387 }
388
389 pub fn invalid_merkle_tree_depth(
390 depth: Felt,
391 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
392 ) -> Self {
393 let (label, source_file) = err_ctx.label_and_source_file();
394 Self::InvalidMerkleTreeDepth { label, source_file, depth }
395 }
396
397 pub fn invalid_merkle_tree_node_index(
398 depth: Felt,
399 index: Felt,
400 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
401 ) -> Self {
402 let (label, source_file) = err_ctx.label_and_source_file();
403 Self::InvalidMerkleTreeNodeIndex { label, source_file, depth, index }
404 }
405
406 pub fn invalid_stack_depth_on_return(
407 depth: usize,
408 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
409 ) -> Self {
410 let (label, source_file) = err_ctx.label_and_source_file();
411 Self::InvalidStackDepthOnReturn { label, source_file, depth }
412 }
413
414 pub fn log_argument_zero(clk: RowIndex, err_ctx: &ErrorContext<'_, impl MastNodeExt>) -> Self {
415 let (label, source_file) = err_ctx.label_and_source_file();
416 Self::LogArgumentZero { label, source_file, clk }
417 }
418
419 pub fn malfored_mast_forest_in_host(
420 root_digest: Digest,
421 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
422 ) -> Self {
423 let (label, source_file) = err_ctx.label_and_source_file();
424 Self::MalformedMastForestInHost { label, source_file, root_digest }
425 }
426
427 pub fn malformed_signature_key(
428 key_type: &'static str,
429 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
430 ) -> Self {
431 let (label, source_file) = err_ctx.label_and_source_file();
432 Self::MalformedSignatureKey { label, source_file, key_type }
433 }
434
435 pub fn merkle_path_verification_failed(
436 value: Word,
437 index: Felt,
438 root: Digest,
439 err_code: Felt,
440 err_msg: Option<Arc<str>>,
441 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
442 ) -> Self {
443 let (label, source_file) = err_ctx.label_and_source_file();
444
445 Self::MerklePathVerificationFailed {
446 label,
447 source_file,
448 value,
449 index,
450 root,
451 err_code,
452 err_msg,
453 }
454 }
455
456 pub fn merkle_store_lookup_failed(
457 err: MerkleError,
458 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
459 ) -> Self {
460 let (label, source_file) = err_ctx.label_and_source_file();
461 Self::MerkleStoreLookupFailed { label, source_file, err }
462 }
463
464 pub fn merkle_store_merge_failed(
466 err: MerkleError,
467 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
468 ) -> Self {
469 let (label, source_file) = err_ctx.label_and_source_file();
470 Self::MerkleStoreMergeFailed { label, source_file, err }
471 }
472
473 pub fn merkle_store_update_failed(
474 err: MerkleError,
475 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
476 ) -> Self {
477 let (label, source_file) = err_ctx.label_and_source_file();
478 Self::MerkleStoreUpdateFailed { label, source_file, err }
479 }
480
481 pub fn no_mast_forest_with_procedure(
482 root_digest: Digest,
483 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
484 ) -> Self {
485 let (label, source_file) = err_ctx.label_and_source_file();
486 Self::NoMastForestWithProcedure { label, source_file, root_digest }
487 }
488
489 pub fn not_binary_value_if(value: Felt, err_ctx: &ErrorContext<'_, impl MastNodeExt>) -> Self {
490 let (label, source_file) = err_ctx.label_and_source_file();
491 Self::NotBinaryValueIf { label, source_file, value }
492 }
493
494 pub fn not_binary_value_op(value: Felt, err_ctx: &ErrorContext<'_, impl MastNodeExt>) -> Self {
495 let (label, source_file) = err_ctx.label_and_source_file();
496 Self::NotBinaryValueOp { label, source_file, value }
497 }
498
499 pub fn not_binary_value_loop(
500 value: Felt,
501 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
502 ) -> Self {
503 let (label, source_file) = err_ctx.label_and_source_file();
504 Self::NotBinaryValueLoop { label, source_file, value }
505 }
506
507 pub fn not_u32_value(
508 value: Felt,
509 err_code: Felt,
510 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
511 ) -> Self {
512 let (label, source_file) = err_ctx.label_and_source_file();
513 Self::NotU32Value { label, source_file, value, err_code }
514 }
515
516 pub fn smt_node_not_found(node: Word, err_ctx: &ErrorContext<'_, impl MastNodeExt>) -> Self {
517 let (label, source_file) = err_ctx.label_and_source_file();
518 Self::SmtNodeNotFound { label, source_file, node }
519 }
520
521 pub fn smt_node_preimage_not_valid(
522 node: Word,
523 preimage_len: usize,
524 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
525 ) -> Self {
526 let (label, source_file) = err_ctx.label_and_source_file();
527 Self::SmtNodePreImageNotValid { label, source_file, node, preimage_len }
528 }
529
530 pub fn syscall_target_not_in_kernel(
531 proc_root: Digest,
532 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
533 ) -> Self {
534 let (label, source_file) = err_ctx.label_and_source_file();
535 Self::SyscallTargetNotInKernel { label, source_file, proc_root }
536 }
537
538 pub fn failed_arithmetic_evaluation(
539 err_ctx: &ErrorContext<'_, impl MastNodeExt>,
540 error: AceError,
541 ) -> Self {
542 let (label, source_file) = err_ctx.label_and_source_file();
543 Self::AceChipError { label, source_file, error }
544 }
545}
546
547impl AsRef<dyn Diagnostic> for ExecutionError {
548 fn as_ref(&self) -> &(dyn Diagnostic + 'static) {
549 self
550 }
551}
552
553#[derive(Debug, thiserror::Error)]
557pub enum AceError {
558 #[error("num of variables should be word aligned and non-zero but was {0}")]
559 NumVarIsNotWordAlignedOrIsEmpty(u64),
560 #[error("num of evaluation gates should be word aligned and non-zero but was {0}")]
561 NumEvalIsNotWordAlignedOrIsEmpty(u64),
562 #[error("circuit does not evaluate to zero")]
563 CircuitNotEvaluateZero,
564 #[error("failed to read from memory")]
565 FailedMemoryRead,
566 #[error("failed to decode instruction")]
567 FailedDecodeInstruction,
568 #[error("failed to read from the wiring bus")]
569 FailedWireBusRead,
570}
571
572#[derive(Debug, thiserror::Error)]
576pub enum Ext2InttError {
577 #[error("input domain size must be a power of two, but was {0}")]
578 DomainSizeNotPowerOf2(u64),
579 #[error("input domain size ({0} elements) is too small")]
580 DomainSizeTooSmall(u64),
581 #[error("address of the last input must be smaller than 2^32, but was {0}")]
582 InputEndAddressTooBig(u64),
583 #[error("input size must be smaller than 2^32, but was {0}")]
584 InputSizeTooBig(u64),
585 #[error("address of the first input must be smaller than 2^32, but was {0}")]
586 InputStartAddressTooBig(u64),
587 #[error("address of the first input is not word aligned: {0}")]
588 InputStartNotWordAligned(u64),
589 #[error("output size ({0}) cannot be greater than the input size ({1})")]
590 OutputSizeTooBig(usize, usize),
591 #[error("output size must be greater than 0")]
592 OutputSizeIsZero,
593 #[error("uninitialized memory at address {0}")]
594 UninitializedMemoryAddress(u32),
595}
596
597#[derive(Debug)]
602pub struct ErrorContext<'a, N: MastNodeExt>(Option<ErrorContextImpl<'a, N>>);
603
604impl<'a, N: MastNodeExt> ErrorContext<'a, N> {
605 pub fn new(
609 mast_forest: &'a MastForest,
610 node: &'a N,
611 source_manager: Arc<dyn SourceManager>,
612 ) -> Self {
613 Self(Some(ErrorContextImpl::new(mast_forest, node, source_manager)))
614 }
615
616 pub fn new_with_op_idx(
620 mast_forest: &'a MastForest,
621 node: &'a N,
622 source_manager: Arc<dyn SourceManager>,
623 op_idx: usize,
624 ) -> Self {
625 Self(Some(ErrorContextImpl::new_with_op_idx(
626 mast_forest,
627 node,
628 source_manager,
629 op_idx,
630 )))
631 }
632
633 pub fn none() -> Self {
637 Self(None)
638 }
639
640 pub fn label_and_source_file(&self) -> (SourceSpan, Option<Arc<SourceFile>>) {
644 self.0
645 .as_ref()
646 .map_or((SourceSpan::UNKNOWN, None), |ctx| ctx.label_and_source_file())
647 }
648}
649
650impl Default for ErrorContext<'_, BasicBlockNode> {
651 fn default() -> Self {
652 Self::none()
653 }
654}
655
656#[derive(Debug)]
657struct ErrorContextImpl<'a, N: MastNodeExt> {
658 mast_forest: &'a MastForest,
659 node: &'a N,
660 source_manager: Arc<dyn SourceManager>,
661 op_idx: Option<usize>,
662}
663
664impl<'a, N: MastNodeExt> ErrorContextImpl<'a, N> {
665 pub fn new(
666 mast_forest: &'a MastForest,
667 node: &'a N,
668 source_manager: Arc<dyn SourceManager>,
669 ) -> Self {
670 Self {
671 mast_forest,
672 node,
673 source_manager,
674 op_idx: None,
675 }
676 }
677
678 pub fn new_with_op_idx(
679 mast_forest: &'a MastForest,
680 node: &'a N,
681 source_manager: Arc<dyn SourceManager>,
682 op_idx: usize,
683 ) -> Self {
684 Self {
685 mast_forest,
686 node,
687 source_manager,
688 op_idx: Some(op_idx),
689 }
690 }
691
692 pub fn label_and_source_file(&self) -> (SourceSpan, Option<Arc<SourceFile>>) {
693 self.node
694 .get_assembly_op(self.mast_forest, self.op_idx)
695 .and_then(|assembly_op| assembly_op.location())
696 .map_or_else(
697 || (SourceSpan::UNKNOWN, None),
698 |location| {
699 (
700 self.source_manager.location_to_span(location.clone()).unwrap_or_default(),
701 self.source_manager.get_by_path(&location.path),
702 )
703 },
704 )
705 }
706}
707
708#[cfg(test)]
712mod error_assertions {
713 use super::*;
714
715 fn _assert_error_is_send_sync_static<E: core::error::Error + Send + Sync + 'static>(_: E) {}
717
718 fn _assert_execution_error_bounds(err: ExecutionError) {
719 _assert_error_is_send_sync_static(err);
720 }
721}