Aft: In BFT mode propagate backup signatures and acks to signatures (#1658)

This commit is contained in:
Alex 2020-09-29 11:40:09 +01:00 коммит произвёл GitHub
Родитель 6b86b17338
Коммит 5d9cfc65f1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 754 добавлений и 64 удалений

Просмотреть файл

@ -319,6 +319,16 @@ if(BUILD_TESTS)
http_parser.host
)
add_unit_test(
progress_tracker_test
${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/progress_tracker.cpp
)
target_include_directories(progress_tracker_test PRIVATE ${EVERCRYPT_INC})
target_link_libraries(
progress_tracker_test PRIVATE ${CRYPTO_LIBRARY} evercrypt.host
secp256k1.host
)
add_unit_test(
secret_sharing_test
${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/secret_share.cpp

Просмотреть файл

@ -334,6 +334,11 @@ namespace aft
return configurations.back().nodes;
}
uint32_t node_count() const
{
return get_latest_configuration().size();
}
template <typename T>
bool replicate(
const std::vector<std::tuple<Index, T, bool>>& entries, Term term)
@ -456,6 +461,14 @@ namespace aft
recv_request_vote_response(data, size);
break;
case bft_signature_received_ack:
recv_signature_received_ack(data, size);
break;
case bft_nonce_reveal:
recv_nonce_reveal(data, size);
break;
default:
{
}
@ -845,22 +858,42 @@ namespace aft
to,
state->last_idx);
auto progress_tracker = store->get_progress_tracker();
CCF_ASSERT(progress_tracker != nullptr, "progress_tracker is not set");
auto h = progress_tracker->get_my_hashed_nonce(
state->current_view, state->last_idx);
Nonce hashed_nonce;
std::copy(h.begin(), h.end(), hashed_nonce.begin());
SignedAppendEntriesResponse r = {
{raft_append_entries_signed_response, state->my_node_id},
state->current_view,
state->last_idx,
hashed_nonce,
static_cast<uint32_t>(sig.sig.size()),
{}};
std::copy(sig.sig.begin(), sig.sig.end(), r.sig.data());
auto progress_tracker = store->get_progress_tracker();
if (progress_tracker != nullptr)
auto result = progress_tracker->add_signature(
r.term,
r.last_log_idx,
r.from_node,
r.signature_size,
r.sig,
hashed_nonce,
node_count());
for (auto it = nodes.begin(); it != nodes.end(); ++it)
{
progress_tracker->add_signature(
r.term, r.last_log_idx, r.from_node, r.signature_size, r.sig);
auto send_to = it->first;
if (send_to != state->my_node_id)
{
channels->send_authenticated(
ccf::NodeMsgType::consensus_msg, send_to, r);
}
}
channels->send_authenticated(ccf::NodeMsgType::consensus_msg, to, r);
try_send_sig_ack(r.term, r.last_log_idx, result);
}
void recv_append_entries_signed_response(const uint8_t* data, size_t size)
@ -891,13 +924,178 @@ namespace aft
}
auto progress_tracker = store->get_progress_tracker();
if (progress_tracker != nullptr)
CCF_ASSERT(progress_tracker != nullptr, "progress_tracker is not set");
auto result = progress_tracker->add_signature(
r.term,
r.last_log_idx,
r.from_node,
r.signature_size,
r.sig,
r.hashed_nonce,
node_count());
try_send_sig_ack(r.term, r.last_log_idx, result);
}
void try_send_sig_ack(
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
kv::TxHistory::Result r)
{
switch (r)
{
progress_tracker->add_signature(
r.term, r.last_log_idx, r.from_node, r.signature_size, r.sig);
case kv::TxHistory::Result::OK:
case kv::TxHistory::Result::FAIL:
{
break;
}
case kv::TxHistory::Result::SEND_SIG_RECEIPT_ACK:
{
SignaturesReceivedAck r = {
{bft_signature_received_ack, state->my_node_id}, view, seqno};
for (auto it = nodes.begin(); it != nodes.end(); ++it)
{
auto send_to = it->first;
if (send_to != state->my_node_id)
{
channels->send_authenticated(
ccf::NodeMsgType::consensus_msg, send_to, r);
}
}
auto progress_tracker = store->get_progress_tracker();
CCF_ASSERT(
progress_tracker != nullptr, "progress_tracker is not set");
auto result = progress_tracker->add_signature_ack(
view, seqno, state->my_node_id, node_count());
try_send_reply_and_nonce(view, seqno, result);
break;
}
default:
{
throw ccf::ccf_logic_error(fmt::format("Unknown enum type: {}", r));
}
}
}
void recv_signature_received_ack(const uint8_t* data, size_t size)
{
SignaturesReceivedAck r;
try
{
r = channels->template recv_authenticated<SignaturesReceivedAck>(
data, size);
}
catch (const std::logic_error& err)
{
LOG_FAIL_FMT("Error in recv_signature_received_ack message");
LOG_DEBUG_FMT(
"Error in recv_signature_received_ack message: {}", err.what());
return;
}
auto node = nodes.find(r.from_node);
if (node == nodes.end())
{
// Ignore if we don't recognise the node.
LOG_FAIL_FMT(
"Recv signature received ack to {} from {}: unknown node",
state->my_node_id,
r.from_node);
return;
}
auto progress_tracker = store->get_progress_tracker();
CCF_ASSERT(progress_tracker != nullptr, "progress_tracker is not set");
LOG_TRACE_FMT(
"processing recv_signature_received_ack, from:{} view:{}, seqno:{}",
r.from_node,
r.term,
r.idx);
auto result = progress_tracker->add_signature_ack(
r.term, r.idx, r.from_node, node_count());
try_send_reply_and_nonce(r.term, r.idx, result);
}
void try_send_reply_and_nonce(
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
kv::TxHistory::Result r)
{
switch (r)
{
case kv::TxHistory::Result::OK:
case kv::TxHistory::Result::FAIL:
{
break;
}
case kv::TxHistory::Result::SEND_REPLY_AND_NONCE:
{
Nonce nonce;
auto progress_tracker = store->get_progress_tracker();
CCF_ASSERT(
progress_tracker != nullptr, "progress_tracker is not set");
nonce = progress_tracker->get_my_nonce(view, seqno);
NonceRevealMsg r = {
{bft_nonce_reveal, state->my_node_id}, view, seqno, nonce};
for (auto it = nodes.begin(); it != nodes.end(); ++it)
{
auto send_to = it->first;
if (send_to != state->my_node_id)
{
channels->send_authenticated(
ccf::NodeMsgType::consensus_msg, send_to, r);
}
}
progress_tracker->add_nonce_reveal(
view, seqno, nonce, state->my_node_id);
break;
}
default:
{
throw ccf::ccf_logic_error(fmt::format("Unknown enum type: {}", r));
}
}
}
void recv_nonce_reveal(const uint8_t* data, size_t size)
{
NonceRevealMsg r;
try
{
r = channels->template recv_authenticated<NonceRevealMsg>(data, size);
}
catch (const std::logic_error& err)
{
LOG_FAIL_FMT("Error in recv_signature_received_ack message");
LOG_DEBUG_FMT(
"Error in recv_signature_received_ack message: {}", err.what());
return;
}
auto node = nodes.find(r.from_node);
if (node == nodes.end())
{
// Ignore if we don't recognise the node.
LOG_FAIL_FMT(
"Recv nonce reveal to {} from {}: unknown node",
state->my_node_id,
r.from_node);
return;
}
auto progress_tracker = store->get_progress_tracker();
CCF_ASSERT(progress_tracker != nullptr, "progress_tracker is not set");
LOG_TRACE_FMT(
"processing nonce_reveal, from:{} view:{}, seqno:{}",
r.from_node,
r.term,
r.idx);
progress_tracker->add_nonce_reveal(r.term, r.idx, r.nonce, r.from_node);
}
void recv_append_entries_response(const uint8_t* data, size_t size)
{
std::lock_guard<SpinLock> guard(state->lock);
@ -1461,7 +1659,7 @@ namespace aft
for (auto node_id : to_remove)
{
if (replica_state == Leader)
if (replica_state == Leader || consensus_type == ConsensusType::BFT)
{
channels->destroy_channel(node_id);
}
@ -1487,13 +1685,16 @@ namespace aft
auto index = state->last_idx + 1;
nodes.try_emplace(node_info.first, node_info.second, index, 0);
if (replica_state == Leader)
if (replica_state == Leader || consensus_type == ConsensusType::BFT)
{
channels->create_channel(
node_info.first,
node_info.second.hostname,
node_info.second.port);
}
if (replica_state == Leader)
{
send_append_entries(node_info.first, index);
}

Просмотреть файл

@ -126,6 +126,11 @@ namespace aft
aft->enable_all_domains();
}
uint32_t node_count() override
{
return aft->node_count();
}
void open_network() override
{
is_open = true;

Просмотреть файл

@ -21,6 +21,7 @@ namespace aft
using Term = int64_t;
using NodeId = uint64_t;
using Node2NodeMsg = uint64_t;
using Nonce = std::array<uint8_t, 32>;
using ReplyCallback = std::function<bool(
void* owner,
@ -133,6 +134,8 @@ namespace aft
raft_request_vote_response,
bft_request,
bft_signature_received_ack,
bft_nonce_reveal
};
#pragma pack(push, 1)
@ -162,10 +165,24 @@ namespace aft
{
Term term;
Index last_log_idx;
Nonce hashed_nonce;
uint32_t signature_size;
std::array<uint8_t, MBEDTLS_ECDSA_MAX_LEN> sig;
};
struct SignaturesReceivedAck : RaftHeader
{
Term term;
Index idx;
};
struct NonceRevealMsg : RaftHeader
{
Term term;
Index idx;
Nonce nonce;
};
struct RequestVote : RaftHeader
{
Term term;

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the Apache 2.0 License.
#pragma once
#include "logger_formatters.h"
#include "ring_buffer.h"
#include "thread_ids.h"

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <msgpack/msgpack.hpp>
#include <sstream>
namespace fmt
{
inline std::string uint8_vector_to_hex_string(const std::vector<uint8_t>& v)
{
std::stringstream ss;
for (auto it = v.begin(); it != v.end(); it++)
{
ss << std::hex << static_cast<unsigned>(*it);
}
return ss.str();
}
template <>
struct formatter<std::vector<uint8_t>>
{
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::vector<uint8_t>& p, FormatContext& ctx)
{
return format_to(ctx.out(), uint8_vector_to_hex_string(p));
}
};
template <>
struct formatter<std::array<uint8_t, 32>>
{
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::array<uint8_t, 32>& p, FormatContext& ctx)
{
return format_to(
ctx.out(), uint8_vector_to_hex_string({p.begin(), p.end()}));
}
};
}

Просмотреть файл

@ -146,15 +146,24 @@ namespace kv
std::vector<uint8_t> response;
};
enum class Result
{
FAIL = 0,
OK,
SEND_SIG_RECEIPT_ACK,
SEND_REPLY_AND_NONCE
};
using ResultCallbackHandler = std::function<bool(ResultCallbackArgs)>;
using ResponseCallbackHandler = std::function<bool(ResponseCallbackArgs)>;
virtual ~TxHistory() {}
virtual void append(const std::vector<uint8_t>& replicated) = 0;
virtual void append(const uint8_t* replicated, size_t replicated_size) = 0;
virtual bool verify_and_sign(
virtual Result verify_and_sign(
ccf::PrimarySignature& signature, Term* term = nullptr) = 0;
virtual bool verify(Term* term = nullptr) = 0;
virtual bool verify(
Term* term = nullptr, ccf::PrimarySignature* sig = nullptr) = 0;
virtual void emit_signature() = 0;
virtual crypto::Sha256Hash get_replicated_state_root() = 0;
virtual std::vector<uint8_t> get_receipt(Version v) = 0;
@ -314,6 +323,7 @@ namespace kv
}
virtual void enable_all_domains() {}
virtual uint32_t node_count() = 0;
virtual void open_network() = 0;
virtual void emit_signature() = 0;
virtual ConsensusType type() = 0;

Просмотреть файл

@ -785,10 +785,16 @@ namespace kv
}
auto h = get_history();
bool result;
bool result = true;
if (sig != nullptr)
{
result = h->verify_and_sign(*sig, term_);
auto r = h->verify_and_sign(*sig, term_);
if (
r != kv::TxHistory::Result::OK &&
r != kv::TxHistory::Result::SEND_SIG_RECEIPT_ACK)
{
result = false;
}
}
else
{

Просмотреть файл

@ -708,7 +708,7 @@ TEST_CASE("Deserialise return status")
{
kv::Tx tx(store.next_version());
auto sig_view = tx.get_view(signatures);
ccf::PrimarySignature sigv(0, 2);
ccf::PrimarySignature sigv(0, 2, {0});
sig_view->put(0, sigv);
auto [success, reqid, data] = tx.commit_reserved();
REQUIRE(success == kv::CommitSuccess::OK);
@ -720,7 +720,7 @@ TEST_CASE("Deserialise return status")
{
kv::Tx tx(store.next_version());
auto [sig_view, data_view] = tx.get_view(signatures, data);
ccf::PrimarySignature sigv(0, 2);
ccf::PrimarySignature sigv(0, 2, {0});
sig_view->put(0, sigv);
data_view->put(43, 43);
auto [success, reqid, data] = tx.commit_reserved();

Просмотреть файл

@ -145,6 +145,11 @@ namespace kv
return;
}
uint32_t node_count() override
{
return 0;
}
void emit_signature() override
{
return;

Просмотреть файл

@ -109,12 +109,12 @@ namespace ccf
void append(const uint8_t*, size_t) override {}
bool verify_and_sign(PrimarySignature&, kv::Term*) override
kv::TxHistory::Result verify_and_sign(PrimarySignature&, kv::Term*) override
{
return true;
return kv::TxHistory::Result::OK;
}
bool verify(kv::Term*) override
bool verify(kv::Term*, ccf::PrimarySignature*) override
{
return true;
}
@ -142,7 +142,7 @@ namespace ccf
[txid, this]() {
kv::Tx sig(txid.version);
auto sig_view = sig.get_view(signatures);
PrimarySignature sig_value(id, txid.version);
PrimarySignature sig_value(id, txid.version, {0});
sig_view->put(0, sig_value);
return sig.commit_reserved();
},
@ -576,22 +576,34 @@ namespace ccf
replicated_state_tree.append(rh);
}
bool verify_and_sign(
kv::TxHistory::Result verify_and_sign(
PrimarySignature& sig, kv::Term* term = nullptr) override
{
if (!verify(term))
if (!verify(term, &sig))
{
return false;
return kv::TxHistory::Result::FAIL;
}
sig.node = id;
crypto::Sha256Hash root = replicated_state_tree.get_root();
sig.sig = kp.sign_hash(root.h.data(), root.h.size());
kv::TxHistory::Result result = kv::TxHistory::Result::OK;
return true;
auto progress_tracker = store.get_progress_tracker();
CCF_ASSERT(progress_tracker != nullptr, "progress_tracker is not set");
result = progress_tracker->record_primary(
sig.view,
sig.seqno,
sig.node,
sig.root,
sig.hashed_nonce,
store.get_consensus()->node_count());
sig.node = id;
sig.sig = kp.sign_hash(sig.root.h.data(), sig.root.h.size());
return result;
}
bool verify(kv::Term* term = nullptr) override
bool verify(
kv::Term* term = nullptr, PrimarySignature* signature = nullptr) override
{
kv::Tx tx;
auto [sig_tv, ni_tv] = tx.get_view(signatures, nodes);
@ -601,12 +613,17 @@ namespace ccf
LOG_FAIL_FMT("No signature found in signatures map");
return false;
}
auto sig_value = sig.value();
auto& sig_value = sig.value();
if (term)
{
*term = sig_value.view;
}
if (signature)
{
*signature = sig_value;
}
auto ni = ni_tv->get(sig_value.node);
if (!ni.has_value())
{
@ -628,12 +645,6 @@ namespace ccf
return false;
}
auto progress_tracker = store.get_progress_tracker();
if (progress_tracker)
{
progress_tracker->record_primary(sig_value.view, sig_value.seqno, root);
}
return true;
}
@ -682,10 +693,33 @@ namespace ccf
auto sig_view = sig.get_view(signatures);
crypto::Sha256Hash root = replicated_state_tree.get_root();
auto progress_tracker = store.get_progress_tracker();
if (progress_tracker)
Nonce hashed_nonce;
auto consensus = store.get_consensus();
if (consensus != nullptr && consensus->type() == ConsensusType::BFT)
{
progress_tracker->record_primary(txid.term, txid.version, root);
auto progress_tracker = store.get_progress_tracker();
CCF_ASSERT(
progress_tracker != nullptr, "progress_tracker is not set");
auto r = progress_tracker->record_primary(
txid.term, txid.version, id, root, hashed_nonce);
if (r != kv::TxHistory::Result::OK)
{
throw ccf::ccf_logic_error(fmt::format(
"Expected success when primary added signature to the "
"progress "
"tracker. r:{}, view:{}, seqno:{}",
r,
txid.term,
txid.version));
}
auto h =
progress_tracker->get_my_hashed_nonce(txid.term, txid.version);
std::copy(h.begin(), h.end(), hashed_nonce.begin());
}
else
{
hashed_nonce.fill(0);
}
PrimarySignature sig_value(
@ -695,6 +729,7 @@ namespace ccf
commit_txid.second,
commit_txid.first,
root,
hashed_nonce,
kp.sign_hash(root.h.data(), root.h.size()),
replicated_state_tree.serialise());

Просмотреть файл

@ -3,30 +3,39 @@
#pragma once
#include "ds/json.h"
#include "entities.h"
#include "tls/hash.h"
#include <vector>
namespace ccf
{
using Nonce = std::array<uint8_t, 32>;
struct NodeSignature
{
std::vector<uint8_t> sig;
ccf::NodeId node;
Nonce hashed_nonce;
NodeSignature(const std::vector<uint8_t>& sig_, NodeId node_) :
NodeSignature(
const std::vector<uint8_t>& sig_, NodeId node_, Nonce hashed_nonce_) :
sig(sig_),
node(node_)
node(node_),
hashed_nonce(hashed_nonce_)
{}
NodeSignature(ccf::NodeId node_, Nonce hashed_nonce_) :
node(node_),
hashed_nonce(hashed_nonce_)
{}
NodeSignature(ccf::NodeId node_) : node(node_) {}
NodeSignature() = default;
bool operator==(const NodeSignature& o) const
{
return sig == o.sig;
return sig == o.sig && hashed_nonce == o.hashed_nonce;
}
MSGPACK_DEFINE(sig, node);
MSGPACK_DEFINE(sig, node, hashed_nonce);
};
DECLARE_JSON_TYPE(NodeSignature);
DECLARE_JSON_REQUIRED_FIELDS(NodeSignature, sig, node);
DECLARE_JSON_REQUIRED_FIELDS(NodeSignature, sig, node, hashed_nonce);
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "kv/tx.h"
#include "node_signature.h"
#include "nodes.h"
#include "tls/hash.h"
#include "tls/tls.h"
#include "tls/verifier.h"
@ -19,16 +20,22 @@ namespace ccf
class ProgressTracker
{
public:
ProgressTracker(kv::NodeId id_, ccf::Nodes& nodes_) : id(id_), nodes(nodes_)
ProgressTracker(kv::NodeId id_, ccf::Nodes& nodes_) :
id(id_),
nodes(nodes_),
entropy(tls::create_entropy())
{}
void add_signature(
kv::TxHistory::Result add_signature(
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
kv::NodeId node_id,
uint32_t signature_size,
std::array<uint8_t, MBEDTLS_ECDSA_MAX_LEN>& sig)
std::array<uint8_t, MBEDTLS_ECDSA_MAX_LEN>& sig,
Nonce& hashed_nonce,
uint32_t node_count)
{
LOG_TRACE_FMT("add_signature node_id:{}, seqno:{}", node_id, seqno);
auto it = certificates.find(CertKey(view, seqno));
if (it == certificates.end())
{
@ -53,7 +60,7 @@ namespace ccf
node_id,
view,
seqno));
return;
return kv::TxHistory::Result::FAIL;
}
LOG_TRACE_FMT(
"Signature verification from {} passed, view:{}, seqno:{}",
@ -64,7 +71,7 @@ namespace ccf
std::vector<uint8_t> sig_vec;
CCF_ASSERT(
signature_size < sig.size(),
signature_size <= sig.size(),
fmt::format(
"Invalid signature size, signature_size:{}, sig.size:{}",
signature_size,
@ -72,27 +79,64 @@ namespace ccf
sig_vec.assign(sig.begin(), sig.begin() + signature_size);
auto& cert = it->second;
cert.sigs.insert(std::pair<kv::NodeId, ccf::NodeSignature>(
node_id, {std::move(sig_vec), node_id}));
BftNodeSignature bft_node_sig(std::move(sig_vec), node_id, hashed_nonce);
try_match_unmatched_nonces(cert, bft_node_sig, view, seqno, node_id);
cert.sigs.insert(std::pair<kv::NodeId, BftNodeSignature>(
node_id, std::move(bft_node_sig)));
if (can_send_sig_ack(cert, node_count))
{
return kv::TxHistory::Result::SEND_SIG_RECEIPT_ACK;
}
return kv::TxHistory::Result::OK;
}
void record_primary(
kv::TxHistory::Result record_primary(
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
crypto::Sha256Hash& root)
kv::NodeId node_id,
crypto::Sha256Hash& root,
Nonce& hashed_nonce,
uint32_t node_count = 0)
{
auto n = entropy->random(hashed_nonce.size());
Nonce my_nonce;
std::copy(n.begin(), n.end(), my_nonce.begin());
if (node_id == id)
{
auto h = hash_data(my_nonce);
std::copy(h.begin(), h.end(), hashed_nonce.begin());
}
auto it = certificates.find(CertKey(view, seqno));
if (it == certificates.end())
{
certificates.insert(std::pair<CertKey, CommitCert>(
CertKey(view, seqno), CommitCert(root)));
return;
CommitCert cert(root, my_nonce);
BftNodeSignature bft_node_sig({}, node_id, hashed_nonce);
bft_node_sig.is_primary = true;
try_match_unmatched_nonces(cert, bft_node_sig, view, seqno, node_id);
cert.sigs.insert(
std::pair<kv::NodeId, BftNodeSignature>(node_id, bft_node_sig));
certificates.insert(
std::pair<CertKey, CommitCert>(CertKey(view, seqno), cert));
LOG_TRACE_FMT("Adding new root for view:{}, seqno:{}", view, seqno);
return kv::TxHistory::Result::OK;
}
else
{
// We received some entries before we got the root so we now need to
// verify the signatures
auto& cert = it->second;
cert.root = root;
BftNodeSignature bft_node_sig({}, node_id, hashed_nonce);
bft_node_sig.is_primary = true;
try_match_unmatched_nonces(cert, bft_node_sig, view, seqno, node_id);
cert.sigs.insert(
std::pair<kv::NodeId, BftNodeSignature>(node_id, bft_node_sig));
cert.my_nonce = my_nonce;
cert.have_primary_signature = true;
for (auto& sig : cert.sigs)
{
if (!verify_signature(
@ -109,15 +153,144 @@ namespace ccf
view,
seqno));
}
LOG_TRACE_FMT(
"Signature verification from {} passed, view:{}, seqno:{}",
sig.second.node,
view,
seqno);
}
}
if (it->second.root != root)
auto& cert = it->second;
if (cert.root != root)
{
// NOTE: At this point we have cryptographic proof that someone is being
// dishonest we need to work out what to do.
throw ccf::ccf_logic_error("We have proof someone is being dishonest");
}
if (node_count > 0 && can_send_sig_ack(cert, node_count))
{
return kv::TxHistory::Result::SEND_SIG_RECEIPT_ACK;
}
return kv::TxHistory::Result::OK;
}
kv::TxHistory::Result add_signature_ack(
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
kv::NodeId node_id,
uint32_t node_count = 0)
{
auto it = certificates.find(CertKey(view, seqno));
if (it == certificates.end())
{
// We currently do not know what the root is, so lets save this
// signature and and we will verify the root when we get it from the
// primary
auto r = certificates.insert(
std::pair<CertKey, CommitCert>(CertKey(view, seqno), CommitCert()));
it = r.first;
}
LOG_TRACE_FMT(
"processing recv_signature_received_ack, from:{} view:{}, seqno:{}",
node_id,
view,
seqno);
auto& cert = it->second;
cert.sig_acks.insert(node_id);
if (can_send_reply_and_nonce(cert, node_count))
{
return kv::TxHistory::Result::SEND_REPLY_AND_NONCE;
}
return kv::TxHistory::Result::OK;
}
void add_nonce_reveal(
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
Nonce nonce,
kv::NodeId node_id)
{
bool did_add = false;
auto it = certificates.find(CertKey(view, seqno));
if (it == certificates.end())
{
// We currently do not know what the root is, so lets save this
// signature and and we will verify the root when we get it from the
// primary
auto r = certificates.insert(
std::pair<CertKey, CommitCert>(CertKey(view, seqno), CommitCert()));
it = r.first;
did_add = true;
}
auto& cert = it->second;
auto it_node_sig = cert.sigs.find(node_id);
if (it_node_sig == cert.sigs.end())
{
cert.unmatched_nonces.insert(
std::pair<kv::NodeId, Nonce>(node_id, nonce));
return;
}
BftNodeSignature& sig = it_node_sig->second;
LOG_TRACE_FMT(
"add_nonce_reveal view:{}, seqno:{}, node_id:{}, sig.hashed_nonce:{}, "
" received.nonce:{}, hash(received.nonce):{} did_add:{}",
view,
seqno,
node_id,
sig.hashed_nonce,
nonce,
hash_data(nonce),
did_add);
if (!match_nonces(hash_data(nonce), sig.hashed_nonce))
{
// NOTE: We need to handle this case but for now having this make a
// test fail will be very handy
LOG_FAIL_FMT(
"Nonces do not match add_nonce_reveal view:{}, seqno:{}, node_id:{}, "
"sig.hashed_nonce:{}, "
" received.nonce:{}, hash(received.nonce):{} did_add:{}",
view,
seqno,
node_id,
sig.hashed_nonce,
nonce,
hash_data(nonce),
did_add);
throw ccf::ccf_logic_error(fmt::format(
"nonces do not match verification from {} FAILED, view:{}, seqno:{}",
node_id,
view,
seqno));
}
sig.nonce = nonce;
}
Nonce get_my_nonce(kv::Consensus::View view, kv::Consensus::SeqNo seqno)
{
auto it = certificates.find(CertKey(view, seqno));
if (it == certificates.end())
{
throw ccf::ccf_logic_error(fmt::format(
"Attempting to access unknown nonce, view:{}, seqno:{}",
view,
seqno));
}
return it->second.my_nonce;
}
std::vector<uint8_t> get_my_hashed_nonce(
kv::Consensus::View view, kv::Consensus::SeqNo seqno)
{
Nonce nonce = get_my_nonce(view, seqno);
return hash_data(nonce);
}
void set_node_id(kv::NodeId id_)
@ -128,6 +301,7 @@ namespace ccf
private:
kv::NodeId id;
ccf::Nodes& nodes;
std::shared_ptr<tls::Entropy> entropy;
struct CertKey
{
@ -149,14 +323,36 @@ namespace ccf
}
};
struct BftNodeSignature : public ccf::NodeSignature
{
bool is_primary;
Nonce nonce;
BftNodeSignature(
const std::vector<uint8_t>& sig_, NodeId node_, Nonce hashed_nonce_) :
NodeSignature(sig_, node_, hashed_nonce_),
is_primary(false)
{}
};
struct CommitCert
{
CommitCert(crypto::Sha256Hash& root_) : root(root_) {}
CommitCert(crypto::Sha256Hash& root_, Nonce my_nonce_) :
root(root_),
my_nonce(my_nonce_),
have_primary_signature(true)
{}
CommitCert() = default;
crypto::Sha256Hash root;
// std::map<kv::NodeId, std::vector<uint8_t>> sigs;
std::map<kv::NodeId, ccf::NodeSignature> sigs;
std::map<kv::NodeId, BftNodeSignature> sigs;
std::set<kv::NodeId> sig_acks;
std::map<kv::NodeId, Nonce> unmatched_nonces;
Nonce my_nonce;
bool have_primary_signature = false;
bool ack_sent = false;
bool reply_and_nonce_sent = false;
};
std::map<CertKey, CommitCert> certificates;
@ -180,5 +376,98 @@ namespace ccf
return from_cert->verify_hash(
root.h.data(), root.h.size(), sig, sig_size);
}
std::vector<uint8_t> hash_data(Nonce data)
{
tls::HashBytes hash;
tls::do_hash(
reinterpret_cast<const uint8_t*>(&data),
data.size(),
hash,
MBEDTLS_MD_SHA256);
return hash;
}
void try_match_unmatched_nonces(
CommitCert& cert,
BftNodeSignature& bft_node_sig,
kv::Consensus::View view,
kv::Consensus::SeqNo seqno,
kv::NodeId node_id)
{
auto it_unmatched_nonces = cert.unmatched_nonces.find(node_id);
if (it_unmatched_nonces != cert.unmatched_nonces.end())
{
if (!match_nonces(
hash_data(it_unmatched_nonces->second),
bft_node_sig.hashed_nonce))
{
// NOTE: We need to handle this case but for now having this make a
// test fail will be very handy
LOG_FAIL_FMT(
"Nonces do not match add_nonce_reveal view:{}, seqno:{}, "
"node_id:{}, "
"sig.hashed_nonce:{}, "
" received.nonce:{}, hash(received.nonce):{}",
view,
seqno,
node_id,
bft_node_sig.hashed_nonce,
it_unmatched_nonces->second,
hash_data(it_unmatched_nonces->second));
throw ccf::ccf_logic_error(fmt::format(
"nonces do not match verification from {} FAILED, view:{}, "
"seqno:{}",
node_id,
view,
seqno));
}
bft_node_sig.nonce = it_unmatched_nonces->second;
cert.unmatched_nonces.erase(it_unmatched_nonces);
}
}
bool match_nonces(std::vector<uint8_t> n_1, Nonce n_2)
{
if (n_1.size() != n_2.size())
{
return false;
}
return std::equal(n_1.begin(), n_1.end(), n_2.begin());
}
uint32_t get_message_threshold(uint32_t node_count)
{
uint32_t f = 0;
for (; 3 * f + 1 < node_count; ++f)
;
return 2 * f + 1;
}
bool can_send_sig_ack(CommitCert& cert, uint32_t node_count)
{
if (
cert.sigs.size() >= get_message_threshold(node_count) &&
!cert.ack_sent && cert.have_primary_signature)
{
cert.ack_sent = true;
return true;
}
return false;
}
bool can_send_reply_and_nonce(CommitCert& cert, uint32_t node_count)
{
if (
cert.sig_acks.size() >= get_message_threshold(node_count) &&
!cert.reply_and_nonce_sent && cert.ack_sent)
{
cert.reply_and_nonce_sent = true;
return true;
}
return false;
}
};
}

Просмотреть файл

@ -31,8 +31,8 @@ namespace ccf
PrimarySignature() {}
PrimarySignature(ccf::NodeId node_, ObjectId seqno_) :
NodeSignature(node_),
PrimarySignature(ccf::NodeId node_, ObjectId seqno_, Nonce hashed_nonce) :
NodeSignature(node_, hashed_nonce),
seqno(seqno_)
{}
@ -45,9 +45,10 @@ namespace ccf
ObjectId commit_seqno_,
ObjectId commit_view_,
const crypto::Sha256Hash root_,
Nonce hashed_nonce_,
const std::vector<uint8_t>& sig_,
const std::vector<uint8_t>& tree_) :
NodeSignature(sig_, node_),
NodeSignature(sig_, node_, hashed_nonce_),
seqno(seqno_),
view(view_),
commit_seqno(commit_seqno_),

Просмотреть файл

@ -61,6 +61,7 @@ TEST_CASE("Check signature verification")
{
auto encryptor = std::make_shared<kv::NullTxEncryptor>();
kv::Store primary_store;
primary_store.set_encryptor(encryptor);
auto& primary_nodes = primary_store.create<ccf::Nodes>(
ccf::Tables::NODES, kv::SecurityDomain::PUBLIC);
@ -113,7 +114,7 @@ TEST_CASE("Check signature verification")
{
kv::Tx txs;
auto tx = txs.get_view(primary_signatures);
ccf::PrimarySignature bogus(0, 0);
ccf::PrimarySignature bogus(0, 0, {0});
bogus.sig = std::vector<uint8_t>(MBEDTLS_ECDSA_MAX_LEN, 1);
tx->put(0, bogus);
REQUIRE(txs.commit() == kv::CommitSuccess::NO_REPLICATE);

Просмотреть файл

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "node/progress_tracker.h"
#include "kv/store.h"
#include "node/nodes.h"
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
#include <string>
TEST_CASE("Ordered Execution")
{
kv::Store store;
auto& nodes =
store.create<ccf::Nodes>(ccf::Tables::NODES, kv::SecurityDomain::PUBLIC);
kv::Consensus::View view = 0;
kv::Consensus::SeqNo seqno = 0;
uint32_t node_count = 4;
crypto::Sha256Hash root;
std::array<uint8_t, MBEDTLS_ECDSA_MAX_LEN> sig;
ccf::Nonce nonce;
INFO("Adding signature");
{
auto pt = std::make_unique<ccf::ProgressTracker>(0, nodes);
auto result = pt->add_signature(
view, seqno, 1, MBEDTLS_ECDSA_MAX_LEN, sig, nonce, node_count);
REQUIRE(result == kv::TxHistory::Result::OK);
REQUIRE_THROWS(pt->record_primary(view, seqno, 0, root, nonce, node_count));
}
INFO("Waits for signature tx");
{
auto pt = std::make_unique<ccf::ProgressTracker>(0, nodes);
for (size_t i = 0; i < node_count; ++i)
{
auto result = pt->add_signature_ack(view, seqno, i, node_count);
REQUIRE(result == kv::TxHistory::Result::OK);
}
}
}