Skip to content
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

audi timestamp #2151

Merged
merged 5 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion cmake/functions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function(compile_proto_to_cpp PROTO_LIBRARY_NAME PB_H PB_CC PROTO)
COMMAND ${GEN_COMMAND}
ARGS -I${PROJECT_SOURCE_DIR}/core -I${GEN_ARGS} --cpp_out=${SCHEMA_OUT_DIR} ${PROTO_ABS}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PROTOBUF_DEPENDS}
DEPENDS ${PROTOBUF_DEPENDS} ${PROTO_ABS}
VERBATIM
)

Expand Down
14 changes: 11 additions & 3 deletions core/authority_discovery/protobuf/authority_discovery.v2.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@

syntax = "proto3";

package authority_discovery.v2;
package authority_discovery_v3;

// First we need to serialize the addresses in order to be able to sign them.
message AuthorityRecord {
// Possibly multiple `MultiAddress`es through which the node can be
// Possibly multiple `MultiAddress`es through which the node can be reached.
repeated bytes addresses = 1;
// Information about the creation time of the record
TimestampInfo creation_time = 2;
}

message PeerSignature {
bytes signature = 1;
bytes public_key = 2;
}

// Information regarding the creation data of the record
message TimestampInfo {
// Time since UNIX_EPOCH in nanoseconds, scale encoded
bytes timestamp = 1;
}

// Then we need to serialize the authority record and signature to send them over the wire.
message SignedAuthorityRecord {
bytes record = 1;
bytes auth_signature = 2;
// Even if there are multiple `record.addresses`, all of them have the same peer id.
// Old versions are missing this field. It is optional in order to provide compatibility both ways.
// Old versions are missing this field. It is optional in order to provide compatibility both ways.
PeerSignature peer_signature = 3;
}
11 changes: 9 additions & 2 deletions core/authority_discovery/publisher/address_publisher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "authority_discovery/protobuf/authority_discovery.v2.pb.h"

#include "authority_discovery/timestamp.hpp"
#include "crypto/sha/sha256.hpp"

#define _PB_SPAN(f) \
Expand Down Expand Up @@ -128,17 +129,23 @@ namespace kagome::authority_discovery {
OUTCOME_TRY(address2, libp2p::multi::Multiaddress::create(s));
addresses.emplace(std::move(address2));
}
::authority_discovery::v2::AuthorityRecord record;
::authority_discovery_v3::AuthorityRecord record;
for (const auto &address : addresses) {
PB_SPAN_ADD(record, addresses, address.getBytesAddress());
}
TimestampScale time{std::chrono::nanoseconds{
std::chrono::system_clock::now().time_since_epoch()}
.count()};
PB_SPAN_SET(*record.mutable_creation_time(),
timestamp,
scale::encode(time).value());

auto record_pb = pbEncodeVec(record);
OUTCOME_TRY(signature, ed_crypto_provider_->sign(*libp2p_key_, record_pb));
OUTCOME_TRY(auth_signature,
sr_crypto_provider_->sign(*audi_key, record_pb));

::authority_discovery::v2::SignedAuthorityRecord signed_record;
::authority_discovery_v3::SignedAuthorityRecord signed_record;
PB_SPAN_SET(signed_record, auth_signature, auth_signature);
PB_SPAN_SET(signed_record, record, record_pb);
auto &ps = *signed_record.mutable_peer_signature();
Expand Down
91 changes: 77 additions & 14 deletions core/authority_discovery/query/query_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::authority_discovery, QueryImpl::Error, e) {
return "Inconsistent peer id";
case E::INVALID_SIGNATURE:
return "Invalid signature";
case E::KADEMLIA_OUTDATED_VALUE:
return "Kademlia outdated value";
}
return "unknown error (authority_discovery::QueryImpl::Error)";
}
Expand All @@ -41,7 +43,7 @@ namespace kagome::authority_discovery {
std::shared_ptr<libp2p::crypto::CryptoProvider> libp2p_crypto_provider,
std::shared_ptr<libp2p::crypto::marshaller::KeyMarshaller> key_marshaller,
libp2p::Host &host,
std::shared_ptr<libp2p::protocol::kademlia::Kademlia> kademlia,
LazySPtr<libp2p::protocol::kademlia::Kademlia> kademlia,
std::shared_ptr<libp2p::basic::Scheduler> scheduler)
: block_tree_{std::move(block_tree)},
authority_discovery_api_{std::move(authority_discovery_api)},
Expand Down Expand Up @@ -80,7 +82,7 @@ namespace kagome::authority_discovery {
std::unique_lock lock{mutex_};
auto it = auth_to_peer_cache_.find(authority);
if (it != auth_to_peer_cache_.end()) {
return it->second;
return it->second.peer;
}
return std::nullopt;
}
Expand All @@ -95,11 +97,52 @@ namespace kagome::authority_discovery {
return std::nullopt;
}

outcome::result<void> QueryImpl::validate(
const libp2p::protocol::kademlia::Key &key,
const libp2p::protocol::kademlia::Value &value) {
std::unique_lock lock{mutex_};
auto id = hashToAuth(key);
if (not id) {
lock.unlock();
return kademlia_validator_.validate(key, value);
}
auto r = add(*id, value);
if (not r) {
SL_DEBUG(log_, "Can't add: {}", r.error());
}
return r;
}

outcome::result<size_t> QueryImpl::select(
const libp2p::protocol::kademlia::Key &key,
const std::vector<libp2p::protocol::kademlia::Value> &values) {
std::unique_lock lock{mutex_};
auto id = hashToAuth(key);
if (not id) {
lock.unlock();
return kademlia_validator_.select(key, values);
}
auto it = auth_to_peer_cache_.find(*id);
if (it != auth_to_peer_cache_.end()) {
auto it_value = std::find(values.begin(), values.end(), it->second.raw);
if (it_value != values.end()) {
return it_value - values.begin();
}
}
return Error::KADEMLIA_OUTDATED_VALUE;
}

outcome::result<void> QueryImpl::update() {
std::unique_lock lock{mutex_};
OUTCOME_TRY(
authorities,
authority_discovery_api_->authorities(block_tree_->bestBlock().hash));
for (auto &id : authorities) {
auto it = hash_to_auth_.find(id);
if (it == hash_to_auth_.end()) {
turuslan marked this conversation as resolved.
Show resolved Hide resolved
hash_to_auth_.emplace(crypto::sha256(id), id);
}
}
OUTCOME_TRY(local_keys,
key_store_->sr25519().getPublicKeys(
crypto::KeyTypes::AUTHORITY_DISCOVERY));
Expand Down Expand Up @@ -132,6 +175,17 @@ namespace kagome::authority_discovery {
return outcome::success();
}

std::optional<primitives::AuthorityDiscoveryId> QueryImpl::hashToAuth(
BufferView key) const {
if (auto r = Hash256::fromSpan(key)) {
auto it = hash_to_auth_.find(r.value());
if (it != hash_to_auth_.end()) {
return it->second;
}
}
return std::nullopt;
}

void QueryImpl::pop() {
while (active_ < kMaxActiveRequests) {
if (queue_.empty()) {
Expand All @@ -144,19 +198,11 @@ namespace kagome::authority_discovery {
common::Buffer hash{crypto::sha256(authority)};
scheduler_->schedule([=, this, wp{weak_from_this()}] {
if (auto self = wp.lock()) {
std::ignore = kademlia_->getValue(
std::ignore = kademlia_.get()->getValue(
hash, [=, this](outcome::result<std::vector<uint8_t>> res) {
std::unique_lock lock{mutex_};
--active_;
pop();
if (res.has_error()) {
SL_DEBUG(log_, "Kademlia can't get value: {}", res.error());
return;
}
auto r = add(authority, std::move(res.value()));
if (not r) {
SL_DEBUG(log_, "Can't add: {}", r.error());
}
});
}
});
Expand All @@ -167,7 +213,12 @@ namespace kagome::authority_discovery {
const primitives::AuthorityDiscoveryId &authority,
outcome::result<std::vector<uint8_t>> _res) {
OUTCOME_TRY(signed_record_pb, _res);
::authority_discovery::v2::SignedAuthorityRecord signed_record;
auto it = auth_to_peer_cache_.find(authority);
if (it != auth_to_peer_cache_.end()
and signed_record_pb == it->second.raw) {
return outcome::success();
}
::authority_discovery_v3::SignedAuthorityRecord signed_record;
if (not signed_record.ParseFromArray(signed_record_pb.data(),
signed_record_pb.size())) {
return Error::DECODE_ERROR;
Expand All @@ -185,13 +236,23 @@ namespace kagome::authority_discovery {
crypto::Sr25519Signature::fromSpan(
str2byte(signed_record.auth_signature())));

::authority_discovery::v2::AuthorityRecord record;
::authority_discovery_v3::AuthorityRecord record;
if (not record.ParseFromString(signed_record.record())) {
return Error::DECODE_ERROR;
}
if (record.addresses().empty()) {
return Error::NO_ADDRESSES;
}
std::optional<Timestamp> time{};
if (record.has_creation_time()) {
OUTCOME_TRY(tmp,
scale::decode<TimestampScale>(
qtils::str2byte(record.creation_time().timestamp())));
time = *tmp;
if (it != auth_to_peer_cache_.end() and time <= it->second.time) {
return outcome::success();
}
}
libp2p::peer::PeerInfo peer{std::move(peer_id), {}};
auto peer_id_str = peer.id.toBase58();
for (auto &pb : record.addresses()) {
Expand Down Expand Up @@ -226,7 +287,9 @@ namespace kagome::authority_discovery {
peer.id, peer.addresses, libp2p::peer::ttl::kRecentlyConnected);

peer_to_auth_cache_.insert_or_assign(peer.id, authority);
auth_to_peer_cache_.insert_or_assign(authority, std::move(peer));
auth_to_peer_cache_.insert_or_assign(
authority,
Authority{std::move(signed_record_pb), time, std::move(peer)});

return outcome::success();
}
Expand Down
29 changes: 26 additions & 3 deletions core/authority_discovery/query/query_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,33 @@

#include "application/app_state_manager.hpp"
#include "authority_discovery/interval.hpp"
#include "authority_discovery/timestamp.hpp"
#include "blockchain/block_tree.hpp"
#include "crypto/key_store.hpp"
#include "crypto/sr25519_provider.hpp"
#include "injector/lazy.hpp"
#include "log/logger.hpp"
#include "runtime/runtime_api/authority_discovery_api.hpp"

#include <libp2p/crypto/crypto_provider.hpp>
#include <libp2p/crypto/key_marshaller.hpp>
#include <libp2p/host/host.hpp>
#include <libp2p/protocol/kademlia/impl/validator_default.hpp>
#include <libp2p/protocol/kademlia/kademlia.hpp>
#include <mutex>
#include <random>

namespace kagome::authority_discovery {
class QueryImpl : public Query,
public libp2p::protocol::kademlia::Validator,
public std::enable_shared_from_this<QueryImpl> {
public:
enum class Error {
DECODE_ERROR = 1,
NO_ADDRESSES,
INCONSISTENT_PEER_ID,
INVALID_SIGNATURE,
KADEMLIA_OUTDATED_VALUE,
};

QueryImpl(
Expand All @@ -44,7 +49,7 @@ namespace kagome::authority_discovery {
std::shared_ptr<libp2p::crypto::marshaller::KeyMarshaller>
key_marshaller,
libp2p::Host &host,
std::shared_ptr<libp2p::protocol::kademlia::Kademlia> kademlia,
LazySPtr<libp2p::protocol::kademlia::Kademlia> kademlia,
std::shared_ptr<libp2p::basic::Scheduler> scheduler);

bool start();
Expand All @@ -55,9 +60,25 @@ namespace kagome::authority_discovery {
std::optional<primitives::AuthorityDiscoveryId> get(
const libp2p::peer::PeerId &peer_id) const override;

// interface: Validator
outcome::result<void> validate(
const libp2p::protocol::kademlia::Key &,
const libp2p::protocol::kademlia::Value &) override;
outcome::result<size_t> select(
const libp2p::protocol::kademlia::Key &,
const std::vector<libp2p::protocol::kademlia::Value> &) override;

outcome::result<void> update();

private:
struct Authority {
Buffer raw;
std::optional<Timestamp> time;
libp2p::peer::PeerInfo peer;
};

std::optional<primitives::AuthorityDiscoveryId> hashToAuth(
BufferView key) const;
void pop();
outcome::result<void> add(const primitives::AuthorityDiscoveryId &authority,
outcome::result<std::vector<uint8_t>> _res);
Expand All @@ -69,13 +90,15 @@ namespace kagome::authority_discovery {
std::shared_ptr<libp2p::crypto::CryptoProvider> libp2p_crypto_provider_;
std::shared_ptr<libp2p::crypto::marshaller::KeyMarshaller> key_marshaller_;
libp2p::Host &host_;
std::shared_ptr<libp2p::protocol::kademlia::Kademlia> kademlia_;
LazySPtr<libp2p::protocol::kademlia::Kademlia> kademlia_;
std::shared_ptr<libp2p::basic::Scheduler> scheduler_;
ExpIncInterval interval_;

mutable std::mutex mutex_;
std::default_random_engine random_;
std::unordered_map<primitives::AuthorityDiscoveryId, libp2p::peer::PeerInfo>
libp2p::protocol::kademlia::ValidatorDefault kademlia_validator_;
std::unordered_map<Hash256, primitives::AuthorityDiscoveryId> hash_to_auth_;
std::unordered_map<primitives::AuthorityDiscoveryId, Authority>
auth_to_peer_cache_;
std::unordered_map<libp2p::peer::PeerId, primitives::AuthorityDiscoveryId>
peer_to_auth_cache_;
Expand Down
14 changes: 14 additions & 0 deletions core/authority_discovery/timestamp.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include "scale/big_fixed_integers.hpp"

namespace kagome::authority_discovery {
using Timestamp = scale::uint128_t;
using TimestampScale = scale::Fixed<Timestamp>;
} // namespace kagome::authority_discovery
9 changes: 6 additions & 3 deletions core/crypto/sr25519/sr25519_provider_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "crypto/sr25519/sr25519_provider_impl.hpp"

#include "crypto/sr25519_types.hpp"
#include "utils/non_null_dangling.hpp"

namespace kagome::crypto {
outcome::result<Sr25519Keypair> Sr25519ProviderImpl::generateKeypair(
Expand Down Expand Up @@ -39,7 +40,7 @@ namespace kagome::crypto {
sr25519_sign(signature.data(),
keypair.public_key.data(),
keypair.secret_key.unsafeBytes().data(),
message.data(),
nonNullDangling(message),
message.size());
} catch (...) {
return Sr25519ProviderError::SIGN_UNKNOWN_ERROR;
Expand Down Expand Up @@ -68,8 +69,10 @@ namespace kagome::crypto {
const Sr25519PublicKey &public_key) const {
bool result = false;
try {
result = sr25519_verify(
signature.data(), message.data(), message.size(), public_key.data());
result = sr25519_verify(signature.data(),
nonNullDangling(message),
message.size(),
public_key.data());
} catch (...) {
return Sr25519ProviderError::SIGN_UNKNOWN_ERROR;
}
Expand Down
2 changes: 2 additions & 0 deletions core/injector/application_injector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ namespace {
network::make_protocols("/{}/kad", genesis, chain_spec);
kademlia_config.maxBucketSize = 1000;
kademlia_config.randomWalk.enabled = false;
kademlia_config.valueLookupsQuorum = 4;

return std::make_shared<libp2p::protocol::kademlia::Config>(
std::move(kademlia_config));
Expand Down Expand Up @@ -851,6 +852,7 @@ namespace {
di::bind<api::InternalApi>.template to<api::InternalApiImpl>(),
di::bind<consensus::babe::BabeConfigRepository>.template to<consensus::babe::BabeConfigRepositoryImpl>(),
di::bind<authority_discovery::Query>.template to<authority_discovery::QueryImpl>(),
di::bind<libp2p::protocol::kademlia::Validator>.template to<authority_discovery::QueryImpl>()[boost::di::override],
di::bind<crypto::SessionKeys>.template to<crypto::SessionKeysImpl>(),
di::bind<network::SyncProtocol>.template to<network::SyncProtocolImpl>(),
di::bind<network::StateProtocol>.template to<network::StateProtocolImpl>(),
Expand Down
Loading
Loading