Repository: ergoplatform/sigma-rust (language: rust)

Issue:
[300 SigmaUSD] AvlTree properties
Implement `SMethod`(in `types::savltree`) and evaluation (in `eval::savltree`) of the following properties and methods:
- digest
- enabledOperations
- keyLength
- valueLengthOpt
- isInsertAllowed
- isUpdateAllowed
- isRemoveAllowed
- updateOperations
- updateDigest

Reference Scala implementation - https://github.com/ScorexFoundation/sigmastate-interpreter/blob/c863a9b1a82589e47b15f76f3affdb30a475e740/sigmastate/src/main/scala/sigmastate/types.scala#L2018-L2101
and
https://github.com/ScorexFoundation/sigmastate-interpreter/blob/c863a9b1a82589e47b15f76f3affdb30a475e740/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala#L128-L150


Discussion / hints:
Starting this one.

Fix patch:
diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs
index f4bc6639f..6405b668b 100644
--- a/ergotree-interpreter/src/eval.rs
+++ b/ergotree-interpreter/src/eval.rs
@@ -198,6 +198,15 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
     use ergotree_ir::types::*;
     Ok(match method.obj_type.type_code() {
         savltree::TYPE_CODE => match method.method_id() {
+            savltree::DIGEST_METHOD_ID => self::savltree::DIGEST_EVAL_FN,
+            savltree::UPDATE_DIGEST_METHOD_ID => self::savltree::UPDATE_DIGEST_EVAL_FN,
+            savltree::ENABLED_OPERATIONS_METHOD_ID => self::savltree::ENABLED_OPERATIONS_EVAL_FN,
+            savltree::KEY_LENGTH_METHOD_ID => self::savltree::KEY_LENGTH_EVAL_FN,
+            savltree::VALUE_LENGTH_OPT_METHOD_ID => self::savltree::VALUE_LENGTH_OPT_EVAL_FN,
+            savltree::IS_INSERT_ALLOWED_METHOD_ID => self::savltree::IS_INSERT_ALLOWED_EVAL_FN,
+            savltree::IS_UPDATE_ALLOWED_METHOD_ID => self::savltree::IS_UPDATE_ALLOWED_EVAL_FN,
+            savltree::IS_REMOVE_ALLOWED_METHOD_ID => self::savltree::IS_REMOVE_ALLOWED_EVAL_FN,
+            savltree::UPDATE_OPERATIONS_METHOD_ID => self::savltree::UPDATE_OPERATIONS_EVAL_FN,
             savltree::INSERT_METHOD_ID => self::savltree::INSERT_EVAL_FN,
             method_id => {
                 return Err(EvalError::NotFound(format!(
diff --git a/ergotree-interpreter/src/eval/savltree.rs b/ergotree-interpreter/src/eval/savltree.rs
index 88660a373..1d7518c91 100644
--- a/ergotree-interpreter/src/eval/savltree.rs
+++ b/ergotree-interpreter/src/eval/savltree.rs
@@ -1,7 +1,12 @@
+use std::convert::TryFrom;
+
 use bytes::Bytes;
 use ergotree_ir::chain::digest32::ADDigest;
 use ergotree_ir::mir::avl_tree_data::AvlTreeData;
+use ergotree_ir::mir::avl_tree_data::AvlTreeFlags;
 use ergotree_ir::mir::constant::TryExtractInto;
+use ergotree_ir::mir::value::CollKind;
+use ergotree_ir::mir::value::NativeColl;
 use ergotree_ir::mir::value::Value;
 use ergotree_ir::serialization::SigmaSerializable;
 use scorex_crypto_avltree::authenticated_tree_ops::AuthenticatedTreeOps;
@@ -15,6 +20,72 @@ use scorex_crypto_avltree::operation::Operation;
 use super::EvalError;
 use super::EvalFn;
 
+pub(crate) static DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Coll(CollKind::NativeColl(NativeColl::CollByte(
+        avl_tree_data.digest.into(),
+    ))))
+};
+
+pub(crate) static ENABLED_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Byte(avl_tree_data.tree_flags.serialize() as i8))
+};
+
+pub(crate) static KEY_LENGTH_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Int(avl_tree_data.key_length as i32))
+};
+
+pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Opt(Box::new(
+        avl_tree_data
+            .value_length_opt
+            .map(|v| Value::Int(*v as i32)),
+    )))
+};
+
+pub(crate) static IS_INSERT_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Boolean(avl_tree_data.tree_flags.insert_allowed()))
+};
+
+pub(crate) static IS_UPDATE_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Boolean(avl_tree_data.tree_flags.update_allowed()))
+};
+
+pub(crate) static IS_REMOVE_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
+    let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    Ok(Value::Boolean(avl_tree_data.tree_flags.remove_allowed()))
+};
+
+pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
+    let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    let new_operations = {
+        let v = args.get(0).cloned().ok_or_else(|| {
+            EvalError::AvlTree("eval is missing first arg (new_operations)".to_string())
+        })?;
+        v.try_extract_into::<i8>()? as u8
+    };
+    avl_tree_data.tree_flags = AvlTreeFlags::parse(new_operations);
+    Ok(Value::AvlTree(Box::new(avl_tree_data)))
+};
+
+pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
+    let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
+    let new_digest = {
+        let v = args.get(0).cloned().ok_or_else(|| {
+            EvalError::AvlTree("eval is missing first arg (new_digest)".to_string())
+        })?;
+        let bytes_vec = v.try_extract_into::<Vec<u8>>()?;
+        ADDigest::try_from(bytes_vec).map_err(map_eval_err)?
+    };
+    avl_tree_data.digest = new_digest;
+    Ok(Value::AvlTree(Box::new(avl_tree_data)))
+};
+
 pub(crate) static INSERT_EVAL_FN: EvalFn =
     |_env, _ctx, obj, args| {
         let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
@@ -82,7 +153,7 @@ fn map_eval_err<T: std::fmt::Debug>(e: T) -> EvalError {
     EvalError::AvlTree(format!("{:?}", e))
 }
 
-#[allow(clippy::unwrap_used)]
+#[allow(clippy::unwrap_used, clippy::panic)]
 #[cfg(test)]
 #[cfg(feature = "arbitrary")]
 mod tests {
@@ -98,13 +169,14 @@ mod tests {
         },
         types::{savltree, stuple::STuple, stype::SType},
     };
+    use proptest::prelude::*;
     use scorex_crypto_avltree::batch_avl_prover::BatchAVLProver;
 
     use crate::eval::tests::eval_out_wo_ctx;
 
     use super::*;
     #[test]
-    fn eval_avl_tree() {
+    fn eval_avl_insert() {
         // This example taken from `scorex_crypto_avltree` README
         let mut prover = BatchAVLProver::new(
             AVLTree::new(
@@ -189,6 +261,188 @@ mod tests {
             unreachable!();
         }
     }
+    proptest! {
+        #[test]
+        fn eval_avl_digest(v in any::<AvlTreeData>()) {
+            let digest: Vec<i8> = v.digest.clone().into();
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::DIGEST_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Coll(CollKind::NativeColl(NativeColl::CollByte(b))) = res {
+                assert_eq!(b, digest);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_enabled_operations(v in any::<AvlTreeData>()) {
+            let enabled_ops = v.tree_flags.serialize() as i8;
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::ENABLED_OPERATIONS_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Byte(b) = res {
+                assert_eq!(b, enabled_ops);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_key_length(v in any::<AvlTreeData>()) {
+            let key_length = v.key_length as i32;
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::KEY_LENGTH_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Int(i) = res {
+                assert_eq!(key_length, i);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_value_length_opt(v in any::<AvlTreeData>()) {
+            let value_length_opt = v.value_length_opt.clone().map(|v| Value::Int(*v as i32));
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::VALUE_LENGTH_OPT_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Opt(opt) = res {
+                assert_eq!(*opt, value_length_opt);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_insert_allowed(v in any::<AvlTreeData>()) {
+            let insert_allowed = v.tree_flags.insert_allowed();
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::IS_INSERT_ALLOWED_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Boolean(i) = res {
+                assert_eq!(insert_allowed, i);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_update_allowed(v in any::<AvlTreeData>()) {
+            let update_allowed = v.tree_flags.update_allowed();
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::IS_UPDATE_ALLOWED_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Boolean(i) = res {
+                assert_eq!(update_allowed, i);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_remove_allowed(v in any::<AvlTreeData>()) {
+            let remove_allowed = v.tree_flags.remove_allowed();
+            let obj = Expr::Const(v.into());
+
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::IS_REMOVE_ALLOWED_METHOD.clone(),
+                vec![],
+            )
+            .unwrap()
+            .into();
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::Boolean(i) = res {
+                assert_eq!(remove_allowed, i);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_update_operations(v in any::<AvlTreeData>(), new_ops in any::<AvlTreeFlags>()) {
+            // Test updateOperations method
+            let obj = Expr::Const(v.into());
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::UPDATE_OPERATIONS_METHOD.clone(),
+                vec![Constant::from(new_ops.serialize() as i8).into()],
+            )
+            .unwrap()
+            .into();
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::AvlTree(a) = res {
+                assert_eq!(a.tree_flags, new_ops);
+            } else {
+                unreachable!();
+            }
+        }
+
+        #[test]
+        fn eval_avl_update_digest(v in any::<AvlTreeData>(), new_digest in any::<ADDigest>()) {
+            let obj = Expr::Const(v.into());
+            let expr: Expr = MethodCall::new(
+                obj,
+                savltree::UPDATE_DIGEST_METHOD.clone(),
+                vec![Constant::from(new_digest.sigma_serialize_bytes()?).into()],
+            )
+            .unwrap()
+            .into();
+            let res = eval_out_wo_ctx::<Value>(&expr);
+            if let Value::AvlTree(a) = res {
+                assert_eq!(a.digest, new_digest);
+            } else {
+                unreachable!();
+            }
+        }
+    }
 
     fn mk_pair(x: u8, y: u64) -> [Literal; 2] {
         [
diff --git a/ergotree-ir/src/chain/digest32.rs b/ergotree-ir/src/chain/digest32.rs
index d0e8205c7..0a02a2706 100644
--- a/ergotree-ir/src/chain/digest32.rs
+++ b/ergotree-ir/src/chain/digest32.rs
@@ -90,6 +90,15 @@ impl<const N: usize> TryFrom<String> for Digest<N> {
     }
 }
 
+impl<const N: usize> TryFrom<Vec<u8>> for Digest<N> {
+    type Error = Digest32Error;
+
+    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+        let bytes: [u8; N] = value.as_slice().try_into()?;
+        Ok(Digest::from(bytes))
+    }
+}
+
 impl<const N: usize> SigmaSerializable for Digest<N> {
     fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
         w.write_all(self.0.as_ref())?;
diff --git a/ergotree-ir/src/mir/avl_tree_data.rs b/ergotree-ir/src/mir/avl_tree_data.rs
index 3158d94fe..411066ac7 100644
--- a/ergotree-ir/src/mir/avl_tree_data.rs
+++ b/ergotree-ir/src/mir/avl_tree_data.rs
@@ -21,8 +21,13 @@ impl AvlTreeFlags {
         AvlTreeFlags(if remove_allowed { u | 0x04 } else { u })
     }
 
+    /// Get byte-representation of the tree-flags
+    pub fn serialize(&self) -> u8 {
+        self.0
+    }
+
     /// Parse tree-flags from byte
-    fn parse(serialized_flags: u8) -> Self {
+    pub fn parse(serialized_flags: u8) -> Self {
         let insert_allowed = serialized_flags & 0x01 != 0;
         let update_allowed = serialized_flags & 0x02 != 0;
         let remove_allowed = serialized_flags & 0x04 != 0;
@@ -99,27 +104,14 @@ mod arbitrary {
         fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
             (
                 any::<ADDigest>(),
-                any::<bool>(),
-                any::<bool>(),
-                any::<bool>(),
+                any::<AvlTreeFlags>(),
                 any::<u32>(),
                 any::<OptBox>(),
             )
                 .prop_map(
-                    |(
-                        digest,
-                        insert_allowed,
-                        update_allowed,
-                        remove_allowed,
-                        key_length,
-                        value_length_opt,
-                    )| AvlTreeData {
+                    |(digest, tree_flags, key_length, value_length_opt)| AvlTreeData {
                         digest,
-                        tree_flags: AvlTreeFlags::new(
-                            insert_allowed,
-                            update_allowed,
-                            remove_allowed,
-                        ),
+                        tree_flags,
                         key_length,
                         value_length_opt,
                     },
@@ -127,6 +119,18 @@ mod arbitrary {
                 .boxed()
         }
     }
+    impl Arbitrary for AvlTreeFlags {
+        type Strategy = BoxedStrategy<Self>;
+        type Parameters = ();
+
+        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+            (any::<bool>(), any::<bool>(), any::<bool>())
+                .prop_map(|(insert_allowed, update_allowed, remove_allowed)| {
+                    AvlTreeFlags::new(insert_allowed, update_allowed, remove_allowed)
+                })
+                .boxed()
+        }
+    }
 }
 
 #[cfg(test)]
diff --git a/ergotree-ir/src/types/savltree.rs b/ergotree-ir/src/types/savltree.rs
index 54f64cccb..56cafdfbb 100644
--- a/ergotree-ir/src/types/savltree.rs
+++ b/ergotree-ir/src/types/savltree.rs
@@ -13,18 +13,180 @@ use lazy_static::lazy_static;
 pub const TYPE_CODE: TypeCode = TypeCode::SAVL_TREE;
 /// SAvlTree type name
 pub static TYPE_NAME: &str = "AvlTree";
+/// AvlTree.digest property
+pub const DIGEST_METHOD_ID: MethodId = MethodId(1);
+/// AvlTree.enabledOperations property
+pub const ENABLED_OPERATIONS_METHOD_ID: MethodId = MethodId(2);
+/// AvlTree.keyLength property
+pub const KEY_LENGTH_METHOD_ID: MethodId = MethodId(3);
+/// AvlTree.valueLengthOpt property
+pub const VALUE_LENGTH_OPT_METHOD_ID: MethodId = MethodId(4);
+/// AvlTree.isInsertAllowed property
+pub const IS_INSERT_ALLOWED_METHOD_ID: MethodId = MethodId(5);
+/// AvlTree.isUpdateAllowed property
+pub const IS_UPDATE_ALLOWED_METHOD_ID: MethodId = MethodId(6);
+/// AvlTree.isRemoveAllowed property
+pub const IS_REMOVE_ALLOWED_METHOD_ID: MethodId = MethodId(7);
+/// AvlTree.updateOperations property
+pub const UPDATE_OPERATIONS_METHOD_ID: MethodId = MethodId(8);
 /// AvlTree.insert property
 pub const INSERT_METHOD_ID: MethodId = MethodId(12);
+/// AvlTree.updateDigest property
+pub const UPDATE_DIGEST_METHOD_ID: MethodId = MethodId(15);
 
 lazy_static! {
     /// AvlTree method descriptors
     pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> =
         vec![
+            &DIGEST_METHOD_DESC,
             &INSERT_METHOD_DESC,
+            &ENABLED_OPERATIONS_METHOD_DESC,
+            &KEY_LENGTH_METHOD_DESC,
+            &VALUE_LENGTH_OPT_METHOD_DESC,
+            &IS_INSERT_ALLOWED_METHOD_DESC,
+            &IS_UPDATE_ALLOWED_METHOD_DESC,
+            &IS_REMOVE_ALLOWED_METHOD_DESC,
+            &UPDATE_OPERATIONS_METHOD_DESC,
+            &UPDATE_DIGEST_METHOD_DESC,
         ]
     ;
 }
 
+lazy_static! {
+    static ref DIGEST_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: DIGEST_METHOD_ID,
+        name: "digest",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SColl(Box::new(SType::SByte)).into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.digest
+    pub static ref DIGEST_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, DIGEST_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref UPDATE_DIGEST_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: UPDATE_DIGEST_METHOD_ID,
+        name: "updateDigest",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree, SType::SColl(Box::new(SType::SByte))],
+            t_range: SType::SAvlTree.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.updateDigest
+    pub static ref UPDATE_DIGEST_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, UPDATE_DIGEST_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref ENABLED_OPERATIONS_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: ENABLED_OPERATIONS_METHOD_ID,
+        name: "enabledOperations",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SByte.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.enabledOperations
+    pub static ref ENABLED_OPERATIONS_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, ENABLED_OPERATIONS_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref KEY_LENGTH_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: KEY_LENGTH_METHOD_ID,
+        name: "keyLength",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SInt.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.keyLength
+    pub static ref KEY_LENGTH_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, KEY_LENGTH_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref VALUE_LENGTH_OPT_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: VALUE_LENGTH_OPT_METHOD_ID,
+        name: "valueLengthOpt",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SOption(Box::new(SType::SInt)).into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.valueLengthOpt
+    pub static ref VALUE_LENGTH_OPT_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, VALUE_LENGTH_OPT_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref IS_INSERT_ALLOWED_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: IS_INSERT_ALLOWED_METHOD_ID,
+        name: "isInsertAllowed",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SBoolean.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.isInsertAllowed
+    pub static ref IS_INSERT_ALLOWED_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, IS_INSERT_ALLOWED_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref IS_UPDATE_ALLOWED_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: IS_UPDATE_ALLOWED_METHOD_ID,
+        name: "isUpdateAllowed",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SBoolean.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.isUpdateAllowed
+    pub static ref IS_UPDATE_ALLOWED_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, IS_UPDATE_ALLOWED_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref IS_REMOVE_ALLOWED_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: IS_REMOVE_ALLOWED_METHOD_ID,
+        name: "isRemoveAllowed",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree],
+            t_range: SType::SBoolean.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.isRemoveAllowed
+    pub static ref IS_REMOVE_ALLOWED_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, IS_REMOVE_ALLOWED_METHOD_DESC.clone(),);
+}
+
+lazy_static! {
+    static ref UPDATE_OPERATIONS_METHOD_DESC: SMethodDesc = SMethodDesc {
+        method_id: UPDATE_OPERATIONS_METHOD_ID,
+        name: "updateOperations",
+        tpe: SFunc {
+            t_dom: vec![ SType::SAvlTree, SType::SByte],
+            t_range: SType::SAvlTree.into(),
+            tpe_params: vec![],
+        },
+    };
+    /// AvlTree.updateOperations
+    pub static ref UPDATE_OPERATIONS_METHOD: SMethod =
+        SMethod::new(STypeCompanion::AvlTree, UPDATE_OPERATIONS_METHOD_DESC.clone(),);
+}
+
 lazy_static! {
     static ref INSERT_METHOD_DESC: SMethodDesc = SMethodDesc {
         method_id: INSERT_METHOD_ID,


Test patch: