diff --git a/tests/integration/test_replicated_merge_tree_encrypted_disk/__init__.py b/tests/integration/test_replicated_merge_tree_encrypted_disk/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/key_a.xml b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/key_a.xml
new file mode 100644
index 00000000000..9d866c91f54
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/key_a.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ aaaaaaaaaaaaaaaa
+
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/key_b.xml b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/key_b.xml
new file mode 100644
index 00000000000..c34283160a5
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/key_b.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ bbbbbbbbbbbbbbbb
+
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/remote_servers.xml b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/remote_servers.xml
new file mode 100644
index 00000000000..84d16206080
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/remote_servers.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ node1
+ 9000
+
+
+ node2
+ 9000
+
+
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/storage.xml b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/storage.xml
new file mode 100644
index 00000000000..312a009ed9a
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encrypted_disk/configs/storage.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ local
+ /disk/
+
+
+ encrypted
+ disk_local
+ encrypted/
+ 0000000000000000
+
+
+
+
+
+
+ disk_encrypted
+
+
+
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encrypted_disk/test.py b/tests/integration/test_replicated_merge_tree_encrypted_disk/test.py
new file mode 100644
index 00000000000..bc5a419aaf2
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encrypted_disk/test.py
@@ -0,0 +1,87 @@
+import pytest
+from helpers.cluster import ClickHouseCluster
+from helpers.test_tools import assert_eq_with_retry, TSV
+import os
+
+
+SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
+cluster = ClickHouseCluster(__file__)
+
+node1 = cluster.add_instance("node1",
+ main_configs=["configs/remote_servers.xml", "configs/storage.xml"],
+ tmpfs=["/disk:size=100M"],
+ macros={'replica': 'node1'},
+ with_zookeeper=True)
+
+node2 = cluster.add_instance("node2",
+ main_configs=["configs/remote_servers.xml", "configs/storage.xml"],
+ tmpfs=["/disk:size=100M"],
+ macros={'replica': 'node2'},
+ with_zookeeper=True)
+
+@pytest.fixture(scope="module", autouse=True)
+def start_cluster():
+ try:
+ cluster.start()
+ yield
+ finally:
+ cluster.shutdown()
+
+
+def copy_keys(instance, keys_file_name):
+ instance.copy_file_to_container(os.path.join(SCRIPT_DIR, f"configs/{keys_file_name}.xml"), "/etc/clickhouse-server/config.d/z_keys.xml")
+ instance.query("SYSTEM RELOAD CONFIG")
+
+def create_table():
+ node1.query("DROP TABLE IF EXISTS tbl ON CLUSTER 'cluster' NO DELAY")
+ node1.query(
+ """
+ CREATE TABLE tbl ON CLUSTER 'cluster' (
+ id Int64,
+ str String
+ ) ENGINE=ReplicatedMergeTree('/clickhouse/tables/tbl/', '{replica}')
+ ORDER BY id
+ SETTINGS storage_policy='encrypted_policy'
+ """
+ )
+
+def insert_data():
+ node1.query("INSERT INTO tbl VALUES (1, 'str1')")
+ node2.query("INSERT INTO tbl VALUES (1, 'str1')") # Test deduplication
+ node2.query("INSERT INTO tbl VALUES (2, 'str2')")
+
+def optimize_table():
+ node1.query("OPTIMIZE TABLE tbl ON CLUSTER 'cluster' FINAL")
+
+def check_table():
+ expected=[[1, 'str1'], [2, 'str2']]
+ assert node1.query("SELECT * FROM tbl ORDER BY id") == TSV(expected)
+ assert node2.query("SELECT * FROM tbl ORDER BY id") == TSV(expected)
+ assert node1.query("CHECK TABLE tbl") == "1\n"
+ assert node2.query("CHECK TABLE tbl") == "1\n"
+
+
+# Actual tests:
+
+def test_same_keys():
+ copy_keys(node1, 'key_a')
+ copy_keys(node2, 'key_a')
+ create_table()
+
+ insert_data()
+ check_table()
+
+ optimize_table()
+ check_table()
+
+
+def test_different_keys():
+ copy_keys(node1, 'key_a')
+ copy_keys(node2, 'key_b')
+ create_table()
+
+ insert_data()
+ check_table()
+
+ optimize_table()
+ check_table()
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/__init__.py b/tests/integration/test_replicated_merge_tree_encryption_codec/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/encryption_codec.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/encryption_codec.xml
new file mode 100644
index 00000000000..eb4f8abaa77
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/encryption_codec.xml
@@ -0,0 +1,7 @@
+
+
+
+ 0000000000000000
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a.xml
new file mode 100644
index 00000000000..a31978e7015
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a.xml
@@ -0,0 +1,7 @@
+
+
+
+ aaaaaaaaaaaaaaaa
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_b_current_a.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_b_current_a.xml
new file mode 100644
index 00000000000..01ca9123ccb
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_b_current_a.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ aaaaaaaaaaaaaaaa
+ bbbbbbbbbbbbbbbb
+ 0
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_b_current_b.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_b_current_b.xml
new file mode 100644
index 00000000000..98cf6ced0c7
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_b_current_b.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ aaaaaaaaaaaaaaaa
+ bbbbbbbbbbbbbbbb
+ 1
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_nonce_x.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_nonce_x.xml
new file mode 100644
index 00000000000..40c5adab19b
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_nonce_x.xml
@@ -0,0 +1,8 @@
+
+
+
+ aaaaaaaaaaaaaaaa
+ xxxxxxxxxxxx
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_nonce_y.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_nonce_y.xml
new file mode 100644
index 00000000000..eadfb6e6733
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_a_and_nonce_y.xml
@@ -0,0 +1,8 @@
+
+
+
+ aaaaaaaaaaaaaaaa
+ yyyyyyyyyyyy
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_b.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_b.xml
new file mode 100644
index 00000000000..e336324f648
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/key_b.xml
@@ -0,0 +1,7 @@
+
+
+
+ bbbbbbbbbbbbbbbb
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/configs/remote_servers.xml b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/remote_servers.xml
new file mode 100644
index 00000000000..84d16206080
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/configs/remote_servers.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ node1
+ 9000
+
+
+ node2
+ 9000
+
+
+
+
+
diff --git a/tests/integration/test_replicated_merge_tree_encryption_codec/test.py b/tests/integration/test_replicated_merge_tree_encryption_codec/test.py
new file mode 100644
index 00000000000..3aec2259703
--- /dev/null
+++ b/tests/integration/test_replicated_merge_tree_encryption_codec/test.py
@@ -0,0 +1,110 @@
+import pytest
+from helpers.cluster import ClickHouseCluster
+from helpers.test_tools import assert_eq_with_retry, TSV
+import os
+
+
+SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
+cluster = ClickHouseCluster(__file__)
+
+node1 = cluster.add_instance("node1",
+ main_configs=["configs/remote_servers.xml", "configs/encryption_codec.xml"],
+ macros={'replica': 'node1'},
+ with_zookeeper=True)
+
+node2 = cluster.add_instance("node2",
+ main_configs=["configs/remote_servers.xml", "configs/encryption_codec.xml"],
+ macros={'replica': 'node2'},
+ with_zookeeper=True)
+
+@pytest.fixture(scope="module", autouse=True)
+def start_cluster():
+ try:
+ cluster.start()
+ yield
+ finally:
+ cluster.shutdown()
+
+
+def copy_keys(instance, keys_file_name):
+ instance.copy_file_to_container(os.path.join(SCRIPT_DIR, f"configs/{keys_file_name}.xml"), "/etc/clickhouse-server/config.d/z_keys.xml")
+ instance.query("SYSTEM RELOAD CONFIG")
+
+def create_table():
+ node1.query("DROP TABLE IF EXISTS tbl ON CLUSTER 'cluster' NO DELAY")
+ node1.query(
+ """
+ CREATE TABLE tbl ON CLUSTER 'cluster' (
+ id Int64,
+ str String Codec(AES_128_GCM_SIV)
+ ) ENGINE=ReplicatedMergeTree('/clickhouse/tables/tbl/', '{replica}')
+ ORDER BY id
+ """
+ )
+
+def insert_data():
+ node1.query("INSERT INTO tbl VALUES (1, 'str1')")
+ node2.query("INSERT INTO tbl VALUES (1, 'str1')") # Test deduplication
+ node2.query("INSERT INTO tbl VALUES (2, 'str2')")
+
+def optimize_table():
+ node1.query("OPTIMIZE TABLE tbl ON CLUSTER 'cluster' FINAL")
+
+def check_table():
+ expected=[[1, 'str1'], [2, 'str2']]
+ assert node1.query("SELECT * FROM tbl ORDER BY id") == TSV(expected)
+ assert node2.query("SELECT * FROM tbl ORDER BY id") == TSV(expected)
+ assert node1.query("CHECK TABLE tbl") == "1\n"
+ assert node2.query("CHECK TABLE tbl") == "1\n"
+
+
+# Actual tests:
+
+def test_same_keys():
+ copy_keys(node1, 'key_a')
+ copy_keys(node2, 'key_a')
+ create_table()
+
+ insert_data()
+ check_table()
+
+ optimize_table()
+ check_table()
+
+
+def test_different_keys():
+ copy_keys(node1, 'key_a')
+ copy_keys(node2, 'key_b')
+ create_table()
+
+ insert_data()
+ assert "BAD_DECRYPT" in node1.query_and_get_error("SELECT * FROM tbl")
+ assert "BAD_DECRYPT" in node2.query_and_get_error("SELECT * FROM tbl")
+
+ # Hang?
+ #optimize_table()
+ #check_table()
+
+
+def test_different_current_key_ids():
+ copy_keys(node1, 'key_a_and_b_current_a')
+ copy_keys(node2, 'key_a_and_b_current_b')
+ create_table()
+
+ insert_data()
+ check_table()
+
+ optimize_table()
+ check_table()
+
+
+def test_different_nonces():
+ copy_keys(node1, 'key_a_and_nonce_x')
+ copy_keys(node2, 'key_a_and_nonce_y')
+ create_table()
+
+ insert_data()
+ check_table()
+
+ optimize_table()
+ check_table()