aboutsummaryrefslogtreecommitdiffhomepage
path: root/tde2e/test/blockchain.cpp
diff options
context:
space:
mode:
authorArseny Smirnov <arseny30@gmail.com>2024-10-04 01:19:51 +0200
committerArseny Smirnov <arseny30@gmail.com>2025-04-10 17:18:46 +0300
commit6a46f51bdf5e5052ddae26b61cdf9d4864238442 (patch)
treebd7d1af6a2b68689cb97fc9391eeaee8851dada6 /tde2e/test/blockchain.cpp
parent6571af5d49bf9b05361187599b156e93face92df (diff)
Add tde2e library
Diffstat (limited to 'tde2e/test/blockchain.cpp')
-rw-r--r--tde2e/test/blockchain.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/tde2e/test/blockchain.cpp b/tde2e/test/blockchain.cpp
new file mode 100644
index 000000000..49397b088
--- /dev/null
+++ b/tde2e/test/blockchain.cpp
@@ -0,0 +1,251 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/e2e/TestBlockchain.h"
+
+#include "td/utils/simple_tests.h"
+#include "td/utils/Status.h"
+
+using namespace tde2e_core;
+using BB = BlockBuilder;
+using BT = BlockchainTester;
+
+S_TEST(BlockchainValidation, ZeroBlock) {
+ auto alice_pk = PrivateKey::generate().move_as_ok();
+ auto bob_pk = PrivateKey::generate().move_as_ok();
+ {
+ TEST_DEBUG_VALUE(description, "Valid: zero block with empty group state");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .with_group_state({{1, AllPermissions, alice_pk.to_public_key()}}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_ok(block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Valid: zero block with group state only in proof");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .set_value("a", "b") // need some changes
+ .with_group_state({}, false, true, 7)
+ .with_shared_key({}, false, true)
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_ok(block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: zero block with wrong height");
+ auto block = BB().with_height(1)
+ .with_block_hash({})
+ .with_group_state({}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_HeightMismatch, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: zero block with wrong hash");
+ auto block = BB().with_height(0)
+ .with_block_hash({1})
+ .with_group_state({}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_HashMismatch, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Valid: zero block with invalid signature");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .with_group_state({}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .with_public_key(alice_pk)
+ .build_zero_sign();
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_InvalidSignature, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: zero block with skipped group state proof");
+ auto block = BB().with_height(0)
+ .set_value("a", "b")
+ .with_block_hash({})
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_DEBUG_VALUE(block, block);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_InvalidStateProof_Group, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: zero block with wrong user_id in group state proof");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .set_value("a", "b")
+ .with_group_state({{1, 3, alice_pk.to_public_key()}}, false, true)
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_InvalidStateProof_Group, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: zero block with other person in group state");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .with_group_state({{2, 3, bob_pk.to_public_key()}}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_NoPermissions, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: zero block with duplicate group state");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .with_group_state({{1, 3, alice_pk.to_public_key()}}, true, true)
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_InvalidStateProof_Group, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: duplicate user_id");
+ auto block = BB().with_height(0)
+ .with_block_hash({})
+ .with_group_state({{1, 1, alice_pk.to_public_key()}, {1, 1, bob_pk.to_public_key()}}, true, false)
+ .with_shared_key({1}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_InvalidGroupState, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: duplicate public key");
+ auto block =
+ BB().with_height(0)
+ .with_block_hash({})
+ .with_group_state({{1, 1, alice_pk.to_public_key()}, {2, 1, alice_pk.to_public_key()}}, true, false)
+ .with_shared_key({1}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(BT().expect_error(E::InvalidBlock_InvalidGroupState, block));
+ }
+ return td::Status::OK();
+}
+
+S_TEST(BlockchainValidation, GroupStateChanges) {
+ auto alice_pk = PrivateKey::generate().move_as_ok();
+ auto bob_pk = PrivateKey::generate().move_as_ok();
+ auto carol_pk = PrivateKey::generate().move_as_ok();
+ Block minus_one_block;
+ auto zero_block =
+ BB().with_previous_block(minus_one_block)
+ .with_group_state({{1, 1, alice_pk.to_public_key()}, {2, 2, bob_pk.to_public_key()}}, true, false, 3)
+ .with_shared_key({1}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ {
+ TEST_DEBUG_VALUE(description, "Valid: sanity check of zero block");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: can't remove without permissions");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ auto block = BB().with_previous_block(zero_block)
+ .with_group_state({{1, 1, alice_pk.to_public_key()}}, true, false)
+ .with_shared_key({1}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(bt.expect_error(E::InvalidBlock_NoPermissions, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: can't add without permissions");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ auto block = BB().with_previous_block(zero_block)
+ .with_group_state({{3, 2, carol_pk.to_public_key()}}, true, false)
+ .with_shared_key({3}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(bob_pk);
+ TEST_TRY_STATUS(bt.expect_error(E::InvalidBlock_NoPermissions, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: can't raise permissions");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ auto block = BB().with_previous_block(zero_block)
+ .with_group_state({{1, 3, alice_pk.to_public_key()}, {2, 2, bob_pk.to_public_key()}}, true, false)
+ .with_shared_key({1, 2}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(bt.expect_error(E::InvalidBlock_NoPermissions, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Valid: new shared key");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ auto block = BB().with_previous_block(zero_block)
+ .with_group_state({{1, 1, alice_pk.to_public_key()}, {2, 2, bob_pk.to_public_key()}}, true, false)
+ .with_shared_key({1, 2}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(bt.expect_ok(block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: remove self and change shared key");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ auto block = BB().with_previous_block(zero_block)
+ .with_group_state({{1, 1, alice_pk.to_public_key()}}, true, false)
+ .with_shared_key({1}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(bob_pk);
+ TEST_TRY_STATUS(bt.expect_error(E::InvalidBlock_NoPermissions, block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Valid: self join");
+ BT bt;
+ TEST_TRY_STATUS(bt.expect_ok(zero_block));
+ auto block =
+ BB().with_previous_block(zero_block)
+ .with_group_state(
+ {{1, 1, alice_pk.to_public_key()}, {2, 2, bob_pk.to_public_key()}, {3, 2, carol_pk.to_public_key()}},
+ true, false)
+ .with_shared_key({1, 2, 3}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(carol_pk);
+ TEST_TRY_STATUS(bt.expect_ok(block));
+ }
+ {
+ TEST_DEBUG_VALUE(description, "Invalid: self join when there is no permission");
+ BT bt;
+ auto zero_block_without_external =
+ BB().with_previous_block(minus_one_block)
+ .with_group_state({{1, 1, alice_pk.to_public_key()}, {2, 2, bob_pk.to_public_key()}}, true, false, 0)
+ .with_shared_key({1}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(alice_pk);
+ TEST_TRY_STATUS(bt.expect_ok(zero_block_without_external));
+ auto block =
+ BB().with_previous_block(zero_block_without_external)
+ .with_group_state(
+ {{1, 1, alice_pk.to_public_key()}, {2, 2, bob_pk.to_public_key()}, {3, 0, carol_pk.to_public_key()}},
+ true, false)
+ .with_shared_key({1, 2, 3}, true, false)
+ .skip_group_state_proof()
+ .skip_shared_key_proof()
+ .build(carol_pk);
+ TEST_TRY_STATUS(bt.expect_error(E::InvalidBlock_NoPermissions, block));
+ }
+ return td::Status::OK();
+}