Repository: hyperledger/indy-vdr (language: python)

Issue:
Add support for config flag transactions
Implements new transaction added in https://github.com/hyperledger/indy-node/pull/1783 to indy-node 1.13.



Discussion / hints:
This needs a test setup with indy-node 1.13 for functioning tests -> waiting for #112

Fix patch:
diff --git a/libindy_vdr/src/ledger/constants.rs b/libindy_vdr/src/ledger/constants.rs
index 3f97da05..2ce03859 100644
--- a/libindy_vdr/src/ledger/constants.rs
+++ b/libindy_vdr/src/ledger/constants.rs
@@ -27,6 +27,8 @@ pub const GET_VALIDATOR_INFO: &str = "119";
 pub const AUTH_RULE: &str = "120";
 pub const GET_AUTH_RULE: &str = "121";
 pub const AUTH_RULES: &str = "122";
+pub const FLAG: &str = "130";
+pub const GET_FLAG: &str = "131";
 // RichSchema objects
 pub const RICH_SCHEMA_CTX: &str = "200";
 pub const RICH_SCHEMA: &str = "201";
@@ -38,7 +40,7 @@ pub const RICH_SCHEMA_PRES_DEF: &str = "205";
 pub const GET_RICH_SCHEMA_BY_ID: &str = "300";
 pub const GET_RICH_SCHEMA_BY_METADATA: &str = "301";
 
-pub const REQUESTS: [&str; 33] = [
+pub const REQUESTS: [&str; 35] = [
     NODE,
     NYM,
     GET_TXN,
@@ -66,6 +68,8 @@ pub const REQUESTS: [&str; 33] = [
     DISABLE_ALL_TXN_AUTHR_AGRMTS,
     LEDGERS_FREEZE,
     GET_FROZEN_LEDGERS,
+    FLAG,
+    GET_FLAG,
     RICH_SCHEMA_CTX,
     RICH_SCHEMA,
     RICH_SCHEMA_ENCODING,
@@ -75,7 +79,7 @@ pub const REQUESTS: [&str; 33] = [
 ];
 
 // likely matches REQUESTS_FOR_STATE_PROOFS
-pub const READ_REQUESTS: [&str; 13] = [
+pub const READ_REQUESTS: [&str; 14] = [
     GET_NYM,
     GET_TXN_AUTHR_AGRMT,
     GET_TXN_AUTHR_AGRMT_AML,
@@ -87,6 +91,7 @@ pub const READ_REQUESTS: [&str; 13] = [
     GET_REVOC_REG_DELTA,
     GET_AUTH_RULE,
     GET_TXN,
+    GET_FLAG,
     GET_RICH_SCHEMA_BY_ID,
     GET_RICH_SCHEMA_BY_METADATA,
 ];
@@ -145,6 +150,8 @@ pub fn txn_name_to_code(txn: &str) -> Option<&str> {
         "DISABLE_ALL_TXN_AUTHR_AGRMTS" => Some(DISABLE_ALL_TXN_AUTHR_AGRMTS),
         "LEDGERS_FREEZE" => Some(LEDGERS_FREEZE),
         "GET_FROZEN_LEDGERS" => Some(GET_FROZEN_LEDGERS),
+        "FLAG" => Some(FLAG),
+        "GET_FLAG" => Some(GET_FLAG),
         val => Some(val),
     }
 }
diff --git a/libindy_vdr/src/ledger/request_builder.rs b/libindy_vdr/src/ledger/request_builder.rs
index bd4e2a22..e222cb8c 100644
--- a/libindy_vdr/src/ledger/request_builder.rs
+++ b/libindy_vdr/src/ledger/request_builder.rs
@@ -20,6 +20,7 @@ use super::requests::author_agreement::{
     TxnAuthorAgreementOperation, TxnAuthrAgrmtAcceptanceData,
 };
 use super::requests::cred_def::{CredDefOperation, CredentialDefinition, GetCredDefOperation};
+use super::requests::flag::{FlagOperation, GetFlagOperation};
 use super::requests::ledgers_freeze::{GetFrozenLedgersOperation, LedgersFreezeOperation};
 use super::requests::node::{NodeOperation, NodeOperationData};
 use super::requests::nym::{role_to_code, GetNymOperation, NymOperation};
@@ -122,6 +123,30 @@ impl RequestBuilder {
         ))
     }
 
+    /// Build a `FLAG` transaction request
+    pub fn build_flag_request(
+        &self,
+        identifier: &DidValue,
+        name: String,
+        value: String,
+    ) -> VdrResult<PreparedRequest> {
+        let operation = FlagOperation::new(name, value);
+        self.build(operation, Some(identifier))
+    }
+
+    /// Build a `GET_FLAG` transaction request
+    /// Use only one of seq_no and timestamp
+    pub fn build_get_flag_request(
+        &self,
+        identifier: Option<&DidValue>,
+        name: String,
+        seq_no: Option<i32>,
+        timestamp: Option<u64>,
+    ) -> VdrResult<PreparedRequest> {
+        let operation = GetFlagOperation::new(name, seq_no, timestamp);
+        self.build(operation, identifier)
+    }
+
     /// Build a `NYM` transaction request
     /// diddoc_content is only supported for did:indy compliant ledgers
     pub fn build_nym_request(
diff --git a/libindy_vdr/src/ledger/requests/flag.rs b/libindy_vdr/src/ledger/requests/flag.rs
new file mode 100644
index 00000000..fb39f56a
--- /dev/null
+++ b/libindy_vdr/src/ledger/requests/flag.rs
@@ -0,0 +1,69 @@
+use crate::common::error::VdrResult;
+use crate::ledger::requests::get_sp_key_marker;
+
+use super::constants::{FLAG, GET_FLAG};
+use super::{ProtocolVersion, RequestType};
+
+#[derive(Serialize, PartialEq, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct FlagOperation {
+    pub _type: String,
+    pub name: String,
+    pub value: String,
+}
+
+impl FlagOperation {
+    pub fn new(name: String, value: String) -> FlagOperation {
+        FlagOperation {
+            _type: Self::get_txn_type().to_string(),
+            name,
+            value,
+        }
+    }
+}
+
+impl RequestType for FlagOperation {
+    fn get_txn_type<'a>() -> &'a str {
+        FLAG
+    }
+}
+
+#[derive(Serialize, PartialEq, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct GetFlagOperation {
+    pub _type: String,
+    pub name: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub seq_no: Option<i32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub timestamp: Option<u64>,
+}
+
+impl GetFlagOperation {
+    pub fn new(name: String, seq_no: Option<i32>, timestamp: Option<u64>) -> GetFlagOperation {
+        GetFlagOperation {
+            _type: Self::get_txn_type().to_string(),
+            name,
+            seq_no,
+            timestamp,
+        }
+    }
+}
+
+impl RequestType for GetFlagOperation {
+    fn get_txn_type<'a>() -> &'a str {
+        GET_FLAG
+    }
+
+    fn get_sp_key(&self, _protocol_version: ProtocolVersion) -> VdrResult<Option<Vec<u8>>> {
+        if !self.name.is_empty() {
+            let marker = get_sp_key_marker(2, _protocol_version);
+            return Ok(Some(
+                format!("{}:{}", marker, self.name.to_string())
+                    .as_bytes()
+                    .to_vec(),
+            ));
+        }
+        Ok(None)
+    }
+}
diff --git a/libindy_vdr/src/ledger/requests/mod.rs b/libindy_vdr/src/ledger/requests/mod.rs
index d9ffe488..5ec5c24c 100644
--- a/libindy_vdr/src/ledger/requests/mod.rs
+++ b/libindy_vdr/src/ledger/requests/mod.rs
@@ -6,6 +6,8 @@ pub mod auth_rule;
 pub mod author_agreement;
 /// Credential definition operations
 pub mod cred_def;
+/// FLAG transactions operations
+pub mod flag;
 /// Frozen Ledger operations
 pub mod ledgers_freeze;
 /// NODE transactions operations


Test patch:
diff --git a/libindy_vdr/tests/flag.rs b/libindy_vdr/tests/flag.rs
new file mode 100644
index 00000000..329ac74b
--- /dev/null
+++ b/libindy_vdr/tests/flag.rs
@@ -0,0 +1,104 @@
+#[macro_use]
+mod utils;
+
+inject_dependencies!();
+
+use indy_vdr::ledger::constants;
+use indy_vdr::utils::did::DidValue;
+
+use crate::utils::fixtures::*;
+use crate::utils::helpers;
+
+const FLAG_NAME: &'static str = r#"REV_STRATEGY_USE_COMPAT_ORDERING"#;
+const FLAG_VALUE: &'static str = r#"True"#;
+
+#[test]
+fn empty() {
+    // Empty test to run module
+}
+
+#[cfg(test)]
+mod builder {
+    use super::*;
+    use indy_vdr::ledger::RequestBuilder;
+
+    mod flag {
+        use super::*;
+
+        #[rstest]
+        fn test_pool_build_flag(request_builder: RequestBuilder, trustee_did: DidValue) {
+            let flag_request = request_builder
+                .build_flag_request(&trustee_did, FLAG_NAME.to_string(), FLAG_VALUE.to_string())
+                .unwrap();
+
+            let expected_result = json!({
+                "type": constants::FLAG,
+                "name": FLAG_NAME,
+                "value": FLAG_VALUE,
+            });
+
+            helpers::check_request_operation(&flag_request, expected_result);
+        }
+    }
+
+    mod get_flag {
+        use super::*;
+
+        #[rstest]
+        fn test_pool_build_get_flag(request_builder: RequestBuilder) {
+            let attrib_request = request_builder
+                .build_get_flag_request(None, FLAG_NAME.to_string(), None, None)
+                .unwrap();
+
+            let expected_result = json!({
+                "type": constants::GET_FLAG,
+                "name": FLAG_NAME,
+            });
+
+            helpers::check_request_operation(&attrib_request, expected_result);
+        }
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "local_nodes_pool")]
+mod send_flag {
+    use super::*;
+    use crate::utils::crypto::Identity;
+    use crate::utils::pool::TestPool;
+
+    #[rstest]
+    fn test_pool_send_flag(pool: TestPool, trustee: Identity) {
+        // Create Flag Request
+        let mut flag_request = pool
+            .request_builder()
+            .build_flag_request(&trustee.did, FLAG_NAME.to_string(), FLAG_VALUE.to_string())
+            .unwrap();
+        // Send Flag Request
+        let flag_response =
+            helpers::sign_and_send_request(&trustee, &pool, &mut flag_request).unwrap();
+
+        // Create get_flag request -> This should return the FLAG_VALUE written previously
+        let get_flag_request = pool
+            .request_builder()
+            .build_get_flag_request(None, FLAG_NAME.to_string(), None, None)
+            .unwrap();
+        let response = pool
+            .send_request_with_retries(&get_flag_request, &flag_response)
+            .unwrap();
+        let data = helpers::get_response_data(&response).unwrap();
+        assert_eq!(data["value"].as_str().unwrap(), FLAG_VALUE.to_string());
+
+        // Crate historic get_flag request -> this should error (transaction did not exist at that pont in time)
+        let timestamp = data["lut"].as_u64().unwrap();
+        let get_flag_request = pool
+            .request_builder()
+            .build_get_flag_request(None, FLAG_NAME.to_string(), None, Some(timestamp - 1))
+            .unwrap();
+        let response = pool
+            .send_request_with_retries(&get_flag_request, &flag_response)
+            .unwrap();
+        let data = helpers::get_response_data(&response);
+        assert_eq!(true, data.is_err())
+    }
+}