Skip to content

Commit ee46209

Browse files
IPFS BlockService (#59)
Signed-off-by: Sergey Kaprovich <[email protected]>
1 parent 57e46ce commit ee46209

File tree

7 files changed

+346
-0
lines changed

7 files changed

+346
-0
lines changed

core/storage/ipfs/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,10 @@ target_link_libraries(ipfs_datastore_leveldb
2222
cid
2323
leveldb
2424
)
25+
26+
add_library(ipfs_blockservice
27+
impl/blockservice_impl.cpp
28+
)
29+
target_link_libraries(ipfs_blockservice
30+
buffer
31+
)

core/storage/ipfs/block.hpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef FILECOIN_STORAGE_IPFS_BLOCK_HPP
7+
#define FILECOIN_STORAGE_IPFS_BLOCK_HPP
8+
9+
#include "common/buffer.hpp"
10+
#include "common/cid.hpp"
11+
12+
namespace fc::storage::ipfs {
13+
/**
14+
* @interface Piece of data, which is used with BlockService
15+
*/
16+
struct Block {
17+
using Content = common::Buffer; /**< Alias for content type */
18+
19+
/**
20+
* @brief Default destructor
21+
*/
22+
virtual ~Block() = default;
23+
24+
/**
25+
* @brief Get content identifier
26+
* @return Block CID
27+
*/
28+
virtual const CID &getCID() const = 0;
29+
30+
/**
31+
* @brief Get block content
32+
* @return Block's raw data for store in the BlockService
33+
*/
34+
virtual const Content &getContent() const = 0;
35+
};
36+
} // namespace fc::storage::ipfs
37+
38+
#endif

core/storage/ipfs/blockservice.hpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef FILECOIN_STORAGE_IPFS_BLOCKSERVICE_HPP
7+
#define FILECOIN_STORAGE_IPFS_BLOCKSERVICE_HPP
8+
9+
#include "common/buffer.hpp"
10+
#include "common/outcome.hpp"
11+
#include "storage/ipfs/datastore.hpp"
12+
13+
#include "storage/ipfs/block.hpp"
14+
15+
namespace fc::storage::ipfs {
16+
/**
17+
* @interface Provides a seamless interface to both local and remote storage
18+
* backends
19+
*/
20+
class BlockService {
21+
public:
22+
/**
23+
* @brief Default destructor
24+
*/
25+
virtual ~BlockService() = default;
26+
27+
/**
28+
* @brief Add new block to local storage
29+
* @param block - entity to add
30+
* @return operation result
31+
*/
32+
virtual outcome::result<void> addBlock(const Block &block) = 0;
33+
34+
/**
35+
* @brief Check for block existence in the local and remote storage
36+
* @param cid - block id
37+
* @return operation result
38+
*/
39+
virtual outcome::result<bool> has(const CID &cid) const = 0;
40+
41+
/**
42+
* @brief Get block's content from local or remote storage
43+
* @param cid - block id
44+
* @return operation result
45+
*/
46+
virtual outcome::result<Block::Content> getBlockContent(
47+
const CID &cid) const = 0;
48+
49+
/**
50+
* @brief Remove block from local storage
51+
* @param cid - block id
52+
* @return operation result
53+
*/
54+
virtual outcome::result<void> removeBlock(const CID &cid) = 0;
55+
};
56+
57+
/**
58+
* @enum Block service errors
59+
*/
60+
enum class BlockServiceError : int {
61+
CID_NOT_FOUND = 1,
62+
ADD_BLOCK_FAILED,
63+
GET_BLOCK_FAILED,
64+
REMOVE_BLOCK_FAILED
65+
};
66+
} // namespace fc::storage::ipfs
67+
68+
OUTCOME_HPP_DECLARE_ERROR(fc::storage::ipfs, BlockServiceError);
69+
70+
#endif
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "storage/ipfs/impl/blockservice_impl.hpp"
7+
8+
using fc::storage::ipfs::Block;
9+
10+
namespace fc::storage::ipfs {
11+
BlockServiceImpl::BlockServiceImpl(std::shared_ptr<IpfsDatastore> data_store)
12+
: local_storage_{std::move(data_store)} {
13+
BOOST_ASSERT_MSG(local_storage_ != nullptr,
14+
"BlockService: IpfsDatastore does not exist");
15+
}
16+
17+
outcome::result<void> BlockServiceImpl::addBlock(const Block &block) {
18+
auto result = local_storage_->set(block.getCID(), block.getContent());
19+
if (result.has_error()) {
20+
return BlockServiceError::ADD_BLOCK_FAILED;
21+
}
22+
return outcome::success();
23+
}
24+
25+
outcome::result<bool> BlockServiceImpl::has(const CID &cid) const {
26+
return local_storage_->contains(cid);
27+
}
28+
29+
outcome::result<Block::Content> BlockServiceImpl::getBlockContent(
30+
const CID &cid) const {
31+
outcome::result<Block::Content> data = local_storage_->get(cid);
32+
if (data.has_error()) {
33+
if (data.error() == IpfsDatastoreError::NOT_FOUND) {
34+
return BlockServiceError::CID_NOT_FOUND;
35+
}
36+
return BlockServiceError::GET_BLOCK_FAILED;
37+
}
38+
return data;
39+
}
40+
41+
outcome::result<void> BlockServiceImpl::removeBlock(const CID &cid) {
42+
auto result = local_storage_->remove(cid);
43+
if (result.has_error()) {
44+
if (result.error() == IpfsDatastoreError::NOT_FOUND) {
45+
return BlockServiceError::CID_NOT_FOUND;
46+
}
47+
return BlockServiceError::REMOVE_BLOCK_FAILED;
48+
}
49+
return outcome::success();
50+
}
51+
} // namespace fc::storage::ipfs
52+
53+
OUTCOME_CPP_DEFINE_CATEGORY(fc::storage::ipfs, BlockServiceError, e) {
54+
using fc::storage::ipfs::BlockServiceError;
55+
switch (e) {
56+
case BlockServiceError::CID_NOT_FOUND:
57+
return "BlockServiceError: block with given CID not found";
58+
case BlockServiceError::ADD_BLOCK_FAILED:
59+
return "BlockServiceError: failed to add block to datastore";
60+
case BlockServiceError::GET_BLOCK_FAILED:
61+
return "BlockServiceError: failed to get block from datastore";
62+
case BlockServiceError::REMOVE_BLOCK_FAILED:
63+
return "BlockServiceError: failed to remove block from datastore";
64+
}
65+
return "BlockServiceError: unknown error";
66+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef FILECOIN_STORAGE_IPFS_BLOCKSERVICE_IMPL_HPP
7+
#define FILECOIN_STORAGE_IPFS_BLOCKSERVICE_IMPL_HPP
8+
9+
#include <memory>
10+
11+
#include "storage/ipfs/blockservice.hpp"
12+
13+
namespace fc::storage::ipfs {
14+
class BlockServiceImpl : public BlockService {
15+
public:
16+
/**
17+
* @brief Construct block service
18+
* @param data_store - IPFS storage implementation
19+
*/
20+
explicit BlockServiceImpl(std::shared_ptr<IpfsDatastore> data_store);
21+
22+
outcome::result<void> addBlock(const Block &block) override;
23+
24+
outcome::result<bool> has(const CID &cid) const override;
25+
26+
outcome::result<Block::Content> getBlockContent(
27+
const CID &cid) const override;
28+
29+
outcome::result<void> removeBlock(const CID &cid) override;
30+
31+
private:
32+
std::shared_ptr<IpfsDatastore> local_storage_; /**< Local data storage */
33+
};
34+
} // namespace fc::storage::ipfs
35+
36+
#endif

test/core/storage/ipfs/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ target_link_libraries(in_memory_ipfs_datastore_test
1616
p2p::p2p_random_generator
1717
ipfs_datastore_in_memory
1818
)
19+
20+
addtest(ipfs_blockservice_test
21+
blockservice_test.cpp
22+
)
23+
target_link_libraries(ipfs_blockservice_test
24+
ipfs_blockservice
25+
ipfs_datastore_in_memory
26+
)
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#include "storage/ipfs/impl/blockservice_impl.hpp"
6+
7+
#include <memory>
8+
9+
#include <gtest/gtest.h>
10+
#include "storage/ipfs/impl/in_memory_datastore.hpp"
11+
#include "testutil/outcome.hpp"
12+
13+
using fc::CID;
14+
using fc::common::Buffer;
15+
using fc::common::getCidOf;
16+
using fc::common::kEmptyCid;
17+
using fc::storage::ipfs::Block;
18+
using fc::storage::ipfs::BlockServiceImpl;
19+
using fc::storage::ipfs::BlockServiceError;
20+
using fc::storage::ipfs::InMemoryDatastore;
21+
22+
/**
23+
* @brief Implementation of Block interface for testing purposes
24+
* This interface can be used by any data structure (like Node from
25+
* MerkleDAG service) and there are no single universal implementation
26+
*/
27+
struct BlockTestImpl : Block {
28+
Content content; /**< Raw data */
29+
CID cid; /**< Block identifier */
30+
31+
/**
32+
* @brief Construct Block
33+
* @param data - raw content
34+
*/
35+
BlockTestImpl(std::vector<uint8_t> data)
36+
: content{data}, cid{getCidOf(content).value()} {}
37+
38+
/**
39+
* @brief Get content identifier
40+
* @return Block's CID
41+
*/
42+
const CID &getCID() const override {
43+
return this->cid;
44+
}
45+
46+
/**
47+
* @brief Get block's content
48+
* @details Type of the return value depends on Block's interface
49+
* implementation: it can be raw bytes, or CBOR/Protobuf serialized value
50+
* @return Block's data
51+
*/
52+
const Buffer &getContent() const override {
53+
return this->content;
54+
}
55+
};
56+
57+
/**
58+
* @class Test fixture for BlockService
59+
*/
60+
class BlockServiceTest : public ::testing::Test {
61+
public:
62+
/**
63+
* @brief Initialize BlockService
64+
*/
65+
BlockServiceTest() : block_service_(std::make_shared<InMemoryDatastore>()) {}
66+
67+
protected:
68+
69+
BlockServiceImpl block_service_; /**< Testing target */
70+
71+
BlockTestImpl sample_block_{
72+
{4, 8, 15, 16, 23, 42}}; /**< Sample block with pre-defined data */
73+
};
74+
75+
/**
76+
* @given Sample block with pre-defined data
77+
* @when Adding, checking existence and retrieving block back from BlockStorage
78+
* @then Add block @and check its existence @and retrieve block back from BlockStorage
79+
*/
80+
TEST_F(BlockServiceTest, StoreBlockSuccess) {
81+
EXPECT_OUTCOME_TRUE_1(block_service_.addBlock(sample_block_))
82+
EXPECT_OUTCOME_TRUE(contains, block_service_.has(sample_block_.getCID()))
83+
ASSERT_TRUE(contains);
84+
EXPECT_OUTCOME_TRUE(block_content, block_service_.getBlockContent(sample_block_.getCID()))
85+
ASSERT_EQ(block_content, sample_block_.getContent());
86+
}
87+
88+
/**
89+
* @given CID of the block, which doesn't exist in the BlockService
90+
* @when Trying to check block existence in the BlockService
91+
* @then Operation must be completed successfully with result "not exists"
92+
*/
93+
TEST_F(BlockServiceTest, CheckExistenceSuccess) {
94+
EXPECT_OUTCOME_TRUE(contains, block_service_.has(sample_block_.getCID()))
95+
ASSERT_FALSE(contains);
96+
}
97+
98+
/**
99+
* @given Sample block with pre-defined data
100+
* @when Trying to remove an existence block
101+
* @then Operation must be completed successfully
102+
*/
103+
TEST_F(BlockServiceTest, RemoveBlockSuccess) {
104+
EXPECT_OUTCOME_TRUE_1(block_service_.addBlock(sample_block_))
105+
EXPECT_OUTCOME_TRUE(block_status, block_service_.has(sample_block_.getCID()))
106+
ASSERT_TRUE(block_status);
107+
EXPECT_OUTCOME_TRUE_1(block_service_.removeBlock(sample_block_.getCID()))
108+
EXPECT_OUTCOME_TRUE(removed_status, block_service_.has(sample_block_.getCID()))
109+
ASSERT_FALSE(removed_status);
110+
}
111+
112+
/**
113+
* @given CID of the block, which doesn't exist in the BlockService
114+
* @when Try to get nonexistent block from BlockService
115+
* @then Attempt fails
116+
*/
117+
TEST_F(BlockServiceTest, GetInvalidCidFailure) {
118+
BlockServiceError expected_error = BlockServiceError::CID_NOT_FOUND;
119+
EXPECT_OUTCOME_FALSE(result, block_service_.getBlockContent(sample_block_.getCID()))
120+
ASSERT_EQ(result.value(), static_cast<int>(expected_error));
121+
}

0 commit comments

Comments
 (0)