Skip to content

Commit e2266e9

Browse files
committed
Add AvlTree.insertOrUpdate method (6.0)
1 parent fb3a19e commit e2266e9

File tree

3 files changed

+161
-0
lines changed

3 files changed

+161
-0
lines changed

ergotree-interpreter/src/eval.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
232232
savltree::CONTAINS_METHOD_ID => self::savltree::CONTAINS_EVAL_FN,
233233
savltree::REMOVE_METHOD_ID => self::savltree::REMOVE_EVAL_FN,
234234
savltree::UPDATE_METHOD_ID => self::savltree::UPDATE_EVAL_FN,
235+
savltree::INSERT_OR_UPDATE_METHOD_ID => self::savltree::INSERT_OR_UPDATE_EVAL_FN,
235236
method_id => {
236237
return Err(EvalError::NotFound(format!(
237238
"Eval fn: unknown method id in SAvlTree: {:?}",

ergotree-interpreter/src/eval/savltree.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,65 @@ pub(crate) static UPDATE_EVAL_FN: EvalFn =
428428
})
429429
};
430430

431+
pub(crate) static INSERT_OR_UPDATE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
432+
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
433+
434+
if !avl_tree_data.tree_flags.insert_allowed() || !avl_tree_data.tree_flags.update_allowed() {
435+
return Ok(Value::Opt(None));
436+
}
437+
438+
let entries = {
439+
let v = args
440+
.first()
441+
.cloned()
442+
.ok_or_else(|| EvalError::AvlTree("eval is missing first arg (entries)".to_string()))?;
443+
v.try_extract_into::<Vec<(Vec<u8>, Vec<u8>)>>()?
444+
};
445+
446+
let proof = {
447+
let v = args
448+
.get(1)
449+
.cloned()
450+
.ok_or_else(|| EvalError::AvlTree("eval is missing second arg (proof)".to_string()))?;
451+
Bytes::from(v.try_extract_into::<Vec<u8>>()?)
452+
};
453+
454+
let starting_digest = Bytes::from(avl_tree_data.digest.0.to_vec());
455+
let mut bv = BatchAVLVerifier::new(
456+
&starting_digest,
457+
&proof,
458+
AVLTree::new(
459+
|digest| Node::LabelOnly(NodeHeader::new(Some(*digest), None)),
460+
avl_tree_data.key_length as usize,
461+
avl_tree_data
462+
.value_length_opt
463+
.as_ref()
464+
.map(|v| **v as usize),
465+
),
466+
None,
467+
None,
468+
)
469+
.map_err(map_eval_err)?;
470+
for (key, value) in entries {
471+
if bv
472+
.perform_one_operation(&Operation::InsertOrUpdate(KeyValue {
473+
key: key.into(),
474+
value: value.into(),
475+
}))
476+
.is_err()
477+
{
478+
break;
479+
}
480+
}
481+
Ok(if let Some(new_digest) = bv.digest() {
482+
let digest = ADDigest::scorex_parse_bytes(&new_digest)?;
483+
avl_tree_data.digest = digest;
484+
Value::Opt(Some(Box::new(Value::AvlTree(avl_tree_data.into()))))
485+
} else {
486+
Value::Opt(None)
487+
})
488+
};
489+
431490
fn map_eval_err<T: core::fmt::Debug>(e: T) -> EvalError {
432491
EvalError::AvlTree(format!("{:?}", e))
433492
}
@@ -708,6 +767,75 @@ mod tests {
708767
Value::Opt(None)
709768
);
710769
}
770+
771+
#[test]
772+
fn eval_avl_insert_or_update() {
773+
let mut prover = BatchAVLProver::new(
774+
AVLTree::new(
775+
|digest| Node::LabelOnly(NodeHeader::new(Some(*digest), None)),
776+
1,
777+
None,
778+
),
779+
true,
780+
);
781+
let initial_digest = ADDigest::scorex_parse_bytes(&prover.digest().unwrap()).unwrap();
782+
let key1 = Bytes::from(vec![1u8]);
783+
let op = Operation::InsertOrUpdate(KeyValue {
784+
key: key1,
785+
value: Bytes::from(10u64.to_be_bytes().to_vec()),
786+
});
787+
prover.perform_one_operation(&op).unwrap();
788+
prover.perform_one_operation(&op).unwrap();
789+
let final_digest = ADDigest::scorex_parse_bytes(&prover.digest().unwrap()).unwrap();
790+
let proof: Constant = prover
791+
.generate_proof()
792+
.into_iter()
793+
.collect::<Vec<_>>()
794+
.into();
795+
796+
let tree_flags = AvlTreeFlags::new(true, true, false);
797+
let obj = Expr::Const(
798+
AvlTreeData {
799+
digest: initial_digest,
800+
tree_flags,
801+
key_length: 1,
802+
value_length_opt: None,
803+
}
804+
.into(),
805+
);
806+
let pair1 = Literal::Tup(mk_pair(1u8, 10u64).into());
807+
let entries = Constant {
808+
tpe: SType::SColl(Arc::new(SType::STuple(STuple::pair(
809+
SType::SColl(Arc::new(SType::SByte)),
810+
SType::SColl(Arc::new(SType::SByte)),
811+
)))),
812+
v: Literal::Coll(CollKind::WrappedColl {
813+
items: Arc::new([pair1.clone(), pair1.clone()]),
814+
elem_tpe: SType::STuple(STuple::pair(
815+
SType::SColl(Arc::new(SType::SByte)),
816+
SType::SColl(Arc::new(SType::SByte)),
817+
)),
818+
}),
819+
};
820+
let expr: Expr = MethodCall::new(
821+
obj.clone(),
822+
savltree::INSERT_OR_UPDATE_METHOD.clone(),
823+
vec![entries.clone().into(), proof.clone().into()],
824+
)
825+
.unwrap()
826+
.into();
827+
828+
let res = eval_out_wo_ctx::<Value>(&expr);
829+
if let Value::Opt(opt) = res {
830+
if let Some(Value::AvlTree(avl)) = opt.as_deref() {
831+
assert_eq!(avl.digest, final_digest);
832+
} else {
833+
unreachable!();
834+
}
835+
} else {
836+
unreachable!();
837+
}
838+
}
711839
proptest! {
712840
#[test]
713841
fn eval_avl_digest(v in any::<AvlTreeData>()) {

ergotree-ir/src/types/savltree.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub const REMOVE_METHOD_ID: MethodId = MethodId(14);
4848
pub const UPDATE_METHOD_ID: MethodId = MethodId(13);
4949
/// AvlTree.updateDigest property
5050
pub const UPDATE_DIGEST_METHOD_ID: MethodId = MethodId(15);
51+
/// AvlTree.insertOrUpdate property
52+
pub const INSERT_OR_UPDATE_METHOD_ID: MethodId = MethodId(16);
5153

5254
lazy_static! {
5355
/// AvlTree method descriptors
@@ -68,6 +70,7 @@ lazy_static! {
6870
REMOVE_METHOD_DESC.clone(),
6971
UPDATE_METHOD_DESC.clone(),
7072
UPDATE_DIGEST_METHOD_DESC.clone(),
73+
INSERT_OR_UPDATE_METHOD_DESC.clone(),
7174
]
7275
;
7376
}
@@ -370,6 +373,35 @@ lazy_static! {
370373
SMethod::new(STypeCompanion::AvlTree, UPDATE_DIGEST_METHOD_DESC.clone(),);
371374
}
372375

376+
lazy_static! {
377+
static ref INSERT_OR_UPDATE_METHOD_DESC: SMethodDesc = SMethodDesc {
378+
method_id: INSERT_OR_UPDATE_METHOD_ID,
379+
name: "insertOrUpdate",
380+
tpe: SFunc {
381+
t_dom: vec![ SType::SAvlTree,
382+
SType::SColl(
383+
Arc::new(
384+
SType::STuple(
385+
STuple::pair(
386+
SType::SColl(Arc::new(SType::SByte)),
387+
SType::SColl(Arc::new(SType::SByte))
388+
)
389+
)
390+
)
391+
),
392+
SType::SColl(Arc::new(SType::SByte)),
393+
],
394+
t_range: SType::SOption(Arc::new(SType::SAvlTree)).into(),
395+
tpe_params: vec![],
396+
},
397+
explicit_type_args: vec![],
398+
min_version: ErgoTreeVersion::V3
399+
};
400+
/// AvlTree.insertOrUpdate
401+
pub static ref INSERT_OR_UPDATE_METHOD: SMethod =
402+
SMethod::new(STypeCompanion::AvlTree, INSERT_OR_UPDATE_METHOD_DESC.clone(),);
403+
}
404+
373405
#[cfg(test)]
374406
mod tests {
375407
use super::*;

0 commit comments

Comments
 (0)