From fafc0958eed6f5064f3e181849e7b47ab91927a1 Mon Sep 17 00:00:00 2001
From: Konstantin Munichev <toobwn@gmail.com>
Date: Wed, 20 Mar 2019 21:54:14 +0300
Subject: [PATCH] Flat file block storage (#2180)

Signed-off-by: Konstantin Munichev <toobwn@gmail.com>
---
 irohad/ametsuchi/CMakeLists.txt               |   2 +
 irohad/ametsuchi/block_storage_factory.hpp    |   1 -
 irohad/ametsuchi/impl/flat_file/flat_file.cpp |  88 ++++-----
 irohad/ametsuchi/impl/flat_file/flat_file.hpp |  42 ++--
 .../impl/flat_file_block_storage.cpp          |  85 +++++++++
 .../impl/flat_file_block_storage.hpp          |  50 +++++
 .../impl/flat_file_block_storage_factory.cpp  |  31 +++
 .../impl/flat_file_block_storage_factory.hpp  |  34 ++++
 irohad/main/application.cpp                   |  13 +-
 .../block_json_deserializer.hpp               |   2 +-
 .../iroha_internal/block_json_serializer.hpp  |   2 +-
 test/module/irohad/ametsuchi/CMakeLists.txt   |   6 +
 .../flat_file_block_storage_test.cpp          | 180 ++++++++++++++++++
 .../irohad/ametsuchi/flat_file_test.cpp       |  62 +++++-
 .../in_memory_block_storage_test.cpp          |   8 +-
 test/module/shared_model/interface_mocks.hpp  |  15 ++
 16 files changed, 531 insertions(+), 90 deletions(-)
 create mode 100644 irohad/ametsuchi/impl/flat_file_block_storage.cpp
 create mode 100644 irohad/ametsuchi/impl/flat_file_block_storage.hpp
 create mode 100644 irohad/ametsuchi/impl/flat_file_block_storage_factory.cpp
 create mode 100644 irohad/ametsuchi/impl/flat_file_block_storage_factory.hpp
 create mode 100644 test/module/irohad/ametsuchi/flat_file_block_storage_test.cpp

diff --git a/irohad/ametsuchi/CMakeLists.txt b/irohad/ametsuchi/CMakeLists.txt
index 158f6ec988..cd9860493f 100644
--- a/irohad/ametsuchi/CMakeLists.txt
+++ b/irohad/ametsuchi/CMakeLists.txt
@@ -20,6 +20,8 @@ add_library(ametsuchi
     impl/tx_presence_cache_impl.cpp
     impl/in_memory_block_storage.cpp
     impl/in_memory_block_storage_factory.cpp
+    impl/flat_file_block_storage.cpp
+    impl/flat_file_block_storage_factory.cpp
     )
 
 target_link_libraries(ametsuchi
diff --git a/irohad/ametsuchi/block_storage_factory.hpp b/irohad/ametsuchi/block_storage_factory.hpp
index c0e7655407..e738813d12 100644
--- a/irohad/ametsuchi/block_storage_factory.hpp
+++ b/irohad/ametsuchi/block_storage_factory.hpp
@@ -12,7 +12,6 @@
 
 namespace iroha {
   namespace ametsuchi {
-
     /**
      * Creates a block storage
      */
diff --git a/irohad/ametsuchi/impl/flat_file/flat_file.cpp b/irohad/ametsuchi/impl/flat_file/flat_file.cpp
index b3b73643d2..5620205b0d 100644
--- a/irohad/ametsuchi/impl/flat_file/flat_file.cpp
+++ b/irohad/ametsuchi/impl/flat_file/flat_file.cpp
@@ -18,6 +18,7 @@
 
 using namespace iroha::ametsuchi;
 using Identifier = FlatFile::Identifier;
+using BlockIdCollectionType = FlatFile::BlockIdCollectionType;
 
 // ----------| public API |----------
 
@@ -27,9 +28,20 @@ std::string FlatFile::id_to_name(Identifier id) {
   return os.str();
 }
 
+boost::optional<Identifier> FlatFile::name_to_id(const std::string &name) {
+  if (name.size() != FlatFile::DIGIT_CAPACITY) {
+    return boost::none;
+  }
+  try {
+    auto id = std::stoul(name);
+    return boost::make_optional<Identifier>(id);
+  } catch (const std::exception &e) {
+    return boost::none;
+  }
+}
+
 boost::optional<std::unique_ptr<FlatFile>> FlatFile::create(
-    const std::string &path,
-    logger::LoggerPtr log) {
+    const std::string &path, logger::LoggerPtr log) {
   boost::system::error_code err;
   if (not boost::filesystem::is_directory(path, err)
       and not boost::filesystem::create_directory(path, err)) {
@@ -37,19 +49,24 @@ boost::optional<std::unique_ptr<FlatFile>> FlatFile::create(
     return boost::none;
   }
 
-  auto res = FlatFile::check_consistency(path, log);
-  return std::make_unique<FlatFile>(*res, path, private_tag{}, std::move(log));
+  BlockIdCollectionType files_found;
+  for (auto it = boost::filesystem::directory_iterator{path};
+       it != boost::filesystem::directory_iterator{};
+       ++it) {
+    if (auto id = FlatFile::name_to_id(it->path().filename().string())) {
+      files_found.insert(*id);
+    } else {
+      boost::filesystem::remove(it->path());
+    }
+  }
+
+  return std::make_unique<FlatFile>(
+      path, std::move(files_found), private_tag{}, std::move(log));
 }
 
 bool FlatFile::add(Identifier id, const Bytes &block) {
   // TODO(x3medima17): Change bool to generic Result return type
 
-  if (id != current_id_ + 1) {
-    log_->warn("Cannot append non-consecutive block");
-    return false;
-  }
-
-  auto next_id = id;
   const auto file_name = boost::filesystem::path{dump_dir_} / id_to_name(id);
 
   // Write block to binary file
@@ -71,8 +88,7 @@ bool FlatFile::add(Identifier id, const Bytes &block) {
   file.write(reinterpret_cast<const char *>(block.data()),
              block.size() * val_size);
 
-  // Update internals, release lock
-  current_id_ = next_id;
+  available_blocks_.insert(id);
   return true;
 }
 
@@ -100,50 +116,24 @@ std::string FlatFile::directory() const {
 }
 
 Identifier FlatFile::last_id() const {
-  return current_id_.load();
+  return (available_blocks_.empty()) ? 0 : *available_blocks_.rbegin();
 }
 
 void FlatFile::dropAll() {
   iroha::remove_dir_contents(dump_dir_, log_);
-  auto res = FlatFile::check_consistency(dump_dir_, log_);
-  current_id_.store(*res);
+  available_blocks_.clear();
+}
+
+const BlockIdCollectionType &FlatFile::blockIdentifiers() const {
+  return available_blocks_;
 }
 
 // ----------| private API |----------
 
-FlatFile::FlatFile(Identifier current_id,
-                   const std::string &path,
+FlatFile::FlatFile(std::string path,
+                   BlockIdCollectionType existing_files,
                    FlatFile::private_tag,
                    logger::LoggerPtr log)
-    : dump_dir_(path), log_{std::move(log)} {
-  current_id_.store(current_id);
-}
-
-boost::optional<Identifier> FlatFile::check_consistency(
-    const std::string &dump_dir, logger::LoggerPtr log) {
-  if (dump_dir.empty()) {
-    log->error("check_consistency({}), not directory", dump_dir);
-    return boost::none;
-  }
-
-  auto const files = [&dump_dir] {
-    std::vector<boost::filesystem::path> ps;
-    std::copy(boost::filesystem::directory_iterator{dump_dir},
-              boost::filesystem::directory_iterator{},
-              std::back_inserter(ps));
-    std::sort(ps.begin(), ps.end(), std::less<boost::filesystem::path>());
-    return ps;
-  }();
-
-  auto const missing = boost::range::find_if(
-      files | boost::adaptors::indexed(1), [](const auto &it) {
-        return FlatFile::id_to_name(it.index()) != it.value().filename();
-      });
-
-  std::for_each(
-      missing.get(), files.cend(), [](const boost::filesystem::path &p) {
-        boost::filesystem::remove(p);
-      });
-
-  return missing.get() - files.cbegin();
-}
+    : dump_dir_(std::move(path)),
+      available_blocks_(std::move(existing_files)),
+      log_{std::move(log)} {}
diff --git a/irohad/ametsuchi/impl/flat_file/flat_file.hpp b/irohad/ametsuchi/impl/flat_file/flat_file.hpp
index da20283075..59f7081848 100644
--- a/irohad/ametsuchi/impl/flat_file/flat_file.hpp
+++ b/irohad/ametsuchi/impl/flat_file/flat_file.hpp
@@ -8,8 +8,8 @@
 
 #include "ametsuchi/key_value_storage.hpp"
 
-#include <atomic>
 #include <memory>
+#include <set>
 
 #include "logger/logger_fwd.hpp"
 
@@ -29,6 +29,8 @@ namespace iroha {
      public:
       // ----------| public API |----------
 
+      using BlockIdCollectionType = std::set<Identifier>;
+
       static const uint32_t DIGIT_CAPACITY = 16;
 
       /**
@@ -45,6 +47,13 @@ namespace iroha {
        */
       static std::string id_to_name(Identifier id);
 
+      /**
+       * Converts aligned string (see above) to number.
+       * @param name - name to convert
+       * @return id or boost::none
+       */
+      static boost::optional<Identifier> name_to_id(const std::string &name);
+
       /**
        * Create storage in paths
        * @param path - target path for creating
@@ -62,18 +71,12 @@ namespace iroha {
 
       Identifier last_id() const override;
 
+      void dropAll() override;
+
       /**
-       * Checking consistency of storage for provided folder
-       * If some block in the middle is missing all blocks following it are
-       * deleted
-       * @param dump_dir - folder of storage
-       * @param log - log for local messages
-       * @return - last available identifier
+       * @return collection of available block ids
        */
-      static boost::optional<Identifier> check_consistency(
-          const std::string &dump_dir, logger::LoggerPtr log);
-
-      void dropAll() override;
+      const BlockIdCollectionType &blockIdentifiers() const;
 
       // ----------| modify operations |----------
 
@@ -88,29 +91,24 @@ namespace iroha {
       // ----------| private API |----------
 
       /**
-       * Create storage in path with respect to last key
-       * @param last_id - maximal key written in storage
+       * Create storage in path
        * @param path - folder of storage
+       * @param existing_files - collection of existing files names
        * @param log to print progress
        */
-      FlatFile(Identifier last_id,
-               const std::string &path,
+      FlatFile(std::string path,
+               BlockIdCollectionType existing_files,
                FlatFile::private_tag,
                logger::LoggerPtr log);
 
      private:
-      // ----------| private fields |----------
-
-      /**
-       * Last written key
-       */
-      std::atomic<Identifier> current_id_;
-
       /**
        * Folder of storage
        */
       const std::string dump_dir_;
 
+      BlockIdCollectionType available_blocks_;
+
       logger::LoggerPtr log_;
 
      public:
diff --git a/irohad/ametsuchi/impl/flat_file_block_storage.cpp b/irohad/ametsuchi/impl/flat_file_block_storage.cpp
new file mode 100644
index 0000000000..dba89a8a1e
--- /dev/null
+++ b/irohad/ametsuchi/impl/flat_file_block_storage.cpp
@@ -0,0 +1,85 @@
+/**
+ * Copyright Soramitsu Co., Ltd. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "ametsuchi/impl/flat_file_block_storage.hpp"
+
+#include <boost/filesystem.hpp>
+
+#include "backend/protobuf/block.hpp"
+#include "common/byteutils.hpp"
+#include "logger/logger.hpp"
+
+using namespace iroha::ametsuchi;
+
+FlatFileBlockStorage::FlatFileBlockStorage(
+    std::unique_ptr<FlatFile> flat_file,
+    std::shared_ptr<shared_model::interface::BlockJsonConverter> json_converter,
+    logger::LoggerPtr log)
+    : flat_file_storage_(std::move(flat_file)),
+      json_converter_(std::move(json_converter)),
+      log_(std::move(log)) {}
+
+FlatFileBlockStorage::~FlatFileBlockStorage() {
+  log_->info("Remove {} temp directory", flat_file_storage_->directory());
+  boost::filesystem::remove_all(flat_file_storage_->directory());
+}
+
+bool FlatFileBlockStorage::insert(
+    std::shared_ptr<const shared_model::interface::Block> block) {
+  return json_converter_->serialize(*block).match(
+      [&](const expected::Value<std::string> &block_json) {
+        return flat_file_storage_->add(block->height(),
+                                       stringToBytes(block_json.value));
+      },
+      [this](const auto &error) {
+        log_->warn("Error while block serialization: {}", error.error);
+        return false;
+      });
+}
+
+bool FlatFileBlockStorage::insert(const shared_model::interface::Block &block) {
+  return insert(clone(block));
+}
+
+boost::optional<std::shared_ptr<const shared_model::interface::Block>>
+FlatFileBlockStorage::fetch(
+    shared_model::interface::types::HeightType height) const {
+  auto storage_block = flat_file_storage_->get(height);
+  if (not storage_block) {
+    return boost::none;
+  }
+
+  return json_converter_->deserialize(bytesToString(*storage_block))
+      .match(
+          [&](expected::Value<std::unique_ptr<shared_model::interface::Block>>
+                  &block) {
+            return boost::make_optional<
+                std::shared_ptr<const shared_model::interface::Block>>(
+                std::move(block.value));
+          },
+          [&](expected::Error<std::string> &error)
+              -> boost::optional<
+                  std::shared_ptr<const shared_model::interface::Block>> {
+            log_->warn("Error while block deserialization: {}", error.error);
+            return boost::none;
+          });
+}
+
+size_t FlatFileBlockStorage::size() const {
+  return flat_file_storage_->blockIdentifiers().size();
+}
+
+void FlatFileBlockStorage::clear() {
+  flat_file_storage_->dropAll();
+}
+
+void FlatFileBlockStorage::forEach(
+    iroha::ametsuchi::BlockStorage::FunctionType function) const {
+  for (auto block_id : flat_file_storage_->blockIdentifiers()) {
+    auto block = fetch(block_id);
+    BOOST_ASSERT(block);
+    function(*block);
+  }
+}
diff --git a/irohad/ametsuchi/impl/flat_file_block_storage.hpp b/irohad/ametsuchi/impl/flat_file_block_storage.hpp
new file mode 100644
index 0000000000..3cdb7be820
--- /dev/null
+++ b/irohad/ametsuchi/impl/flat_file_block_storage.hpp
@@ -0,0 +1,50 @@
+/**
+ * Copyright Soramitsu Co., Ltd. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef IROHA_FLAT_FILE_BLOCK_STORAGE_HPP
+#define IROHA_FLAT_FILE_BLOCK_STORAGE_HPP
+
+#include "ametsuchi/block_storage.hpp"
+
+#include "ametsuchi/impl/flat_file/flat_file.hpp"
+#include "interfaces/iroha_internal/block_json_converter.hpp"
+#include "logger/logger_fwd.hpp"
+
+namespace iroha {
+  namespace ametsuchi {
+    class FlatFileBlockStorage : public BlockStorage {
+     public:
+      FlatFileBlockStorage(
+          std::unique_ptr<FlatFile> flat_file,
+          std::shared_ptr<shared_model::interface::BlockJsonConverter>
+              json_converter,
+          logger::LoggerPtr log);
+
+      ~FlatFileBlockStorage() override;
+
+      bool insert(
+          std::shared_ptr<const shared_model::interface::Block> block) override;
+
+      bool insert(const shared_model::interface::Block &block) override;
+
+      boost::optional<std::shared_ptr<const shared_model::interface::Block>>
+      fetch(shared_model::interface::types::HeightType height) const override;
+
+      size_t size() const override;
+
+      void clear() override;
+
+      void forEach(FunctionType function) const override;
+
+     private:
+      std::unique_ptr<FlatFile> flat_file_storage_;
+      std::shared_ptr<shared_model::interface::BlockJsonConverter>
+          json_converter_;
+      logger::LoggerPtr log_;
+    };
+  }  // namespace ametsuchi
+}  // namespace iroha
+
+#endif  // IROHA_FLAT_FILE_BLOCK_STORAGE_HPP
diff --git a/irohad/ametsuchi/impl/flat_file_block_storage_factory.cpp b/irohad/ametsuchi/impl/flat_file_block_storage_factory.cpp
new file mode 100644
index 0000000000..6b69e46f66
--- /dev/null
+++ b/irohad/ametsuchi/impl/flat_file_block_storage_factory.cpp
@@ -0,0 +1,31 @@
+/**
+ * Copyright Soramitsu Co., Ltd. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "ametsuchi/impl/flat_file_block_storage_factory.hpp"
+
+#include "ametsuchi/impl/flat_file_block_storage.hpp"
+
+using namespace iroha::ametsuchi;
+
+FlatFileBlockStorageFactory::FlatFileBlockStorageFactory(
+    std::function<std::string()> path_provider,
+    std::shared_ptr<shared_model::interface::BlockJsonConverter>
+        json_block_converter,
+    logger::LoggerManagerTreePtr log_manager)
+    : path_provider_(std::move(path_provider)),
+      json_block_converter_(std::move(json_block_converter)),
+      log_manager_(std::move(log_manager)) {}
+
+std::unique_ptr<BlockStorage> FlatFileBlockStorageFactory::create() {
+  auto flat_file = FlatFile::create(
+      path_provider_(), log_manager_->getChild("FlatFile")->getLogger());
+  if (not flat_file) {
+    return nullptr;
+  }
+  return std::make_unique<FlatFileBlockStorage>(
+      std::move(flat_file.get()),
+      json_block_converter_,
+      log_manager_->getChild("FlatFileBlockFactory")->getLogger());
+}
diff --git a/irohad/ametsuchi/impl/flat_file_block_storage_factory.hpp b/irohad/ametsuchi/impl/flat_file_block_storage_factory.hpp
new file mode 100644
index 0000000000..30d9f288e5
--- /dev/null
+++ b/irohad/ametsuchi/impl/flat_file_block_storage_factory.hpp
@@ -0,0 +1,34 @@
+/**
+ * Copyright Soramitsu Co., Ltd. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef IROHA_FLAT_FILE_BLOCK_STORAGE_FACTORY_HPP
+#define IROHA_FLAT_FILE_BLOCK_STORAGE_FACTORY_HPP
+
+#include "ametsuchi/block_storage_factory.hpp"
+
+#include "interfaces/iroha_internal/block_json_converter.hpp"
+#include "logger/logger_manager.hpp"
+
+namespace iroha {
+  namespace ametsuchi {
+    class FlatFileBlockStorageFactory : public BlockStorageFactory {
+     public:
+      FlatFileBlockStorageFactory(
+          std::function<std::string()> path_provider,
+          std::shared_ptr<shared_model::interface::BlockJsonConverter>
+              json_block_converter,
+          logger::LoggerManagerTreePtr log_manager);
+      std::unique_ptr<BlockStorage> create() override;
+
+     private:
+      std::function<std::string()> path_provider_;
+      std::shared_ptr<shared_model::interface::BlockJsonConverter>
+          json_block_converter_;
+      logger::LoggerManagerTreePtr log_manager_;
+    };
+  }  // namespace ametsuchi
+}  // namespace iroha
+
+#endif  // IROHA_FLAT_FILE_BLOCK_STORAGE_FACTORY_HPP
diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp
index 751becedc9..dc2085236b 100644
--- a/irohad/main/application.cpp
+++ b/irohad/main/application.cpp
@@ -5,7 +5,9 @@
 
 #include "main/application.hpp"
 
-#include "ametsuchi/impl/in_memory_block_storage_factory.hpp"
+#include <boost/filesystem.hpp>
+
+#include "ametsuchi/impl/flat_file_block_storage_factory.hpp"
 #include "ametsuchi/impl/storage_impl.hpp"
 #include "ametsuchi/impl/tx_presence_cache_impl.hpp"
 #include "ametsuchi/impl/wsv_restorer_impl.hpp"
@@ -161,7 +163,14 @@ void Irohad::initStorage() {
       std::make_shared<shared_model::proto::ProtoPermissionToString>();
   auto block_converter =
       std::make_shared<shared_model::proto::ProtoBlockJsonConverter>();
-  auto block_storage_factory = std::make_unique<InMemoryBlockStorageFactory>();
+  auto block_storage_factory = std::make_unique<FlatFileBlockStorageFactory>(
+      []() {
+        return (boost::filesystem::temp_directory_path()
+                / boost::filesystem::unique_path())
+            .string();
+      },
+      block_converter,
+      log_manager_);
   auto storageResult = StorageImpl::create(block_store_dir_,
                                            pg_conn_,
                                            common_objects_factory_,
diff --git a/shared_model/interfaces/iroha_internal/block_json_deserializer.hpp b/shared_model/interfaces/iroha_internal/block_json_deserializer.hpp
index 444bfaf5b4..d57273f71a 100644
--- a/shared_model/interfaces/iroha_internal/block_json_deserializer.hpp
+++ b/shared_model/interfaces/iroha_internal/block_json_deserializer.hpp
@@ -26,7 +26,7 @@ namespace shared_model {
        * @return pointer to a block if json was valid or an error
        */
       virtual iroha::expected::Result<std::unique_ptr<Block>, std::string>
-      deserialize(const types::JsonType &json) const noexcept = 0;
+      deserialize(const types::JsonType &json) const = 0;
 
       virtual ~BlockJsonDeserializer() = default;
     };
diff --git a/shared_model/interfaces/iroha_internal/block_json_serializer.hpp b/shared_model/interfaces/iroha_internal/block_json_serializer.hpp
index f505e5321d..238528cb67 100644
--- a/shared_model/interfaces/iroha_internal/block_json_serializer.hpp
+++ b/shared_model/interfaces/iroha_internal/block_json_serializer.hpp
@@ -26,7 +26,7 @@ namespace shared_model {
        * @return json string or an error
        */
       virtual iroha::expected::Result<types::JsonType, std::string>
-      serialize(const Block &block) const noexcept = 0;
+      serialize(const Block &block) const = 0;
 
       virtual ~BlockJsonSerializer() = default;
     };
diff --git a/test/module/irohad/ametsuchi/CMakeLists.txt b/test/module/irohad/ametsuchi/CMakeLists.txt
index 09bed3e35d..63e12c49fc 100644
--- a/test/module/irohad/ametsuchi/CMakeLists.txt
+++ b/test/module/irohad/ametsuchi/CMakeLists.txt
@@ -74,6 +74,12 @@ target_link_libraries(in_memory_block_storage_test
     ametsuchi
     )
 
+addtest(flat_file_block_storage_test flat_file_block_storage_test.cpp)
+target_link_libraries(flat_file_block_storage_test
+    ametsuchi
+    test_logger
+    )
+
 add_library(ametsuchi_fixture INTERFACE)
 target_link_libraries(ametsuchi_fixture INTERFACE
     integration_framework_config_helper
diff --git a/test/module/irohad/ametsuchi/flat_file_block_storage_test.cpp b/test/module/irohad/ametsuchi/flat_file_block_storage_test.cpp
new file mode 100644
index 0000000000..7ee0e6c5d8
--- /dev/null
+++ b/test/module/irohad/ametsuchi/flat_file_block_storage_test.cpp
@@ -0,0 +1,180 @@
+/**
+ * Copyright Soramitsu Co., Ltd. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "ametsuchi/impl/flat_file_block_storage.hpp"
+#include "ametsuchi/impl/flat_file_block_storage_factory.hpp"
+
+#include <gtest/gtest.h>
+#include <boost/filesystem.hpp>
+#include "framework/test_logger.hpp"
+#include "module/shared_model/interface_mocks.hpp"
+
+using namespace iroha::ametsuchi;
+using namespace boost::filesystem;
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+class FlatFileBlockStorageTest : public ::testing::Test {
+ public:
+  FlatFileBlockStorageTest() {
+    ON_CALL(*block_, height()).WillByDefault(Return(height_));
+  }
+
+ protected:
+  void SetUp() override {
+    create_directory(path_provider_());
+  }
+
+  const std::string block_store_path_ =
+      (temp_directory_path() / unique_path()).string();
+
+  std::function<std::string()> path_provider_ = [&]() {
+    return block_store_path_;
+  };
+
+  std::shared_ptr<MockBlockJsonConverter> converter_ =
+      std::make_shared<NiceMock<MockBlockJsonConverter>>();
+  logger::LoggerManagerTreePtr log_manager_ = getTestLoggerManager();
+  std::shared_ptr<MockBlock> block_ = std::make_shared<NiceMock<MockBlock>>();
+  shared_model::interface::types::HeightType height_ = 1;
+};
+
+/**
+ * @given block storage factory
+ * @when create is called
+ * @then block storage is created
+ */
+TEST_F(FlatFileBlockStorageTest, Creation) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  ASSERT_TRUE(block_storage);
+}
+
+/**
+ * @given initialized block storage, single block with height_ inserted
+ * @when another block with height_ is inserted
+ * @then second insertion fails
+ */
+TEST_F(FlatFileBlockStorageTest, Insert) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  ASSERT_TRUE(block_storage->insert(block_));
+  ASSERT_FALSE(block_storage->insert(block_));
+}
+
+/**
+ * @given initialized block storage, single block with height_ inserted
+ * @when block with height_ is fetched
+ * @then it is returned
+ */
+TEST_F(FlatFileBlockStorageTest, FetchExisting) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  ASSERT_TRUE(block_storage->insert(block_));
+
+  shared_model::interface::Block *raw_block;
+
+  EXPECT_CALL(*converter_, deserialize(_))
+      .WillOnce(Invoke([&](const shared_model::interface::types::JsonType &)
+                           -> iroha::expected::Result<
+                               std::unique_ptr<shared_model::interface::Block>,
+                               std::string> {
+        auto return_block = std::make_unique<MockBlock>();
+        raw_block = return_block.get();
+        return iroha::expected::makeValue<
+            std::unique_ptr<shared_model::interface::Block>>(
+            std::move(return_block));
+      }));
+  std::shared_ptr<const shared_model::interface::Block> block_var =
+      *(block_storage->fetch(height_));
+
+  ASSERT_EQ(raw_block, block_var.get());
+}
+
+/**
+ * @given initialized block storage without blocks
+ * @when block with height_ is fetched
+ * @then nothing is returned
+ */
+TEST_F(FlatFileBlockStorageTest, FetchNonexistent) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  auto block_var = block_storage->fetch(height_);
+  ASSERT_FALSE(block_var);
+}
+
+/**
+ * @given initialized block storage, single block with height_ inserted
+ * @when size is fetched
+ * @then 1 is returned
+ */
+TEST_F(FlatFileBlockStorageTest, Size) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  ASSERT_TRUE(block_storage->insert(block_));
+
+  ASSERT_EQ(1, block_storage->size());
+}
+
+/**
+ * @given initialized block storage, single block with height_ inserted
+ * @when storage is cleared with clear
+ * @then no blocks are left in storage
+ */
+TEST_F(FlatFileBlockStorageTest, Clear) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  ASSERT_TRUE(block_storage->insert(block_));
+
+  block_storage->clear();
+
+  auto block_var = block_storage->fetch(height_);
+
+  ASSERT_FALSE(block_var);
+}
+
+/**
+ * @given initialized block storage, single block with height_ inserted
+ * @when forEach is called
+ * @then block with height_ is visited, lambda is invoked once
+ */
+TEST_F(FlatFileBlockStorageTest, ForEach) {
+  auto block_storage =
+      FlatFileBlockStorageFactory(path_provider_, converter_, log_manager_)
+          .create();
+  ASSERT_TRUE(block_storage->insert(block_));
+
+  shared_model::interface::Block *raw_block;
+
+  EXPECT_CALL(*converter_, deserialize(_))
+      .WillOnce(Invoke([&](const shared_model::interface::types::JsonType &)
+                           -> iroha::expected::Result<
+                               std::unique_ptr<shared_model::interface::Block>,
+                               std::string> {
+        auto return_block = std::make_unique<MockBlock>();
+        raw_block = return_block.get();
+        return iroha::expected::makeValue<
+            std::unique_ptr<shared_model::interface::Block>>(
+            std::move(return_block));
+      }));
+
+  size_t count = 0;
+
+  block_storage->forEach([&count, &raw_block](const auto &block) {
+    ++count;
+    ASSERT_EQ(raw_block, block.get());
+  });
+
+  ASSERT_EQ(1, count);
+}
diff --git a/test/module/irohad/ametsuchi/flat_file_test.cpp b/test/module/irohad/ametsuchi/flat_file_test.cpp
index e4d5a1647e..f6dc7f9ea5 100644
--- a/test/module/irohad/ametsuchi/flat_file_test.cpp
+++ b/test/module/irohad/ametsuchi/flat_file_test.cpp
@@ -52,6 +52,12 @@ TEST_F(BlStore_Test, Read_Write_Test) {
   ASSERT_EQ(*res, block);
 }
 
+/**
+ * @given initialized FlatFile storage and 3 blocks are inserted into it
+ * @when storage removed, file for a second block removed and new storage is
+ * created on the same directory
+ * @then last block id is still 3
+ */
 TEST_F(BlStore_Test, BlockStoreWhenRemoveBlock) {
   log_->info("----------| Simulate removal of the block |----------");
   // Remove file in the middle of the block store
@@ -79,7 +85,7 @@ TEST_F(BlStore_Test, BlockStoreWhenRemoveBlock) {
   ASSERT_TRUE(store);
   auto bl_store = std::move(*store);
   auto res = bl_store->last_id();
-  ASSERT_EQ(res, 1);
+  ASSERT_EQ(res, 3);
 }
 
 TEST_F(BlStore_Test, BlockStoreWhenAbsentFolder) {
@@ -120,15 +126,6 @@ TEST_F(BlStore_Test, BlockStoreInitializationFromNonemptyFolder) {
   ASSERT_EQ(bl_store1->last_id(), bl_store2->last_id());
 }
 
-/**
- * @given empty folder name
- * @then check consistency fails
- */
-TEST_F(BlStore_Test, EmptyDumpDir) {
-  auto res = FlatFile::check_consistency("", flat_file_log_);
-  ASSERT_FALSE(res);
-}
-
 /**
  * @given empty folder with block store
  * @when block id does not exist
@@ -217,3 +214,48 @@ TEST_F(BlStore_Test, WriteDeniedFolder) {
   auto res = bl_store->add(id, block);
   ASSERT_FALSE(res);
 }
+
+/**
+ * @given initialized FlatFile storage
+ * @when several blocks with non-consecutive ids are inserted
+ * @then all inserted blocks are available, block 1 is not available
+ */
+TEST_F(BlStore_Test, RandomNumbers) {
+  auto store = FlatFile::create(block_store_path, flat_file_log_);
+  ASSERT_TRUE(store);
+  auto bl_store = std::move(*store);
+  bl_store->add(5, block);
+  bl_store->add(22, block);
+  bl_store->add(11, block);
+  ASSERT_TRUE(bl_store->get(5));
+  ASSERT_TRUE(bl_store->get(22));
+  ASSERT_TRUE(bl_store->get(11));
+  ASSERT_FALSE(bl_store->get(1));
+}
+
+/**
+ * @given initialized FlatFile storage
+ * @when 3 blocks with non-consecutive ids are inserted, storage removed, and
+ * new storage is created on the same directory
+ * @then only blocks with inserted ids are available
+ */
+TEST_F(BlStore_Test, RemoveAndCreateNew) {
+  {
+    auto store = FlatFile::create(block_store_path, flat_file_log_);
+    ASSERT_TRUE(store);
+    auto bl_store = std::move(*store);
+
+    bl_store->add(4, block);
+    bl_store->add(17, block);
+    bl_store->add(7, block);
+  }
+
+  auto store = FlatFile::create(block_store_path, flat_file_log_);
+  ASSERT_TRUE(store);
+  auto bl_store = std::move(*store);
+
+  ASSERT_TRUE(bl_store->get(4));
+  ASSERT_TRUE(bl_store->get(17));
+  ASSERT_TRUE(bl_store->get(7));
+  ASSERT_FALSE(bl_store->get(1));
+}
diff --git a/test/module/irohad/ametsuchi/in_memory_block_storage_test.cpp b/test/module/irohad/ametsuchi/in_memory_block_storage_test.cpp
index b30d927f97..37cb983a11 100644
--- a/test/module/irohad/ametsuchi/in_memory_block_storage_test.cpp
+++ b/test/module/irohad/ametsuchi/in_memory_block_storage_test.cpp
@@ -77,7 +77,7 @@ TEST_F(InMemoryBlockStorageTest, FetchNonexistent) {
 }
 
 /**
- * @given initialized block storage, single block with id_ inserted
+ * @given initialized block storage, single block with height_ inserted
  * @when size is fetched
  * @then 1 is returned
  */
@@ -88,7 +88,7 @@ TEST_F(InMemoryBlockStorageTest, Size) {
 }
 
 /**
- * @given initialized block storage, single block with id_ inserted
+ * @given initialized block storage, single block with height_ inserted
  * @when storage is cleared with clear
  * @then no blocks are left in storage
  */
@@ -103,9 +103,9 @@ TEST_F(InMemoryBlockStorageTest, Clear) {
 }
 
 /**
- * @given initialized block storage, single block with id_ inserted
+ * @given initialized block storage, single block with height_ inserted
  * @when forEach is called
- * @then block with id_ is visited, lambda is invoked once
+ * @then block with height_ is visited, lambda is invoked once
  */
 TEST_F(InMemoryBlockStorageTest, ForEach) {
   ASSERT_TRUE(block_storage_.insert(block_));
diff --git a/test/module/shared_model/interface_mocks.hpp b/test/module/shared_model/interface_mocks.hpp
index 2d387c0a44..34fb5b5e50 100644
--- a/test/module/shared_model/interface_mocks.hpp
+++ b/test/module/shared_model/interface_mocks.hpp
@@ -13,6 +13,7 @@
 #include "interfaces/common_objects/common_objects_factory.hpp"
 #include "interfaces/common_objects/peer.hpp"
 #include "interfaces/iroha_internal/block.hpp"
+#include "interfaces/iroha_internal/block_json_converter.hpp"
 #include "interfaces/iroha_internal/proposal.hpp"
 #include "interfaces/iroha_internal/transaction_batch.hpp"
 #include "interfaces/iroha_internal/unsafe_proposal_factory.hpp"
@@ -260,4 +261,18 @@ struct MockAccount : public shared_model::interface::Account {
   MOCK_CONST_METHOD0(clone, MockAccount *());
 };
 
+struct MockBlockJsonConverter
+    : public shared_model::interface::BlockJsonConverter {
+  MOCK_CONST_METHOD1(
+      serialize,
+      iroha::expected::Result<shared_model::interface::types::JsonType,
+                              std::string>(
+          const shared_model::interface::Block &block));
+  MOCK_CONST_METHOD1(
+      deserialize,
+      iroha::expected::Result<std::unique_ptr<shared_model::interface::Block>,
+                              std::string>(
+          const shared_model::interface::types::JsonType &json));
+};
+
 #endif  // IROHA_SHARED_MODEL_INTERFACE_MOCKS_HPP