From be23e1fd7787af7ce16c5cd031e7bedb44cbfdcb Mon Sep 17 00:00:00 2001 From: Sergey Kaprovich Date: Tue, 3 Mar 2020 13:45:06 +0300 Subject: [PATCH] Chain: block producer --- core/blockchain/CMakeLists.txt | 1 + core/blockchain/production/CMakeLists.txt | 18 ++ .../blockchain/production/block_generator.hpp | 27 --- core/blockchain/production/block_producer.hpp | 53 ++++++ .../production/impl/block_producer_impl.cpp | 161 ++++++++++++++++++ .../production/impl/block_producer_impl.hpp | 85 +++++++++ core/crypto/bls/bls_provider.hpp | 11 +- core/crypto/bls/bls_types.hpp | 3 +- core/crypto/bls/impl/bls_provider_impl.cpp | 20 +++ core/crypto/bls/impl/bls_provider_impl.hpp | 3 + core/primitives/block/block.hpp | 20 ++- core/primitives/ticket/CMakeLists.txt | 1 + core/storage/amt/CMakeLists.txt | 1 + core/storage/amt/amt.hpp | 10 +- .../merkledag/impl/merkledag_service_impl.cpp | 3 +- core/storage/ipld/CMakeLists.txt | 7 +- core/storage/ipld/impl/ipld_block_impl.cpp | 40 ----- core/storage/ipld/impl/ipld_block_impl.hpp | 58 ------- .../ipld/impl/ipld_node_encoder_pb.cpp | 22 ++- .../ipld/impl/ipld_node_encoder_pb.hpp | 2 +- core/storage/ipld/impl/ipld_node_impl.cpp | 6 +- core/storage/ipld/impl/ipld_node_impl.hpp | 11 +- core/storage/ipld/ipld_block.hpp | 11 +- core/storage/ipld/ipld_block_common.hpp | 72 ++++++++ core/vm/interpreter/CMakeLists.txt | 2 +- .../interpreter_impl.cpp} | 51 +++--- core/vm/interpreter/impl/interpreter_impl.hpp | 33 ++++ core/vm/interpreter/interpreter.hpp | 22 ++- core/vm/message/CMakeLists.txt | 1 + core/vm/message/message.hpp | 45 ++++- core/vm/message/message_codec.hpp | 29 ---- core/vm/message/message_util.hpp | 2 +- core/vm/runtime/impl/env.cpp | 1 - core/vm/runtime/impl/runtime_impl.cpp | 1 - test/core/blockchain/CMakeLists.txt | 1 + .../core/blockchain/production/CMakeLists.txt | 11 ++ .../production/block_producer_test.cpp | 157 +++++++++++++++++ .../production/block_producer_test.hpp | 92 ++++++++++ .../vm/actor/builtin/cron/cron_actor_test.cpp | 4 +- .../storage_power_actor_test.cpp | 8 +- test/core/vm/actor/invoker_test.cpp | 2 +- test/core/vm/runtime/runtime_test.cpp | 2 +- .../message_pool/message_storage_mock.hpp | 24 +++ .../blockchain/weight_calculator_mock.hpp | 21 +++ test/testutil/mocks/clock/utc_clock_mock.hpp | 21 +++ .../mocks/crypto/bls/bls_provider_mock.hpp | 34 ++++ .../storage/ipfs/ipfs_datastore_mock.hpp | 4 + .../mocks/vm/interpreter/interpreter_mock.hpp | 24 +++ 48 files changed, 999 insertions(+), 239 deletions(-) create mode 100644 core/blockchain/production/CMakeLists.txt delete mode 100644 core/blockchain/production/block_generator.hpp create mode 100644 core/blockchain/production/block_producer.hpp create mode 100644 core/blockchain/production/impl/block_producer_impl.cpp create mode 100644 core/blockchain/production/impl/block_producer_impl.hpp delete mode 100644 core/storage/ipld/impl/ipld_block_impl.cpp delete mode 100644 core/storage/ipld/impl/ipld_block_impl.hpp create mode 100644 core/storage/ipld/ipld_block_common.hpp rename core/vm/interpreter/{interpreter.cpp => impl/interpreter_impl.cpp} (92%) create mode 100644 core/vm/interpreter/impl/interpreter_impl.hpp delete mode 100644 core/vm/message/message_codec.hpp create mode 100644 test/core/blockchain/production/CMakeLists.txt create mode 100644 test/core/blockchain/production/block_producer_test.cpp create mode 100644 test/core/blockchain/production/block_producer_test.hpp create mode 100644 test/testutil/mocks/blockchain/message_pool/message_storage_mock.hpp create mode 100644 test/testutil/mocks/blockchain/weight_calculator_mock.hpp create mode 100644 test/testutil/mocks/clock/utc_clock_mock.hpp create mode 100644 test/testutil/mocks/crypto/bls/bls_provider_mock.hpp create mode 100644 test/testutil/mocks/vm/interpreter/interpreter_mock.hpp diff --git a/core/blockchain/CMakeLists.txt b/core/blockchain/CMakeLists.txt index 0123b9c910..67c34bffe8 100644 --- a/core/blockchain/CMakeLists.txt +++ b/core/blockchain/CMakeLists.txt @@ -4,6 +4,7 @@ # add_subdirectory(message_pool) +add_subdirectory(production) add_library(block_validator impl/block_validator_impl.cpp diff --git a/core/blockchain/production/CMakeLists.txt b/core/blockchain/production/CMakeLists.txt new file mode 100644 index 0000000000..312f33ad83 --- /dev/null +++ b/core/blockchain/production/CMakeLists.txt @@ -0,0 +1,18 @@ +# +#Copyright Soramitsu Co., Ltd.All Rights Reserved. +#SPDX - License - Identifier : Apache - 2.0 +# + +add_library(block_producer + impl/block_producer_impl.cpp + ) +target_link_libraries(block_producer + bls_provider + outcome + buffer + address + clock + interpreter + tipset + ipfs_datastore_in_memory + ) diff --git a/core/blockchain/production/block_generator.hpp b/core/blockchain/production/block_generator.hpp deleted file mode 100644 index 97dbe1d202..0000000000 --- a/core/blockchain/production/block_generator.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "blockchain/message_pool/message_storage.hpp" -#include "storage/ipfs/datastore.hpp" -#include "primitives/tipset/tipset.hpp" -#include "primitives/block/block.hpp" -#include "primitives/cid/cid.hpp" - -namespace fc::blockchain::production { - class BlockGenerator { - using fc::blockchain::message_pool::MessageStorage; - using fc::primitives::tipset::Tipset; - using fc::storage::ipfs::IpfsDataStore; - using primitives::block::BlockHeader; - - public: - BlockGenerator(std::shared_ptr data_store, - std::shared_ptr messages_store); - - - }; -} // namespace fc::blockchain::production diff --git a/core/blockchain/production/block_producer.hpp b/core/blockchain/production/block_producer.hpp new file mode 100644 index 0000000000..d3a7a0042f --- /dev/null +++ b/core/blockchain/production/block_producer.hpp @@ -0,0 +1,53 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/outcome.hpp" +#include "primitives/address/address.hpp" +#include "primitives/block/block.hpp" +#include "primitives/ticket/epost_ticket.hpp" +#include "primitives/ticket/ticket.hpp" +#include "vm/indices/indices.hpp" + +namespace fc::blockchain::production { + /** + * @class Generating new blocks for chain's tipset + */ + class BlockProducer { + protected: + using Block = primitives::block::Block; + using Address = primitives::address::Address; + using EPostProof = primitives::ticket::EPostProof; + using Ticket = primitives::ticket::Ticket; + using Indices = vm::indices::Indices; + + public: + virtual ~BlockProducer() = default; + + /** + * @brief Generate new block + * @param miner_address - source address + * @param parent_tipset_id - id of the parent tipset + * @param proof - evidence of mining permission + * @param ticket - used ticket for election round + * @return Generated full block + */ + virtual outcome::result generate( + Address miner_address, + const CID &parent_tipset_id, + EPostProof proof, + Ticket ticket, + std::shared_ptr indices) = 0; + }; + + /** + * @enum Block production errors + */ + enum class BlockProducerError { + PARENT_TIPSET_NOT_FOUND = 1, + PARENT_TIPSET_INVALID_CONTENT + }; +} // namespace fc::blockchain::production + +OUTCOME_HPP_DECLARE_ERROR(fc::blockchain::production, BlockProducerError); diff --git a/core/blockchain/production/impl/block_producer_impl.cpp b/core/blockchain/production/impl/block_producer_impl.cpp new file mode 100644 index 0000000000..4290bd0b02 --- /dev/null +++ b/core/blockchain/production/impl/block_producer_impl.cpp @@ -0,0 +1,161 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "blockchain/production/impl/block_producer_impl.hpp" + +#include + +#include +#include "clock/chain_epoch_clock.hpp" +#include "codec/cbor/cbor.hpp" +#include "common/visitor.hpp" +#include "primitives/cid/cid_of_cbor.hpp" +#include "storage/amt/amt.hpp" +#include "storage/ipfs/impl/in_memory_datastore.hpp" + +namespace fc::blockchain::production { + using clock::Time; + using codec::cbor::decode; + using crypto::signature::BlsSignature; + using crypto::signature::Secp256k1Signature; + using primitives::block::BlockHeader; + using storage::amt::Amt; + using storage::amt::Root; + using storage::ipfs::InMemoryDatastore; + using vm::message::SignedMessage; + using vm::message::UnsignedMessage; + + BlockProducerImpl::BlockProducerImpl( + std::shared_ptr data_store, + std::shared_ptr message_store, + std::shared_ptr utc_clock, + std::shared_ptr epoch_clock, + std::shared_ptr weight_calculator, + std::shared_ptr crypto_provider, + std::shared_ptr interpreter) + : data_storage_{std::move(data_store)}, + message_storage_{std::move(message_store)}, + clock_{std::move(utc_clock)}, + epoch_{std::move(epoch_clock)}, + chain_weight_calculator_{std::move(weight_calculator)}, + bls_provider_{std::move(crypto_provider)}, + vm_interpreter_{std::move(interpreter)} {} + + outcome::result BlockProducerImpl::generate( + primitives::address::Address miner_address, + const CID &parent_tipset_id, + EPostProof proof, + Ticket ticket, + std::shared_ptr indices) { + OUTCOME_TRY(parent_tipset, getTipset(parent_tipset_id)); + OUTCOME_TRY( + vm_result, + vm_interpreter_->interpret(data_storage_, parent_tipset, indices)); + OUTCOME_TRY(parent_weight, + chain_weight_calculator_->calculateWeight(parent_tipset)); + std::vector messages = + message_storage_->getTopScored(config::kBlockMaxMessagesCount); + OUTCOME_TRY(msg_meta, getMessagesMeta(messages)); + std::vector bls_messages; + std::vector secp_messages; + std::vector bls_signatures; + for (auto &&message : messages) { + visit_in_place( + message.signature, + [&message, &bls_messages, &bls_signatures]( + const BlsSignature &signature) { + bls_messages.emplace_back(std::move(message.message)); + bls_signatures.push_back(signature); + }, + [&message, &secp_messages](const Secp256k1Signature &signature) { + secp_messages.emplace_back(std::move(message)); + }); + } + OUTCOME_TRY(bls_aggregate_sign, + bls_provider_->aggregateSignatures(bls_signatures)); + Time now = clock_->nowUTC(); + OUTCOME_TRY(current_epoch, epoch_->epochAtTime(now)); + BlockHeader header{ + .miner = std::move(miner_address), + .ticket = ticket, + .epost_proof = std::move(proof), + .parents = std::move(parent_tipset.cids), + .parent_weight = parent_weight, + .height = static_cast(current_epoch), + .parent_state_root = std::move(vm_result.state_root), + .parent_message_receipts = std::move(vm_result.message_receipts), + .messages = msg_meta.getCID(), + .bls_aggregate = {std::move_iterator(bls_aggregate_sign.begin()), + std::move_iterator(bls_aggregate_sign.end())}, + .timestamp = static_cast(now.unixTime().count()), + .block_sig = {}, // Block must be signed be Actor Miner + .fork_signaling = 0}; + return Block{.header = std::move(header), + .bls_messages = std::move(bls_messages), + .secp_messages = std::move(secp_messages)}; + } + + outcome::result BlockProducerImpl::getTipset( + const CID &tipset_id) const { + auto raw_data = data_storage_->get(tipset_id); + if (raw_data.has_error()) { + return BlockProducerError::PARENT_TIPSET_NOT_FOUND; + } + auto tipset = codec::cbor::decode(raw_data.value()); + if (tipset.has_error()) { + return BlockProducerError::PARENT_TIPSET_INVALID_CONTENT; + } + return tipset.value(); + } + + outcome::result + BlockProducerImpl::getMessagesMeta( + const std::vector &messages) { + auto bls_backend = std::make_shared(); + auto secp_backend = std::make_shared(); + Amt bls_messages_amt{bls_backend}; + Amt secp_messages_amt{secp_backend}; + std::vector bls_messages; + std::vector secp_messages; + for (const auto &msg : messages) { + visit_in_place( + msg.signature, + [&msg, &bls_messages](const BlsSignature &signature) { + std::ignore = signature; + bls_messages.push_back(msg); + }, + [&msg, &secp_messages](const Secp256k1Signature &signature) { + std::ignore = signature; + secp_messages.push_back(msg); + }); + } + for (size_t index = 0; index < bls_messages.size(); ++index) { + OUTCOME_TRY(message_cid, + primitives::cid::getCidOfCbor(bls_messages.at(index))); + OUTCOME_TRY(bls_messages_amt.setCbor(index, message_cid)); + } + for (size_t index = 0; index < secp_messages.size(); ++index) { + OUTCOME_TRY(message_cid, + primitives::cid::getCidOfCbor(secp_messages.at(index))); + OUTCOME_TRY(secp_messages_amt.setCbor(index, message_cid)); + } + OUTCOME_TRY(bls_root_cid, bls_messages_amt.flush()); + OUTCOME_TRY(secp_root_cid, secp_messages_amt.flush()); + MsgMeta msg_meta{}; + msg_meta.bls_messages = bls_root_cid; + msg_meta.secpk_messages = secp_root_cid; + return msg_meta; + } +} // namespace fc::blockchain::production + +OUTCOME_CPP_DEFINE_CATEGORY(fc::blockchain::production, BlockProducerError, e) { + using fc::blockchain::production::BlockProducerError; + switch (e) { + case (BlockProducerError::PARENT_TIPSET_NOT_FOUND): + return "Block Generator: failed to load parent tipset"; + case (BlockProducerError::PARENT_TIPSET_INVALID_CONTENT): + return "Block Generator: failed to decode parent tipset content"; + } +} diff --git a/core/blockchain/production/impl/block_producer_impl.hpp b/core/blockchain/production/impl/block_producer_impl.hpp new file mode 100644 index 0000000000..75c0098590 --- /dev/null +++ b/core/blockchain/production/impl/block_producer_impl.hpp @@ -0,0 +1,85 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "blockchain/message_pool/message_storage.hpp" +#include "blockchain/production/block_producer.hpp" +#include "blockchain/weight_calculator.hpp" +#include "clock/chain_epoch_clock.hpp" +#include "clock/utc_clock.hpp" +#include "crypto/bls/bls_provider.hpp" +#include "primitives/block/block.hpp" +#include "primitives/tipset/tipset.hpp" +#include "storage/ipfs/datastore.hpp" +#include "vm/interpreter/interpreter.hpp" + +namespace fc::blockchain::production { + namespace config { + // Max messages count in the Block + constexpr size_t kBlockMaxMessagesCount = 1000; + } // namespace config + + /** + * @class Block Generator implementation + */ + class BlockProducerImpl : public BlockProducer { + protected: + using MessageStorage = blockchain::message_pool::MessageStorage; + using WeightCalculator = blockchain::weight::WeightCalculator; + using ChainEpochClock = clock::ChainEpochClock; + using UTCClock = clock::UTCClock; + using BlsProvider = crypto::bls::BlsProvider; + using MsgMeta = primitives::block::MsgMeta; + using Tipset = primitives::tipset::Tipset; + using IpfsDatastore = storage::ipfs::IpfsDatastore; + using SignedMessage = vm::message::SignedMessage; + using Interpreter = vm::interpreter::Interpreter; + + public: + /** + * @brief Construct new Block Generator + * @param data_store + */ + explicit BlockProducerImpl( + std::shared_ptr data_store, + std::shared_ptr message_store, + std::shared_ptr utc_clock, + std::shared_ptr epoch_clock, + std::shared_ptr weight_calculator, + std::shared_ptr crypto_provider, + std::shared_ptr interpreter); + + outcome::result generate(Address miner_address, + const CID &parent_tipset_id, + EPostProof proof, + Ticket ticket, + std::shared_ptr indices) override; + + private: + std::shared_ptr data_storage_; + std::shared_ptr message_storage_; + std::shared_ptr clock_; + std::shared_ptr epoch_; + std::shared_ptr chain_weight_calculator_; + std::shared_ptr bls_provider_; + std::shared_ptr vm_interpreter_; + + /** + * @brief Load tipset from IPFS storage by CID + * @param tipset_id - CID of the tipset + * @return requested tipset or appropriate error + */ + outcome::result getTipset(const CID &tipset_id) const; + + /** + * @brief Generate messages meta-data + * @param messages - messages to include in the new Block + * @return Generated meta-data + */ + static outcome::result getMessagesMeta( + const std::vector &messages); + }; +} // namespace fc::blockchain::production diff --git a/core/crypto/bls/bls_provider.hpp b/core/crypto/bls/bls_provider.hpp index f7df76d028..60320dfed0 100644 --- a/core/crypto/bls/bls_provider.hpp +++ b/core/crypto/bls/bls_provider.hpp @@ -6,8 +6,9 @@ #ifndef CRYPTO_BLS_PROVIDER_HPP #define CRYPTO_BLS_PROVIDER_HPP -#include +#include +#include #include "crypto/bls/bls_types.hpp" namespace fc::crypto::bls { @@ -51,6 +52,14 @@ namespace fc::crypto::bls { gsl::span message, const Signature &signature, const PublicKey &key) const = 0; + + /** + * @brief Aggregate BLS signatures + * @param signatures - signatures to aggregate + * @return aggregated single signature + */ + virtual outcome::result aggregateSignatures( + const std::vector &signatures) const = 0; }; } // namespace fc::crypto::bls diff --git a/core/crypto/bls/bls_types.hpp b/core/crypto/bls/bls_types.hpp index c9848f2674..d9dec6f2d9 100644 --- a/core/crypto/bls/bls_types.hpp +++ b/core/crypto/bls/bls_types.hpp @@ -32,7 +32,8 @@ namespace fc::crypto::bls { SignatureGenerationFailed, SignatureVerificationFailed, InvalidPrivateKey, - InvalidPublicKey + InvalidPublicKey, + AggregateError }; }; diff --git a/core/crypto/bls/impl/bls_provider_impl.cpp b/core/crypto/bls/impl/bls_provider_impl.cpp index ba68e7b71e..52767f0c8a 100644 --- a/core/crypto/bls/impl/bls_provider_impl.cpp +++ b/core/crypto/bls/impl/bls_provider_impl.cpp @@ -86,6 +86,24 @@ namespace fc::crypto::bls { digest.begin()); return digest; } + + outcome::result BlsProviderImpl::aggregateSignatures( + const std::vector &signatures) const { + Signature signature; + const uint8_t *flat_bytes = + reinterpret_cast(signatures.data()); + size_t flat_size = sizeof(Signature) * signatures.size(); + AggregateResponse *response = ::aggregate(flat_bytes, flat_size); + if (response == nullptr) { + return Errors::InternalError; + } + auto free_response = + gsl::finally([response]() { destroy_aggregate_response(response); }); + std::move(std::begin(response->signature), + std::end(response->signature), + signature.begin()); + return signature; + } }; // namespace fc::crypto::bls OUTCOME_CPP_DEFINE_CATEGORY(fc::crypto::bls, Errors, e) { @@ -103,6 +121,8 @@ OUTCOME_CPP_DEFINE_CATEGORY(fc::crypto::bls, Errors, e) { return "BLS provider: signature generation failed"; case (Errors::SignatureVerificationFailed): return "BLS provider: signature verification failed"; + case (Errors::AggregateError): + return "BLS provider: signatures aggregating failed"; default: return "BLS provider: unknown error"; } diff --git a/core/crypto/bls/impl/bls_provider_impl.hpp b/core/crypto/bls/impl/bls_provider_impl.hpp index 4d2b108630..98e83da0fc 100644 --- a/core/crypto/bls/impl/bls_provider_impl.hpp +++ b/core/crypto/bls/impl/bls_provider_impl.hpp @@ -23,6 +23,9 @@ namespace fc::crypto::bls { const Signature &signature, const PublicKey &key) const override; + outcome::result aggregateSignatures( + const std::vector &signatures) const override; + private: /** * @brief Generate BLS message digest diff --git a/core/primitives/block/block.hpp b/core/primitives/block/block.hpp index 7bc5763b61..635feb7655 100644 --- a/core/primitives/block/block.hpp +++ b/core/primitives/block/block.hpp @@ -6,6 +6,7 @@ #ifndef CPP_FILECOIN_CORE_PRIMITIVES_BLOCK_BLOCK_HPP #define CPP_FILECOIN_CORE_PRIMITIVES_BLOCK_BLOCK_HPP +#include #include #include "codec/cbor/streams_annotation.hpp" @@ -18,6 +19,8 @@ #include "primitives/ticket/epost_ticket_codec.hpp" #include "primitives/ticket/ticket.hpp" #include "primitives/ticket/ticket_codec.hpp" +#include "storage/ipld/ipld_block_common.hpp" +#include "vm/message/message.hpp" namespace fc::primitives::block { using primitives::BigInt; @@ -26,6 +29,9 @@ namespace fc::primitives::block { using primitives::ticket::Ticket; // TODO (yuraz) : FIL-142 replace by crypto::signature::Signature using Signature = std::vector; + using storage::ipld::IPLDBlockCommon; + using vm::message::SignedMessage; + using vm::message::UnsignedMessage; struct BlockHeader { Address miner; @@ -43,9 +49,21 @@ namespace fc::primitives::block { uint64_t fork_signaling; }; - struct MsgMeta { + struct MsgMeta : public IPLDBlockCommon { CID bls_messages; CID secpk_messages; + + outcome::result> getBlockContent() const override { + return codec::cbor::encode(*this); + } + }; + + struct Block { + BlockHeader header; + std::vector bls_messages; + std::vector secp_messages; }; inline bool operator==(const BlockHeader &lhs, const BlockHeader &rhs) { diff --git a/core/primitives/ticket/CMakeLists.txt b/core/primitives/ticket/CMakeLists.txt index a65ea05ce3..7e9eaa278e 100644 --- a/core/primitives/ticket/CMakeLists.txt +++ b/core/primitives/ticket/CMakeLists.txt @@ -12,4 +12,5 @@ target_link_libraries(tickets buffer cbor p2p::p2p_sha + address ) diff --git a/core/storage/amt/CMakeLists.txt b/core/storage/amt/CMakeLists.txt index 7cf0f6640f..731554e85a 100644 --- a/core/storage/amt/CMakeLists.txt +++ b/core/storage/amt/CMakeLists.txt @@ -10,4 +10,5 @@ target_link_libraries(amt cbor cid outcome + ipld_block ) diff --git a/core/storage/amt/amt.hpp b/core/storage/amt/amt.hpp index 07d08d07e0..4f640ae522 100644 --- a/core/storage/amt/amt.hpp +++ b/core/storage/amt/amt.hpp @@ -14,6 +14,7 @@ #include "common/which.hpp" #include "primitives/cid/cid.hpp" #include "storage/ipfs/datastore.hpp" +#include "storage/ipld/ipld_block_common.hpp" namespace fc::storage::amt { enum class AmtError { @@ -32,6 +33,7 @@ namespace fc::storage::amt { using common::which; using Value = ipfs::IpfsDatastore::Value; + using ipld::IPLDBlockCommon; struct Node { using Ptr = std::shared_ptr; @@ -117,10 +119,16 @@ namespace fc::storage::amt { return s; } - struct Root { + struct Root : public IPLDBlockCommon { uint64_t height{}; uint64_t count{}; Node node; + + outcome::result> getBlockContent() const override { + return codec::cbor::encode(*this); + } }; CBOR_TUPLE(Root, height, count, node) diff --git a/core/storage/ipfs/merkledag/impl/merkledag_service_impl.cpp b/core/storage/ipfs/merkledag/impl/merkledag_service_impl.cpp index c35bcad38e..f341ee766c 100644 --- a/core/storage/ipfs/merkledag/impl/merkledag_service_impl.cpp +++ b/core/storage/ipfs/merkledag/impl/merkledag_service_impl.cpp @@ -23,7 +23,8 @@ namespace fc::storage::ipfs::merkledag { outcome::result MerkleDagServiceImpl::addNode( std::shared_ptr node) { - return block_service_->set(node->getCID(), node->getRawBytes()); + const common::Buffer &raw_bytes = node->getRawBytes(); + return block_service_->set(node->getCID(), raw_bytes); } outcome::result> MerkleDagServiceImpl::getNode( diff --git a/core/storage/ipld/CMakeLists.txt b/core/storage/ipld/CMakeLists.txt index 0bba57f0f9..37e4fbcbf8 100644 --- a/core/storage/ipld/CMakeLists.txt +++ b/core/storage/ipld/CMakeLists.txt @@ -22,10 +22,8 @@ target_link_libraries(ipld_node_protobuf ) disable_clang_tidy(ipld_node_protobuf) -add_library(ipld_block - impl/ipld_block_impl.cpp - ) -target_link_libraries(ipld_block +add_library(ipld_block INTERFACE) +target_link_libraries(ipld_block INTERFACE cid filecoin_hasher ) @@ -37,7 +35,6 @@ target_link_libraries(ipld_link cid ) - add_library(ipld_node impl/ipld_node_impl.cpp impl/ipld_node_encoder_pb.cpp diff --git a/core/storage/ipld/impl/ipld_block_impl.cpp b/core/storage/ipld/impl/ipld_block_impl.cpp deleted file mode 100644 index 63b0a5db36..0000000000 --- a/core/storage/ipld/impl/ipld_block_impl.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "storage/ipld/impl/ipld_block_impl.hpp" - -#include "crypto/hasher/hasher.hpp" - -namespace fc::storage::ipld { - - IPLDBlockImpl::IPLDBlockImpl(CID::Version version, - HashType hash_type, - ContentType content_type) - : cid_version_{version}, - cid_hash_type_{hash_type}, - content_type_{content_type} {} - - const CID &IPLDBlockImpl::getCID() const { - if (!cid_) { - auto multi_hash = - crypto::Hasher::calculate(cid_hash_type_, getRawBytes()); - CID id{cid_version_, content_type_, std::move(multi_hash)}; - cid_ = std::move(id); - } - return cid_.value(); - } - - const common::Buffer &IPLDBlockImpl::getRawBytes() const { - if (!raw_bytes_) { - raw_bytes_ = serialize(); - } - return raw_bytes_.value(); - } - - void IPLDBlockImpl::clearCache() const { - cid_ = boost::none; - raw_bytes_ = boost::none; - } -} // namespace fc::storage::ipld diff --git a/core/storage/ipld/impl/ipld_block_impl.hpp b/core/storage/ipld/impl/ipld_block_impl.hpp deleted file mode 100644 index 7bf61c74b6..0000000000 --- a/core/storage/ipld/impl/ipld_block_impl.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef FILECOIN_STORAGE_IPLD_BLOCK_IMPL_HPP -#define FILECOIN_STORAGE_IPLD_BLOCK_IMPL_HPP - -#include -#include - -#include -#include -#include -#include "common/buffer.hpp" -#include "primitives/cid/cid.hpp" -#include "storage/ipld/ipld_block.hpp" - -namespace fc::storage::ipld { - using libp2p::multi::HashType; - using ContentType = libp2p::multi::MulticodecType::Code; - - /** - * @class IPLD data structure implementation - */ - class IPLDBlockImpl : public virtual IPLDBlock { - public: - /** - * @brief Construct IPLD block - * @param version - CID version - * @param hash_type - CID hash type - * @param content_type - block content type - */ - IPLDBlockImpl(CID::Version version, - HashType hash_type, - ContentType content_type); - - const CID &getCID() const override; - - const common::Buffer &getRawBytes() const override; - - protected: - /** - * @brief Clear cached CID and serialized values - */ - void clearCache() const; - - private: - CID::Version cid_version_; - HashType cid_hash_type_; - ContentType content_type_; - - mutable boost::optional cid_; - mutable boost::optional raw_bytes_; - }; -} // namespace fc::storage::ipld - -#endif diff --git a/core/storage/ipld/impl/ipld_node_encoder_pb.cpp b/core/storage/ipld/impl/ipld_node_encoder_pb.cpp index c27a96c5be..e2d8f52614 100644 --- a/core/storage/ipld/impl/ipld_node_encoder_pb.cpp +++ b/core/storage/ipld/impl/ipld_node_encoder_pb.cpp @@ -15,19 +15,23 @@ using protobuf::ipld::node::PBLink; using protobuf::ipld::node::PBNode; namespace fc::storage::ipld { - common::Buffer IPLDNodeEncoderPB::encode( + std::vector IPLDNodeEncoderPB::encode( const common::Buffer &content, const std::map &links) { - common::Buffer data; std::vector links_pb = serializeLinks(links); std::vector content_pb = serializeContent(content); - data.put(links_pb); - data.put(content_pb); - return data; + std::vector result; + result.insert(result.end(), + std::move_iterator(links_pb.begin()), + std::move_iterator(links_pb.end())); + result.insert(result.end(), + std::move_iterator(content_pb.begin()), + std::move_iterator(content_pb.end())); + return result; } size_t IPLDNodeEncoderPB::getLinkLengthPB(const std::string &name, - const IPLDLinkImpl &link) { + const IPLDLinkImpl &link) { size_t length{}; size_t cid_bytes_size = link.getCID().content_address.toBuffer().size(); length += cid_bytes_size; @@ -112,11 +116,11 @@ namespace fc::storage::ipld { return length; } - IPLDNodeEncoderPB::PBTag IPLDNodeEncoderPB::createTag(PBFieldType type, - uint8_t order) { + IPLDNodeEncoderPB::PBTag IPLDNodeEncoderPB::createTag(PBFieldType type, + uint8_t order) { constexpr size_t pb_type_length = 3; uint8_t tag = (order << pb_type_length); tag |= static_cast(type); return tag; } -} // namespace fc::storage::ipfs::merkledag +} // namespace fc::storage::ipld diff --git a/core/storage/ipld/impl/ipld_node_encoder_pb.hpp b/core/storage/ipld/impl/ipld_node_encoder_pb.hpp index 3474e15b26..9a85419018 100644 --- a/core/storage/ipld/impl/ipld_node_encoder_pb.hpp +++ b/core/storage/ipld/impl/ipld_node_encoder_pb.hpp @@ -32,7 +32,7 @@ namespace fc::storage::ipld { * @param links - references for child Nodes * @return Protobuf-encoded data */ - static common::Buffer encode(const common::Buffer &content, + static std::vector encode(const common::Buffer &content, const std::map &links); private: diff --git a/core/storage/ipld/impl/ipld_node_impl.cpp b/core/storage/ipld/impl/ipld_node_impl.cpp index e84e865b2b..23ed76ae0b 100644 --- a/core/storage/ipld/impl/ipld_node_impl.cpp +++ b/core/storage/ipld/impl/ipld_node_impl.cpp @@ -23,10 +23,6 @@ using Version = libp2p::multi::ContentIdentifier::Version; namespace fc::storage::ipld { - IPLDNodeImpl::IPLDNodeImpl() - : IPLDBlockImpl{CID::Version::V0, HashType::sha256, ContentType::DAG_PB} { - } - size_t IPLDNodeImpl::size() const { return getRawBytes().size() + child_nodes_size_; } @@ -104,7 +100,7 @@ namespace fc::storage::ipld { return node; } - common::Buffer IPLDNodeImpl::serialize() const { + outcome::result> IPLDNodeImpl::getBlockContent() const { return IPLDNodeEncoderPB::encode(content_, links_); } } // namespace fc::storage::ipld diff --git a/core/storage/ipld/impl/ipld_node_impl.hpp b/core/storage/ipld/impl/ipld_node_impl.hpp index eaf8595ae6..d76c841f29 100644 --- a/core/storage/ipld/impl/ipld_node_impl.hpp +++ b/core/storage/ipld/impl/ipld_node_impl.hpp @@ -12,16 +12,17 @@ #include #include -#include "storage/ipld/impl/ipld_block_impl.hpp" #include "storage/ipld/impl/ipld_link_impl.hpp" #include "storage/ipld/impl/ipld_node_encoder_pb.hpp" +#include "storage/ipld/ipld_block_common.hpp" #include "storage/ipld/ipld_node.hpp" namespace fc::storage::ipld { - class IPLDNodeImpl : public IPLDNode, IPLDBlockImpl { + class IPLDNodeImpl : public IPLDNode, + IPLDBlockCommon { public: - IPLDNodeImpl(); - size_t size() const override; void assign(common::Buffer input) override; @@ -53,7 +54,7 @@ namespace fc::storage::ipld { IPLDNodeEncoderPB pb_node_codec_; size_t child_nodes_size_{}; - common::Buffer serialize() const override; + outcome::result> getBlockContent() const override; }; } // namespace fc::storage::ipld diff --git a/core/storage/ipld/ipld_block.hpp b/core/storage/ipld/ipld_block.hpp index 276d0159b6..7e7926e470 100644 --- a/core/storage/ipld/ipld_block.hpp +++ b/core/storage/ipld/ipld_block.hpp @@ -6,6 +6,8 @@ #ifndef FILECOIN_STORAGE_IPLD_BLOCK_HPP #define FILECOIN_STORAGE_IPLD_BLOCK_HPP +#include + #include "common/buffer.hpp" #include "primitives/cid/cid.hpp" @@ -32,15 +34,6 @@ namespace fc::storage::ipld { */ virtual const common::Buffer &getRawBytes() const = 0; - protected: - /** - * @brief Optionally used for generating CID - * Children class must overload it to support structure serialization - * @return Serialized value - */ - virtual common::Buffer serialize() const { - return {}; - } }; } // namespace fc::storage::ipld diff --git a/core/storage/ipld/ipld_block_common.hpp b/core/storage/ipld/ipld_block_common.hpp new file mode 100644 index 0000000000..02168a1063 --- /dev/null +++ b/core/storage/ipld/ipld_block_common.hpp @@ -0,0 +1,72 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef FILECOIN_STORAGE_IPLD_BLOCK_IMPL_HPP +#define FILECOIN_STORAGE_IPLD_BLOCK_IMPL_HPP + +#include +#include + +#include +#include +#include +#include "common/buffer.hpp" +#include "crypto/hasher/hasher.hpp" +#include "primitives/cid/cid.hpp" +#include "storage/ipld/ipld_block.hpp" + +namespace fc::storage::ipld { + + using HashType = libp2p::multi::HashType; + using ContentType = libp2p::multi::MulticodecType::Code; + + /** + * @class IPLD data structure implementation + */ + template + class IPLDBlockCommon : public virtual IPLDBlock { + public: + const CID &getCID() const override { + if (!cid_) { + auto multi_hash = crypto::Hasher::calculate(hash_type, getRawBytes()); + CID id{cid_version, content_type, std::move(multi_hash)}; + cid_ = std::move(id); + } + return cid_.value(); + } + + const common::Buffer &getRawBytes() const override { + if (!raw_bytes_) { + auto bytes = getBlockContent(); + BOOST_ASSERT(bytes.has_value()); + raw_bytes_ = common::Buffer{std::move(bytes.value())}; + } + return raw_bytes_.value(); + } + + protected: + /** + * @brief Clear cached CID and serialized values + */ + void clearCache() const { + cid_ = boost::none; + raw_bytes_ = boost::none; + } + + /** + * @brief Get block content + * @return IPLD block raw bytes + */ + virtual outcome::result> getBlockContent() const = 0; + + private: + mutable boost::optional cid_; + mutable boost::optional raw_bytes_; + }; +} // namespace fc::storage::ipld + +#endif diff --git a/core/vm/interpreter/CMakeLists.txt b/core/vm/interpreter/CMakeLists.txt index 873adae29a..e309a2b7eb 100644 --- a/core/vm/interpreter/CMakeLists.txt +++ b/core/vm/interpreter/CMakeLists.txt @@ -4,7 +4,7 @@ # add_library(interpreter - interpreter.cpp + impl/interpreter_impl.cpp ) target_link_libraries(interpreter amt diff --git a/core/vm/interpreter/interpreter.cpp b/core/vm/interpreter/impl/interpreter_impl.cpp similarity index 92% rename from core/vm/interpreter/interpreter.cpp rename to core/vm/interpreter/impl/interpreter_impl.cpp index c38d14aa42..248a4bd7f0 100644 --- a/core/vm/interpreter/interpreter.cpp +++ b/core/vm/interpreter/impl/interpreter_impl.cpp @@ -3,17 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "vm/interpreter/interpreter.hpp" +#include "vm/interpreter/impl/interpreter_impl.hpp" #include "crypto/randomness/randomness_provider.hpp" #include "storage/amt/amt.hpp" #include "vm/actor/builtin/cron/cron_actor.hpp" #include "vm/actor/builtin/miner/miner_actor.hpp" #include "vm/actor/impl/invoker_impl.hpp" -#include "vm/message/message_codec.hpp" #include "vm/runtime/gas_cost.hpp" #include "vm/runtime/impl/runtime_impl.hpp" -#include "vm/state/impl/state_tree_impl.hpp" OUTCOME_CPP_DEFINE_CATEGORY(fc::vm::interpreter, InterpreterError, e) { using E = fc::vm::interpreter::InterpreterError; @@ -39,37 +37,19 @@ namespace fc::vm::interpreter { using crypto::randomness::RandomnessProvider; using message::SignedMessage; using message::UnsignedMessage; - using primitives::address::Address; - using primitives::block::BlockHeader; + + using primitives::block::MsgMeta; using runtime::Env; using runtime::kInfiniteGas; using runtime::MessageReceipt; using runtime::RuntimeImpl; - using state::StateTreeImpl; - using storage::amt::Amt; - bool hasDuplicateMiners(const std::vector &blocks) { - std::set
set; - for (auto &block : blocks) { - if (!set.insert(block.miner).second) { - return true; - } - } - return false; - } - - outcome::result
getMinerOwner(StateTreeImpl &state_tree, - const Address &miner) { - OUTCOME_TRY(actor, state_tree.get(miner)); - OUTCOME_TRY(state, - state_tree.getStore()->getCbor(actor.head)); - return state.info.owner; - } + using storage::amt::Amt; - outcome::result interpret(const std::shared_ptr &ipld, + outcome::result InterpreterImpl::interpret(const std::shared_ptr &ipld, const Tipset &tipset, - const std::shared_ptr &indices) { + const std::shared_ptr &indices) const { if (hasDuplicateMiners(tipset.blks)) { return InterpreterError::DUPLICATE_MINER; } @@ -188,4 +168,23 @@ namespace fc::vm::interpreter { receipts_amt.cid(), }; } + + bool InterpreterImpl::hasDuplicateMiners(const std::vector &blocks) const { + std::set
set; + for (auto &block : blocks) { + if (!set.insert(block.miner).second) { + return true; + } + } + return false; + } + + outcome::result InterpreterImpl::getMinerOwner(StateTreeImpl &state_tree, + const Address &miner) const { + OUTCOME_TRY(actor, state_tree.get(miner)); + OUTCOME_TRY(state, + state_tree.getStore()->getCbor(actor.head)); + return state.info.owner; + } + } // namespace fc::vm::interpreter diff --git a/core/vm/interpreter/impl/interpreter_impl.hpp b/core/vm/interpreter/impl/interpreter_impl.hpp new file mode 100644 index 0000000000..a972e4e9d3 --- /dev/null +++ b/core/vm/interpreter/impl/interpreter_impl.hpp @@ -0,0 +1,33 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CPP_FILECOIN_CORE_VM_INTERPRETER_INTERPRETER_IMPL_HPP +#define CPP_FILECOIN_CORE_VM_INTERPRETER_INTERPRETER_IMPL_HPP + +#include "vm/interpreter/interpreter.hpp" +#include "vm/state/impl/state_tree_impl.hpp" + +namespace fc::vm::interpreter { + class InterpreterImpl : public Interpreter { + public: + outcome::result interpret( + const std::shared_ptr &store, + const Tipset &tipset, + const std::shared_ptr &indices) const override; + + protected: + using BlockHeader = primitives::block::BlockHeader; + using Address = primitives::address::Address; + using StateTreeImpl = vm::state::StateTreeImpl; + + private: + bool hasDuplicateMiners(const std::vector &blocks) const; + + outcome::result
getMinerOwner(StateTreeImpl &state_tree, + const Address &miner) const; + }; +} // namespace fc::vm::interpreter + +#endif diff --git a/core/vm/interpreter/interpreter.hpp b/core/vm/interpreter/interpreter.hpp index 1f59065977..4d264c8770 100644 --- a/core/vm/interpreter/interpreter.hpp +++ b/core/vm/interpreter/interpreter.hpp @@ -17,18 +17,26 @@ namespace fc::vm::interpreter { CRON_TICK_FAILED, }; - using indices::Indices; - using primitives::tipset::Tipset; - using storage::ipfs::IpfsDatastore; - struct Result { CID state_root; CID message_receipts; }; - outcome::result interpret(const std::shared_ptr &store, - const Tipset &tipset, - const std::shared_ptr &indices); + class Interpreter { + protected: + using Indices = indices::Indices; + using Tipset = primitives::tipset::Tipset; + using IpfsDatastore = storage::ipfs::IpfsDatastore; + + public: + virtual ~Interpreter() = default; + + virtual outcome::result interpret( + const std::shared_ptr &store, + const Tipset &tipset, + const std::shared_ptr &indices) const = 0; + }; + } // namespace fc::vm::interpreter OUTCOME_HPP_DECLARE_ERROR(fc::vm::interpreter, InterpreterError); diff --git a/core/vm/message/CMakeLists.txt b/core/vm/message/CMakeLists.txt index 01584551d4..5b9151e7a8 100644 --- a/core/vm/message/CMakeLists.txt +++ b/core/vm/message/CMakeLists.txt @@ -17,4 +17,5 @@ target_link_libraries(message keystore outcome signature + ipld_block ) diff --git a/core/vm/message/message.hpp b/core/vm/message/message.hpp index cf94a7dca3..af77b5bbd7 100644 --- a/core/vm/message/message.hpp +++ b/core/vm/message/message.hpp @@ -6,12 +6,16 @@ #ifndef CPP_FILECOIN_CORE_VM_MESSAGE_HPP #define CPP_FILECOIN_CORE_VM_MESSAGE_HPP +#include #include +#include "codec/cbor/streams_annotation.hpp" #include "common/outcome.hpp" #include "crypto/signature/signature.hpp" #include "primitives/address/address.hpp" +#include "primitives/address/address_codec.hpp" #include "primitives/big_int.hpp" +#include "storage/ipld/ipld_block_common.hpp" #include "vm/actor/actor.hpp" namespace fc::vm::message { @@ -30,11 +34,34 @@ namespace fc::vm::message { using crypto::signature::Signature; using primitives::BigInt; using primitives::address::Address; + using storage::ipld::IPLDBlockCommon; /** * @brief UnsignedMessage struct */ - struct UnsignedMessage { + struct UnsignedMessage + : public IPLDBlockCommon { + UnsignedMessage() = default; + + UnsignedMessage(Address to, + Address from, + uint64_t nonce, + BigInt value, + BigInt gasPrice, + BigInt gasLimit, + MethodNumber method, + MethodParams params) + : to{std::move(to)}, + from{std::move(from)}, + nonce{nonce}, + value{std::move(value)}, + gasPrice{std::move(gasPrice)}, + gasLimit{std::move(gasLimit)}, + method{method}, + params{std::move(params)} {} + Address to; Address from; @@ -48,6 +75,10 @@ namespace fc::vm::message { MethodNumber method{}; MethodParams params{}; + outcome::result> getBlockContent() const override { + return codec::cbor::encode(*this); + } + /** * @brief Message equality operator */ @@ -61,6 +92,16 @@ namespace fc::vm::message { BigInt requiredFunds() const; }; + CBOR_TUPLE(UnsignedMessage, + to, + from, + nonce, + value, + gasPrice, + gasLimit, + method, + params) + /** * @brief SignedMessage struct */ @@ -69,6 +110,8 @@ namespace fc::vm::message { Signature signature; }; + CBOR_TUPLE(SignedMessage, message, signature) + constexpr uint64_t kMessageMaxSize = 32 * 1024; }; // namespace fc::vm::message diff --git a/core/vm/message/message_codec.hpp b/core/vm/message/message_codec.hpp deleted file mode 100644 index ccb2aee53c..0000000000 --- a/core/vm/message/message_codec.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef CPP_FILECOIN_CORE_VM_MESSAGE_CODEC_HPP -#define CPP_FILECOIN_CORE_VM_MESSAGE_CODEC_HPP - -#include "codec/cbor/cbor.hpp" -#include "codec/cbor/streams_annotation.hpp" -#include "crypto/signature/signature.hpp" -#include "primitives/address/address_codec.hpp" -#include "vm/message/message.hpp" - -namespace fc::vm::message { - CBOR_TUPLE(UnsignedMessage, - to, - from, - nonce, - value, - gasPrice, - gasLimit, - method, - params) - - CBOR_TUPLE(SignedMessage, message, signature) -}; // namespace fc::vm::message - -#endif // CPP_FILECOIN_CORE_VM_MESSAGE_CODEC_HPP diff --git a/core/vm/message/message_util.hpp b/core/vm/message/message_util.hpp index 801c059373..f6d370c36e 100644 --- a/core/vm/message/message_util.hpp +++ b/core/vm/message/message_util.hpp @@ -7,7 +7,7 @@ #define CPP_FILECOIN_CORE_VM_MESSAGE_UTIL_HPP #include "primitives/cid/cid.hpp" -#include "vm/message/message_codec.hpp" +#include "vm/message/message.hpp" namespace fc::vm::message { diff --git a/core/vm/runtime/impl/env.cpp b/core/vm/runtime/impl/env.cpp index 2b69054cc6..2fe977a57a 100644 --- a/core/vm/runtime/impl/env.cpp +++ b/core/vm/runtime/impl/env.cpp @@ -7,7 +7,6 @@ #include "vm/actor/builtin/account/account_actor.hpp" #include "vm/exit_code/exit_code.hpp" -#include "vm/message/message_codec.hpp" #include "vm/runtime/gas_cost.hpp" #include "vm/runtime/impl/runtime_impl.hpp" #include "vm/runtime/runtime_error.hpp" diff --git a/core/vm/runtime/impl/runtime_impl.cpp b/core/vm/runtime/impl/runtime_impl.cpp index 3e34badc47..8ef5ec69a2 100644 --- a/core/vm/runtime/impl/runtime_impl.cpp +++ b/core/vm/runtime/impl/runtime_impl.cpp @@ -7,7 +7,6 @@ #include "codec/cbor/cbor.hpp" #include "vm/actor/builtin/account/account_actor.hpp" -#include "vm/message/message_codec.hpp" #include "vm/runtime/gas_cost.hpp" #include "vm/runtime/impl/actor_state_handle_impl.hpp" #include "vm/runtime/runtime_error.hpp" diff --git a/test/core/blockchain/CMakeLists.txt b/test/core/blockchain/CMakeLists.txt index 2c7ff46ab8..6826667395 100644 --- a/test/core/blockchain/CMakeLists.txt +++ b/test/core/blockchain/CMakeLists.txt @@ -4,3 +4,4 @@ # add_subdirectory(message_pool) +add_subdirectory(production) diff --git a/test/core/blockchain/production/CMakeLists.txt b/test/core/blockchain/production/CMakeLists.txt new file mode 100644 index 0000000000..7e4c4ed8a6 --- /dev/null +++ b/test/core/blockchain/production/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +addtest(block_producer_test + block_producer_test.cpp + ) +target_link_libraries(block_producer_test + block_producer + ) diff --git a/test/core/blockchain/production/block_producer_test.cpp b/test/core/blockchain/production/block_producer_test.cpp new file mode 100644 index 0000000000..25663d4379 --- /dev/null +++ b/test/core/blockchain/production/block_producer_test.cpp @@ -0,0 +1,157 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "block_producer_test.hpp" + +#include "codec/cbor/cbor.hpp" + +using testing::_; + +BlockProducerTest::Tipset BlockProducerTest::getParentTipset() const { + return Tipset{.cids = config::kParentTipsetBlocks, .blks = {}, .height = 0}; +} + +BlockProducerTest::Ticket BlockProducerTest::getTicket() const { + fc::crypto::vrf::VRFProof ticket_proof{}; + std::copy_n( + config::kTicket.begin(), ticket_proof.size(), ticket_proof.begin()); + return Ticket{.bytes = ticket_proof}; +} + +std::vector +BlockProducerTest::getSampleMessages() const { + using fc::vm::actor::MethodNumber; + using fc::vm::actor::MethodParams; + UnsignedMessage message_A{Address::makeFromId(1), + Address::makeFromId(2), + 123, + 5, + 100, + 1000, + MethodNumber{1}, + MethodParams{}}; + UnsignedMessage message_B{Address::makeFromId(2), + Address::makeFromId(3), + 456, + 10, + 120, + 800, + MethodNumber{2}, + MethodParams{}}; + auto signature_A_bytes{ + "6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a" + "6b6c6d6e6f7071" + "72737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a61" + "62636465666768" + "696a6b6c6d6e6f707172"_unhex}; + auto signature_B{ + "7271706f6e6d6c6b6a6968676665646362617a797877767574737271706f6e6d6c6b6a69" + "68676665646362" + "617a797877767574737271706f6e6d6c6b6a6968676665646362617a7978777675747372" + "71706f6e6d6c6b" + "6a696867666564636261"_unhex}; + BlsSignature signature_A{}; + std::copy_n( + signature_A_bytes.begin(), signature_A_bytes.size(), signature_A.begin()); + return {{message_A, signature_A}, {message_B, signature_B}}; +} + +/** + * @given Sample data and required modules + * @when Generating new block + * @then Operation must be completed successfully + */ +TEST_F(BlockProducerTest, Main) { + /** + * Setup IPFS datastore, which should contain parent tipset + * BlockProducer will try to obtain serialized parent tipset bytes by CID + */ + auto parent_tipset = getParentTipset(); + EXPECT_OUTCOME_TRUE(parent_tipset_raw_bytes, + fc::codec::cbor::encode(parent_tipset)); + auto ipfs_datastore = std::make_shared(); + EXPECT_CALL(*ipfs_datastore, get(config::kParentTipset)) + .WillOnce(testing::Return(fc::outcome::success(parent_tipset_raw_bytes))); + + /** + * Setup Message Store, which should contain several sample messages + */ + auto message_store = std::make_shared(); + using fc::blockchain::production::config::kBlockMaxMessagesCount; + EXPECT_CALL(*message_store, getTopScored(kBlockMaxMessagesCount)) + .WillOnce(testing::Return(getSampleMessages())); + + /** + * Initialize UTC Clock + */ + auto utc_clock = std::make_shared(); + std::chrono::duration block_timestamp{ + config::kBlockCreationUnixTime}; + EXPECT_CALL(*utc_clock, nowUTC()) + .Times(1) + .WillOnce(testing::Return(Time{block_timestamp})); + + /** + * Initialize Epoch Clock (non-mock implementation) + */ + std::chrono::duration genesis_time{config::kGenesisTime}; + auto epoch_clock = std::make_shared(Time{genesis_time}); + + /** + * Initialize weight calculator + */ + auto weight_calculator = std::make_shared(); + EXPECT_CALL(*weight_calculator, calculateWeight(parent_tipset)) + .Times(1) + .WillOnce(testing::Return(config::kParentTipsetWeight)); + + /** + * Initialize BLS Provider + */ + auto bls_provider = std::make_shared(); + BlsSignature aggregated_signature{}; + auto aggregated_signature_bytes{ + "7271706f6e6d6c6b6a6968676665646362617a797877767574737271706f6e6d6c6b6a69" + "68676665646362" + "617a797877767574737271706f6e6d6c6b6a6968676665646362617a7978777675747372" + "71706f6e6d6c6b" + "6a696867666564636261"_unhex}; + std::copy_n(aggregated_signature_bytes.begin(), + aggregated_signature_bytes.size(), + aggregated_signature.begin()); + + EXPECT_CALL(*bls_provider, aggregateSignatures(_)) + .WillOnce(testing::Return(fc::outcome::success(aggregated_signature))); + + /** + * Initialize VM Interpreter + */ + using InterpreterResult = fc::vm::interpreter::Result; + auto vm_interpreter = std::make_shared(); + std::shared_ptr vm_indices = std::make_shared(); + std::shared_ptr vm_datastore = ipfs_datastore; + InterpreterResult interpreter_result{config::kParentTipset, + config::kParentTipset}; + EXPECT_CALL(*vm_interpreter, interpret(_, _, _)) + .WillOnce(testing::Return(interpreter_result)); + + /** + * Instantiate Block Producer + */ + std::shared_ptr block_producer = + std::make_shared(ipfs_datastore, + message_store, + utc_clock, + epoch_clock, + weight_calculator, + bls_provider, + vm_interpreter); + EXPECT_OUTCOME_TRUE_1( + block_producer->generate(Address::makeFromId(config::kMinerAddressId), + config::kParentTipset, + e_post_proof_, + getTicket(), + std::make_shared())); +} diff --git a/test/core/blockchain/production/block_producer_test.hpp b/test/core/blockchain/production/block_producer_test.hpp new file mode 100644 index 0000000000..e64f952bbc --- /dev/null +++ b/test/core/blockchain/production/block_producer_test.hpp @@ -0,0 +1,92 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include "blockchain/message_pool/message_storage.hpp" +#include "blockchain/production/impl/block_producer_impl.hpp" +#include "clock/impl/chain_epoch_clock_impl.hpp" +#include "clock/time.hpp" +#include "crypto/bls/impl/bls_provider_impl.hpp" +#include "primitives/address/address.hpp" +#include "primitives/cid/cid.hpp" +#include "primitives/tipset/tipset.hpp" +#include "testutil/cbor.hpp" +#include "testutil/literals.hpp" +#include "testutil/mocks/blockchain/message_pool/message_storage_mock.hpp" +#include "testutil/mocks/blockchain/weight_calculator_mock.hpp" +#include "testutil/mocks/clock/utc_clock_mock.hpp" +#include "testutil/mocks/crypto/bls/bls_provider_mock.hpp" +#include "testutil/mocks/storage/ipfs/ipfs_datastore_mock.hpp" +#include "testutil/mocks/vm/indices/indices_mock.hpp" +#include "testutil/mocks/vm/interpreter/interpreter_mock.hpp" +#include "testutil/outcome.hpp" + +namespace config { + constexpr size_t kMinerAddressId{32615184}; + constexpr size_t kGenesisTime{7000}; + constexpr size_t kBlockCreationUnixTime{48151623}; + constexpr size_t kParentTipsetWeight{111307}; + const auto kParentTipset{"010001020005"_cid}; + const std::vector kParentTipsetBlocks{ + "010001020006"_cid, + "010001020007"_cid}; + const fc::common::Buffer kPostProof{"a0b0cc"_unhex}; + const auto kPostRand{ + "e9cecfc7c4c120d4c1cb20c8cfdec5d4d3d120dac1c2d9d4d820cf20d1ddc9cbc520d320" + "cdc9cbd2cfd3c8c5cdc1cdc920c920cec520cfd4cbd2d9d7c1d4d820d7cfccdbc5c2ced9" + "c520d7cfd2cfd4c120d720d7cfccdbc5c2ced9ca20cdc9d2"_blob96}; + const auto kTicket{ + "7672662070726f6f66303030303030307672662070726f6f663030303030303076726620" + "70726f6f66303030303030307672662070726f6f66303030303030307672662070726f6f" + "66303030303030307672662070726f6f6630303030303030"_blob96}; +} // namespace config + +class BlockProducerTest : public testing::Test { + protected: + using CID = fc::CID; + using UTCClock = fc::clock::UTCClock; + using UTCClockImpl = UTCClockMock; + using ChainEpochClock = fc::clock::ChainEpochClock; + using ChainEpochClockImpl = fc::clock::ChainEpochClockImpl; + using Time = fc::clock::Time; + using Address = fc::primitives::address::Address; + using Tipset = fc::primitives::tipset::Tipset; + using IpfsDatastore = fc::storage::ipfs::IpfsDatastore; + using IpfsDatastoreImpl = fc::storage::ipfs::MockIpfsDatastore; + using MessageStorage = fc::blockchain::message_pool::MessageStorage; + using MessageStorageImpl = fc::blockchain::message_pool::MessageStorageMock; + using UnsignedMessage = fc::primitives::block::UnsignedMessage; + using SignedMessage = fc::blockchain::message_pool::SignedMessage; + using BlsSignature = fc::crypto::signature::BlsSignature; + using SecpSignature = fc::crypto::signature::Secp256k1Signature; + using WeightCalculator = fc::blockchain::weight::WeightCalculator; + using WeightCalculatorImpl = fc::blockchain::weight::WeightCalculatorMock; + using BlsProvider = fc::crypto::bls::BlsProvider; + using BlsProviderImpl = fc::crypto::bls::BlsProviderMock; + using Interpreter = fc::vm::interpreter::Interpreter; + using InterpreterImpl = fc::vm::interpreter::InterpreterMock; + using CIDCodec = libp2p::multi::ContentIdentifierCodec; + using EPostProof = fc::primitives::ticket::EPostProof; + using PostRandomness = fc::primitives::ticket::PostRandomness; + using Ticket = fc::primitives::ticket::Ticket; + using Indices = fc::vm::indices::Indices; + using IndicesImpl = fc::vm::indices::MockIndices; + using BlockProducer = fc::blockchain::production::BlockProducer; + using BlockProducerImpl = fc::blockchain::production::BlockProducerImpl; + + EPostProof e_post_proof_{.proof = config::kPostProof, + .post_rand = config::kPostRand, + .candidates = {}}; + + Tipset getParentTipset() const; + + Ticket getTicket() const; + + std::vector getSampleMessages() const; +}; diff --git a/test/core/vm/actor/builtin/cron/cron_actor_test.cpp b/test/core/vm/actor/builtin/cron/cron_actor_test.cpp index 291093967e..b8843f96db 100644 --- a/test/core/vm/actor/builtin/cron/cron_actor_test.cpp +++ b/test/core/vm/actor/builtin/cron/cron_actor_test.cpp @@ -26,7 +26,7 @@ using fc::vm::runtime::MockRuntime; */ TEST(CronActorTest, WrongSender) { auto message_wrong_sender = - UnsignedMessage{actor::kInitAddress, actor::kInitAddress}; + UnsignedMessage{actor::kInitAddress, actor::kInitAddress, {}, {}, {}, {}, {}, {}}; MockRuntime runtime; actor::Actor actor; EXPECT_CALL(runtime, getMessage()) @@ -41,7 +41,7 @@ TEST(CronActorTest, WrongSender) { * @then success */ TEST(CronActorTest, Correct) { - auto message = UnsignedMessage{actor::kInitAddress, actor::kCronAddress}; + auto message = UnsignedMessage{actor::kInitAddress, actor::kCronAddress, {}, {}, {}, {}, {}, {}}; MockRuntime runtime; actor::Actor actor; EXPECT_CALL(runtime, getMessage()).WillOnce(testing::Return(message)); diff --git a/test/core/vm/actor/builtin/storage_power/storage_power_actor_test.cpp b/test/core/vm/actor/builtin/storage_power/storage_power_actor_test.cpp index 64ba212603..d2eb67716d 100644 --- a/test/core/vm/actor/builtin/storage_power/storage_power_actor_test.cpp +++ b/test/core/vm/actor/builtin/storage_power/storage_power_actor_test.cpp @@ -287,7 +287,7 @@ TEST_F(StoragePowerActorTest, AddBalanceSuccess) { .Times(3) .WillRepeatedly(testing::Return(datastore)); // get message - UnsignedMessage message{miner_address, caller_address, 0, amount_to_add}; + UnsignedMessage message{miner_address, caller_address, 0, amount_to_add, {}, {}, {}, {}}; EXPECT_CALL(runtime, getMessage()).WillOnce(testing::Return(message)); // commit and capture state CID @@ -385,7 +385,7 @@ TEST_F(StoragePowerActorTest, CreateMinerSuccess) { "2222222222222222222222222222222222222222" "2222222222222222"_blob48); TokenAmount amount{100200}; - UnsignedMessage message{any_address_1, caller_address, 0, amount}; + UnsignedMessage message{any_address_1, caller_address, 0, amount, {}, {}, {}, {}}; EXPECT_CALL(runtime, getMessage()).WillOnce(testing::Return(message)); // return immediate caller is signable code id @@ -558,7 +558,7 @@ TEST_F(StoragePowerActorTest, OnSectorProofCommitSuccess) { .Times(3) .WillRepeatedly(testing::Return(datastore)); - UnsignedMessage message{caller_address, miner_address, 0, 0}; + UnsignedMessage message{caller_address, miner_address, 0, 0, {}, {}, {}, {}}; EXPECT_CALL(runtime, getMessage()).WillOnce(testing::Return(message)); // commit and capture state CID @@ -607,7 +607,7 @@ TEST_F(StoragePowerActorTest, OnSectorTerminateSuccess) { EXPECT_CALL(runtime, getCurrentActorState()) .WillOnce(::testing::Return(actor_head_cid)); - UnsignedMessage message{caller_address, miner_address, 0, 0}; + UnsignedMessage message{caller_address, miner_address, 0, 0, {}, {}, {}, {}}; EXPECT_CALL(runtime, getMessage()).WillOnce(testing::Return(message)); EXPECT_CALL(runtime, getIpfsDatastore()) diff --git a/test/core/vm/actor/invoker_test.cpp b/test/core/vm/actor/invoker_test.cpp index 81ca087a15..d701fec17d 100644 --- a/test/core/vm/actor/invoker_test.cpp +++ b/test/core/vm/actor/invoker_test.cpp @@ -20,7 +20,7 @@ using fc::vm::runtime::MockRuntime; TEST(InvokerTest, InvokeCron) { using namespace fc::vm::actor; - auto message = UnsignedMessage{kInitAddress, kInitAddress}; + auto message = UnsignedMessage{kInitAddress, kInitAddress, {}, {}, {}, {}, {}, {}}; InvokerImpl invoker; MockRuntime runtime; diff --git a/test/core/vm/runtime/runtime_test.cpp b/test/core/vm/runtime/runtime_test.cpp index 3c7b03fb2b..5da7f9fbdb 100644 --- a/test/core/vm/runtime/runtime_test.cpp +++ b/test/core/vm/runtime/runtime_test.cpp @@ -60,7 +60,7 @@ class RuntimeTest : public ::testing::Test { std::make_shared(); std::shared_ptr indices_ = std::make_shared(); std::shared_ptr invoker_ = std::make_shared(); - UnsignedMessage message_{message_to, message_from}; + UnsignedMessage message_{message_to, message_from, {}, {}, {}, {}, {}, {}}; ChainEpoch chain_epoch_{0}; Address immediate_caller_{fc::primitives::address::TESTNET, 1}; Address block_miner_{}; diff --git a/test/testutil/mocks/blockchain/message_pool/message_storage_mock.hpp b/test/testutil/mocks/blockchain/message_pool/message_storage_mock.hpp new file mode 100644 index 0000000000..86001c2ff3 --- /dev/null +++ b/test/testutil/mocks/blockchain/message_pool/message_storage_mock.hpp @@ -0,0 +1,24 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CPP_FILECOIN_MOCKS_BLOCKCHAIN_MESSAGE_POOL_STORAGE +#define CPP_FILECOIN_MOCKS_BLOCKCHAIN_MESSAGE_POOL_STORAGE + +#include + +#include "blockchain/message_pool/message_storage.hpp" + +namespace fc::blockchain::message_pool { + class MessageStorageMock : public MessageStorage { + public: + MOCK_METHOD1(put, outcome::result(const SignedMessage &message)); + + MOCK_METHOD1(remove, void(const SignedMessage &message)); + + MOCK_CONST_METHOD1(getTopScored, std::vector(size_t n)); + }; +} // namespace fc::blockchain::message_pool + +#endif diff --git a/test/testutil/mocks/blockchain/weight_calculator_mock.hpp b/test/testutil/mocks/blockchain/weight_calculator_mock.hpp new file mode 100644 index 0000000000..3a66eabfa7 --- /dev/null +++ b/test/testutil/mocks/blockchain/weight_calculator_mock.hpp @@ -0,0 +1,21 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CPP_FILECOIN_MOCKS_BLOCKCHAIN_WEIGHT_CALCULATOR +#define CPP_FILECOIN_MOCKS_BLOCKCHAIN_WEIGHT_CALCULATOR + +#include + +#include "blockchain/weight_calculator.hpp" + +namespace fc::blockchain::weight { + class WeightCalculatorMock : public WeightCalculator { + public: + MOCK_METHOD1(calculateWeight, + outcome::result(const Tipset &tipset)); + }; +} // namespace fc::blockchain::weight + +#endif diff --git a/test/testutil/mocks/clock/utc_clock_mock.hpp b/test/testutil/mocks/clock/utc_clock_mock.hpp new file mode 100644 index 0000000000..d085aa721c --- /dev/null +++ b/test/testutil/mocks/clock/utc_clock_mock.hpp @@ -0,0 +1,21 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TEST_CORE_BLOCKCHAIN_MOCKS_UTC_CLOCK +#define TEST_CORE_BLOCKCHAIN_MOCKS_UTC_CLOCK + +#include + +#include "clock/utc_clock.hpp" + +using fc::clock::UTCClock; +using fc::clock::Time; + +class UTCClockMock : public UTCClock { + public: + MOCK_CONST_METHOD0(nowUTC, Time()); +}; + +#endif diff --git a/test/testutil/mocks/crypto/bls/bls_provider_mock.hpp b/test/testutil/mocks/crypto/bls/bls_provider_mock.hpp new file mode 100644 index 0000000000..d407c6cd31 --- /dev/null +++ b/test/testutil/mocks/crypto/bls/bls_provider_mock.hpp @@ -0,0 +1,34 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CPP_FILECOIN_TEST_TESTUTIL_CRYPTO_BLS_PROVIDER_MOCK_HPP +#define CPP_FILECOIN_TEST_TESTUTIL_CRYPTO_BLS_PROVIDER_MOCK_HPP + +#include +#include "crypto/bls/bls_provider.hpp" + +namespace fc::crypto::bls { + class BlsProviderMock : public BlsProvider { + public: + MOCK_CONST_METHOD0(generateKeyPair, outcome::result()); + + MOCK_CONST_METHOD1(derivePublicKey, + outcome::result(const PrivateKey &)); + + MOCK_CONST_METHOD2(sign, + outcome::result(gsl::span, + const PrivateKey &)); + + MOCK_CONST_METHOD3(verifySignature, + outcome::result(gsl::span, + const Signature &, + const PublicKey &)); + MOCK_CONST_METHOD1( + aggregateSignatures, + outcome::result(const std::vector &)); + }; +} // namespace fc::crypto::bls + +#endif diff --git a/test/testutil/mocks/storage/ipfs/ipfs_datastore_mock.hpp b/test/testutil/mocks/storage/ipfs/ipfs_datastore_mock.hpp index c9685f1f0f..39d6905ea5 100644 --- a/test/testutil/mocks/storage/ipfs/ipfs_datastore_mock.hpp +++ b/test/testutil/mocks/storage/ipfs/ipfs_datastore_mock.hpp @@ -18,6 +18,10 @@ namespace fc::storage::ipfs { MOCK_METHOD2(set, outcome::result(const CID &key, Value value)); MOCK_CONST_METHOD1(get, outcome::result(const CID &key)); MOCK_METHOD1(remove, outcome::result(const CID &key)); + + bool operator==(const MockIpfsDatastore &) const { + return true; + } }; } // namespace fc::storage::ipfs diff --git a/test/testutil/mocks/vm/interpreter/interpreter_mock.hpp b/test/testutil/mocks/vm/interpreter/interpreter_mock.hpp new file mode 100644 index 0000000000..30cf77a094 --- /dev/null +++ b/test/testutil/mocks/vm/interpreter/interpreter_mock.hpp @@ -0,0 +1,24 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CPP_FILECOIN_MOCKS_VM_INTERPRETER +#define CPP_FILECOIN_MOCKS_VM_INTERPRETER + +#include + +#include "vm/interpreter/impl/interpreter_impl.hpp" + +namespace fc::vm::interpreter { + class InterpreterMock : public Interpreter { + public: + MOCK_CONST_METHOD3( + interpret, + outcome::result(const std::shared_ptr &store, + const Tipset &tipset, + const std::shared_ptr &indices)); + }; +} // namespace fc::vm::interpreter + +#endif