Skip to content

Block producer #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/blockchain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#

add_subdirectory(message_pool)
add_subdirectory(production)

add_library(block_validator
impl/block_validator_impl.cpp
Expand Down
18 changes: 18 additions & 0 deletions core/blockchain/production/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
27 changes: 0 additions & 27 deletions core/blockchain/production/block_generator.hpp

This file was deleted.

53 changes: 53 additions & 0 deletions core/blockchain/production/block_producer.hpp
Original file line number Diff line number Diff line change
@@ -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<Block> generate(
Address miner_address,
const CID &parent_tipset_id,
EPostProof proof,
Ticket ticket,
std::shared_ptr<Indices> 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);
161 changes: 161 additions & 0 deletions core/blockchain/production/impl/block_producer_impl.cpp
Original file line number Diff line number Diff line change
@@ -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 <vector>

#include <gsl/span>
#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<IpfsDatastore> data_store,
std::shared_ptr<MessageStorage> message_store,
std::shared_ptr<UTCClock> utc_clock,
std::shared_ptr<ChainEpochClock> epoch_clock,
std::shared_ptr<WeightCalculator> weight_calculator,
std::shared_ptr<BlsProvider> crypto_provider,
std::shared_ptr<Interpreter> 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<BlockProducer::Block> BlockProducerImpl::generate(
primitives::address::Address miner_address,
const CID &parent_tipset_id,
EPostProof proof,
Ticket ticket,
std::shared_ptr<Indices> 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<SignedMessage> messages =
message_storage_->getTopScored(config::kBlockMaxMessagesCount);
OUTCOME_TRY(msg_meta, getMessagesMeta(messages));
std::vector<UnsignedMessage> bls_messages;
std::vector<SignedMessage> secp_messages;
std::vector<crypto::bls::Signature> 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<uint64_t>(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<uint64_t>(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::Tipset> 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<Tipset>(raw_data.value());
if (tipset.has_error()) {
return BlockProducerError::PARENT_TIPSET_INVALID_CONTENT;
}
return tipset.value();
}

outcome::result<BlockProducerImpl::MsgMeta>
BlockProducerImpl::getMessagesMeta(
const std::vector<SignedMessage> &messages) {
auto bls_backend = std::make_shared<InMemoryDatastore>();
auto secp_backend = std::make_shared<InMemoryDatastore>();
Amt bls_messages_amt{bls_backend};
Amt secp_messages_amt{secp_backend};
std::vector<SignedMessage> bls_messages;
std::vector<SignedMessage> 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";
}
}
85 changes: 85 additions & 0 deletions core/blockchain/production/impl/block_producer_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include <memory>

#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<IpfsDatastore> data_store,
std::shared_ptr<MessageStorage> message_store,
std::shared_ptr<UTCClock> utc_clock,
std::shared_ptr<ChainEpochClock> epoch_clock,
std::shared_ptr<WeightCalculator> weight_calculator,
std::shared_ptr<BlsProvider> crypto_provider,
std::shared_ptr<Interpreter> interpreter);

outcome::result<Block> generate(Address miner_address,
const CID &parent_tipset_id,
EPostProof proof,
Ticket ticket,
std::shared_ptr<Indices> indices) override;

private:
std::shared_ptr<IpfsDatastore> data_storage_;
std::shared_ptr<MessageStorage> message_storage_;
std::shared_ptr<UTCClock> clock_;
std::shared_ptr<ChainEpochClock> epoch_;
std::shared_ptr<WeightCalculator> chain_weight_calculator_;
std::shared_ptr<BlsProvider> bls_provider_;
std::shared_ptr<Interpreter> 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<Tipset> 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<MsgMeta> getMessagesMeta(
const std::vector<SignedMessage> &messages);
};
} // namespace fc::blockchain::production
11 changes: 10 additions & 1 deletion core/crypto/bls/bls_provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
#ifndef CRYPTO_BLS_PROVIDER_HPP
#define CRYPTO_BLS_PROVIDER_HPP

#include <gsl/span>
#include <vector>

#include <gsl/span>
#include "crypto/bls/bls_types.hpp"

namespace fc::crypto::bls {
Expand Down Expand Up @@ -51,6 +52,14 @@ namespace fc::crypto::bls {
gsl::span<const uint8_t> 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<Signature> aggregateSignatures(
const std::vector<Signature> &signatures) const = 0;
};
} // namespace fc::crypto::bls

Expand Down
3 changes: 2 additions & 1 deletion core/crypto/bls/bls_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ namespace fc::crypto::bls {
SignatureGenerationFailed,
SignatureVerificationFailed,
InvalidPrivateKey,
InvalidPublicKey
InvalidPublicKey,
AggregateError
};
};

Expand Down
Loading