mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
29
validator-session/CMakeLists.txt
Normal file
29
validator-session/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||
|
||||
if (NOT OPENSSL_FOUND)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
endif()
|
||||
|
||||
set(VALIDATOR_SESSION_SOURCE
|
||||
persistent-vector.cpp
|
||||
validator-session-description.cpp
|
||||
validator-session-state.cpp
|
||||
validator-session.cpp
|
||||
|
||||
persistent-vector.h
|
||||
validator-session-description.h
|
||||
validator-session-description.hpp
|
||||
validator-session-state.h
|
||||
validator-session.h
|
||||
validator-session.hpp
|
||||
)
|
||||
|
||||
add_library(validatorsession STATIC ${VALIDATOR_SESSION_SOURCE})
|
||||
|
||||
target_include_directories(validatorsession PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
)
|
||||
target_link_libraries(validatorsession PRIVATE tdutils tdactor adnl rldp tl_api dht tdfec
|
||||
overlay catchain)
|
62
validator-session/persistent-vector.cpp
Normal file
62
validator-session/persistent-vector.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "persistent-vector.h"
|
||||
|
||||
#include "auto/tl/ton_api.h"
|
||||
|
||||
#include "adnl/utils.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
HashType get_vector_hash(ValidatorSessionDescription& desc, std::vector<HashType>&& value) {
|
||||
std::vector<td::int32> v;
|
||||
v.resize(value.size());
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
v[i] = value[i];
|
||||
}
|
||||
auto obj = ton::create_tl_object<ton::ton_api::hashable_vector>(std::move(v));
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::uint32& value) {
|
||||
auto obj = ton::create_tl_object<ton::ton_api::hashable_int32>(value);
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::Bits256& value) {
|
||||
auto obj = ton::create_tl_object<ton::ton_api::hashable_int256>(value);
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::uint64& value) {
|
||||
auto obj = ton::create_tl_object<ton::ton_api::hashable_int64>(value);
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const bool& value) {
|
||||
auto obj = ton::create_tl_object<ton::ton_api::hashable_bool>(value);
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::BufferSlice& value) {
|
||||
auto obj = ton::create_tl_object<ton::ton_api::hashable_bytes>(value.clone());
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
808
validator-session/persistent-vector.h
Normal file
808
validator-session/persistent-vector.h
Normal file
|
@ -0,0 +1,808 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "td/utils/int_types.h"
|
||||
#include "td/utils/buffer.h"
|
||||
|
||||
#include "adnl/utils.hpp"
|
||||
|
||||
#include "validator-session-description.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
using HashType = ValidatorSessionDescription::HashType;
|
||||
|
||||
template <class T>
|
||||
inline HashType get_vs_hash(ValidatorSessionDescription& desc, const T& value) {
|
||||
return value.get_hash(desc);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline HashType get_vs_hash(ValidatorSessionDescription& desc, const T* value) {
|
||||
return value ? value->get_hash(desc) : desc.zero_hash();
|
||||
}
|
||||
|
||||
HashType get_vector_hash(ValidatorSessionDescription& desc, std::vector<HashType>&& value);
|
||||
HashType get_pair_hash(ValidatorSessionDescription& desc, const HashType& left, const HashType& right);
|
||||
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const bool& value);
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::uint32& value);
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::uint64& value);
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::Bits256& value);
|
||||
HashType get_vs_hash(ValidatorSessionDescription& desc, const td::BufferSlice& value);
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline HashType get_vs_hash(ValidatorSessionDescription& desc, const std::pair<T1, T2>& value) {
|
||||
return get_pair_hash(desc, get_vs_hash(value.first), get_vs_hash(value.second));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline HashType get_vs_hash(ValidatorSessionDescription& desc, const std::vector<T>& value) {
|
||||
std::vector<HashType> v;
|
||||
v.resize(value.size());
|
||||
for (size_t i = 0; i < value.size(); i++) {
|
||||
v[i] = get_vs_hash(desc, value[i]);
|
||||
}
|
||||
return get_vector_hash(desc, std::move(v));
|
||||
}
|
||||
inline HashType get_vs_hash(ValidatorSessionDescription& desc, const std::vector<bool>& value) {
|
||||
std::vector<HashType> v;
|
||||
v.resize(value.size());
|
||||
for (size_t i = 0; i < value.size(); i++) {
|
||||
bool b = value[i];
|
||||
v[i] = get_vs_hash(desc, b);
|
||||
}
|
||||
return get_vector_hash(desc, std::move(v));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline HashType get_vs_hash(ValidatorSessionDescription& desc, td::uint32 size, const T* value) {
|
||||
std::vector<HashType> v;
|
||||
v.resize(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
v[i] = get_vs_hash(desc, value[i]);
|
||||
}
|
||||
return get_vector_hash(desc, std::move(v));
|
||||
}
|
||||
|
||||
inline bool move_to_persistent(ValidatorSessionDescription& desc, bool v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
inline td::uint32 move_to_persistent(ValidatorSessionDescription& desc, td::uint32 v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
inline td::uint64 move_to_persistent(ValidatorSessionDescription& desc, td::uint64 v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const T* move_to_persistent(ValidatorSessionDescription& desc, const T* v) {
|
||||
return T::move_to_persistent(desc, v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class CntVector : public ValidatorSessionDescription::RootObject {
|
||||
public:
|
||||
static HashType create_hash(ValidatorSessionDescription& desc, std::vector<T>& value) {
|
||||
auto obj = create_tl_object<ton_api::hashable_cntVector>(get_vs_hash(desc, value));
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 size, const T* value) {
|
||||
auto obj = create_tl_object<ton_api::hashable_cntVector>(get_vs_hash(desc, size, value));
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
static bool compare(const RootObject* r, td::uint32 size, const T* data, HashType hash) {
|
||||
if (!r || r->get_size() < sizeof(CntVector)) {
|
||||
return false;
|
||||
}
|
||||
auto R = static_cast<const CntVector*>(r);
|
||||
if (R->data_size_ != size * sizeof(T) || R->hash_ != hash) {
|
||||
return false;
|
||||
}
|
||||
for (td::uint32 i = 0; i < size; i++) {
|
||||
if (R->data_[i] != data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool compare(const RootObject* r, const std::vector<T>& data, HashType hash) {
|
||||
if (!r || r->get_size() < sizeof(CntVector)) {
|
||||
return false;
|
||||
}
|
||||
auto R = static_cast<const CntVector*>(r);
|
||||
if (R->data_size_ != sizeof(T) * data.size() || R->hash_ != hash) {
|
||||
return false;
|
||||
}
|
||||
for (td::uint32 i = 0; i < data.size(); i++) {
|
||||
if (R->data_[i] != data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static const CntVector* lookup(ValidatorSessionDescription& desc, std::vector<T>& value, HashType hash, bool temp) {
|
||||
auto r = desc.get_by_hash(hash, temp);
|
||||
if (compare(r, value, hash)) {
|
||||
desc.on_reuse();
|
||||
return static_cast<const CntVector*>(r);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const CntVector* lookup(ValidatorSessionDescription& desc, td::uint32 size, const T* data, HashType hash,
|
||||
bool temp) {
|
||||
auto r = desc.get_by_hash(hash, temp);
|
||||
if (compare(r, size, data, hash)) {
|
||||
desc.on_reuse();
|
||||
return static_cast<const CntVector*>(r);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const CntVector* create(ValidatorSessionDescription& desc, std::vector<T> value) {
|
||||
if (value.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto hash = create_hash(desc, value);
|
||||
auto r = lookup(desc, value, hash, true);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto size = static_cast<td::uint32>(value.size());
|
||||
auto data = static_cast<T*>(desc.alloc(sizeof(T) * size, 8, true));
|
||||
for (td::uint32 i = 0; i < size; i++) {
|
||||
data[i] = value[i];
|
||||
}
|
||||
|
||||
return new (desc, true) CntVector{desc, size, data, hash};
|
||||
}
|
||||
static const CntVector* create(ValidatorSessionDescription& desc, td::uint32 size, const T* value) {
|
||||
if (!size) {
|
||||
return nullptr;
|
||||
}
|
||||
auto hash = create_hash(desc, size, value);
|
||||
auto r = lookup(desc, size, value, hash, true);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return new (desc, true) CntVector{desc, size, value, hash};
|
||||
}
|
||||
static const CntVector* move_to_persistent(ValidatorSessionDescription& desc, const CntVector* b) {
|
||||
if (desc.is_persistent(b)) {
|
||||
return b;
|
||||
}
|
||||
std::vector<T> v;
|
||||
v.resize(b->size());
|
||||
for (td::uint32 i = 0; i < b->size(); i++) {
|
||||
v[i] = ton::validatorsession::move_to_persistent(desc, b->data_[i]);
|
||||
}
|
||||
auto r = lookup(desc, v, b->hash_, false);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
auto data = static_cast<T*>(desc.alloc(sizeof(T) * b->size(), 8, false));
|
||||
for (td::uint32 i = 0; i < b->size(); i++) {
|
||||
data[i] = v[i];
|
||||
}
|
||||
|
||||
return new (desc, false) CntVector{desc, b->size(), data, b->hash_};
|
||||
}
|
||||
static const CntVector* merge(ValidatorSessionDescription& desc, const CntVector* l, const CntVector* r,
|
||||
std::function<T(T, T)> merge_f, bool merge_all = false) {
|
||||
if (!merge_all) {
|
||||
if (!l) {
|
||||
return r;
|
||||
}
|
||||
if (!r) {
|
||||
return l;
|
||||
}
|
||||
if (l == r) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
auto sz = std::max(l->size(), r->size());
|
||||
bool ret_left = true;
|
||||
bool ret_right = true;
|
||||
for (td::uint32 i = 0; i < sz; i++) {
|
||||
if (i >= l->size()) {
|
||||
ret_left = false;
|
||||
break;
|
||||
} else if (i >= r->size()) {
|
||||
ret_right = false;
|
||||
break;
|
||||
} else if (l->at(i) != r->at(i)) {
|
||||
if (l->at(i)) {
|
||||
ret_right = false;
|
||||
}
|
||||
if (r->at(i)) {
|
||||
ret_left = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!merge_all && ret_left) {
|
||||
return l;
|
||||
}
|
||||
if (!merge_all && ret_right) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto v = static_cast<T*>(desc.alloc(sizeof(T) * sz, 8, true));
|
||||
for (td::uint32 i = 0; i < sz; i++) {
|
||||
if (i >= l->size()) {
|
||||
if (!merge_all) {
|
||||
v[i] = r->at(i);
|
||||
} else {
|
||||
v[i] = merge_f(r->at(i), r->at(i));
|
||||
}
|
||||
} else if (i >= r->size()) {
|
||||
if (!merge_all) {
|
||||
v[i] = l->at(i);
|
||||
} else {
|
||||
v[i] = merge_f(l->at(i), l->at(i));
|
||||
}
|
||||
} else {
|
||||
v[i] = merge_f(l->at(i), r->at(i));
|
||||
}
|
||||
}
|
||||
|
||||
return create(desc, sz, v);
|
||||
}
|
||||
static const CntVector* modify(ValidatorSessionDescription& desc, const CntVector* l, std::function<T(T)> mod_f) {
|
||||
if (!l) {
|
||||
return l;
|
||||
}
|
||||
auto sz = l->size();
|
||||
|
||||
auto v = static_cast<T*>(desc.alloc(sizeof(T) * sz, 8, true));
|
||||
for (td::uint32 i = 0; i < sz; i++) {
|
||||
v[i] = mod_f(l->at(i));
|
||||
}
|
||||
|
||||
return create(desc, sz, v);
|
||||
}
|
||||
static const CntVector* change(ValidatorSessionDescription& desc, const CntVector* l, td::uint32 idx, T value) {
|
||||
auto sz = l->size();
|
||||
auto v = static_cast<T*>(desc.alloc(sizeof(T) * sz, 8, true));
|
||||
std::memcpy(v, l->data_, sizeof(T) * sz);
|
||||
v[idx] = std::move(value);
|
||||
return create(desc, sz, v);
|
||||
}
|
||||
static const CntVector* push(ValidatorSessionDescription& desc, const CntVector* l, td::uint32 idx, T value) {
|
||||
td::uint32 sz = l ? l->size() : 0;
|
||||
CHECK(idx == sz);
|
||||
sz++;
|
||||
auto v = static_cast<T*>(desc.alloc(sizeof(T) * sz, 8, true));
|
||||
if (l) {
|
||||
std::memcpy(v, l->data_, sizeof(T) * (sz - 1));
|
||||
}
|
||||
v[idx] = std::move(value);
|
||||
return create(desc, sz, v);
|
||||
}
|
||||
CntVector(ValidatorSessionDescription& desc, td::uint32 data_size, const T* data, HashType hash)
|
||||
: RootObject{sizeof(CntVector)}
|
||||
, data_size_(static_cast<td::uint32>(data_size * sizeof(T)))
|
||||
, data_(data)
|
||||
, hash_(std::move(hash)) {
|
||||
desc.update_hash(this, hash_);
|
||||
}
|
||||
td::uint32 size() const {
|
||||
return static_cast<td::uint32>(data_size_ / sizeof(T));
|
||||
}
|
||||
auto data() const {
|
||||
return data_;
|
||||
}
|
||||
auto get_hash(ValidatorSessionDescription& desc) const {
|
||||
return hash_;
|
||||
}
|
||||
T at(td::uint32 idx) const {
|
||||
CHECK(idx < size());
|
||||
return data_[idx];
|
||||
}
|
||||
//const T& at(size_t idx) const;
|
||||
|
||||
private:
|
||||
const td::uint32 data_size_;
|
||||
const T* data_;
|
||||
const HashType hash_;
|
||||
};
|
||||
|
||||
template <>
|
||||
class CntVector<bool> : public ValidatorSessionDescription::RootObject {
|
||||
private:
|
||||
static bool get_bit(const td::uint32* value, td::uint32 idx) {
|
||||
return (value[idx / 32] & (1u << (idx % 32))) != 0;
|
||||
}
|
||||
static void set_bit(td::uint32* value, td::uint32 idx, bool v) {
|
||||
if (v) {
|
||||
value[idx / 32] |= (1u << (idx % 32));
|
||||
} else {
|
||||
value[idx / 32] &= ~static_cast<td::uint32>((1u << (idx % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static HashType create_hash(ValidatorSessionDescription& desc, std::vector<bool>& value) {
|
||||
CHECK(value.size() % 32 == 0);
|
||||
auto b = new td::uint32[value.size() / 32];
|
||||
for (td::uint32 i = 0; i < value.size(); i++) {
|
||||
set_bit(b, i, value[i]);
|
||||
}
|
||||
auto hash = create_hash(desc, static_cast<td::uint32>(value.size()), b);
|
||||
delete[] b;
|
||||
return hash;
|
||||
}
|
||||
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 size, const td::uint32* value) {
|
||||
return desc.compute_hash(td::Slice(reinterpret_cast<const td::uint8*>(value), size / 8));
|
||||
}
|
||||
static bool compare(const RootObject* r, td::uint32 size, const td::uint32* data, HashType hash) {
|
||||
CHECK(size % 32 == 0);
|
||||
if (!r || r->get_size() < sizeof(CntVector)) {
|
||||
return false;
|
||||
}
|
||||
auto R = static_cast<const CntVector*>(r);
|
||||
if (R->data_size_ != size / 8 || R->hash_ != hash) {
|
||||
return false;
|
||||
}
|
||||
return std::memcmp(R->data_, data, size / 8) == 0;
|
||||
}
|
||||
static bool compare(const RootObject* r, const std::vector<bool>& data, HashType hash) {
|
||||
CHECK(data.size() % 32 == 0);
|
||||
if (!r || r->get_size() < sizeof(CntVector)) {
|
||||
return false;
|
||||
}
|
||||
auto R = static_cast<const CntVector*>(r);
|
||||
if (R->data_size_ != data.size() / 8 || R->hash_ != hash) {
|
||||
return false;
|
||||
}
|
||||
for (td::uint32 i = 0; i < data.size(); i++) {
|
||||
if (get_bit(R->data_, i) != data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static const CntVector* lookup(ValidatorSessionDescription& desc, std::vector<bool>& value, HashType hash,
|
||||
bool temp) {
|
||||
CHECK(value.size() % 32 == 0);
|
||||
auto r = desc.get_by_hash(hash, temp);
|
||||
if (compare(r, value, hash)) {
|
||||
desc.on_reuse();
|
||||
return static_cast<const CntVector*>(r);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const CntVector* lookup(ValidatorSessionDescription& desc, td::uint32 size, const td::uint32* data,
|
||||
HashType hash, bool temp) {
|
||||
CHECK(size % 32 == 0);
|
||||
auto r = desc.get_by_hash(hash, temp);
|
||||
if (compare(r, size, data, hash)) {
|
||||
desc.on_reuse();
|
||||
return static_cast<const CntVector*>(r);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const CntVector* create(ValidatorSessionDescription& desc, std::vector<bool> value) {
|
||||
if (value.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (value.size() % 32) {
|
||||
auto new_size = value.size() - value.size() % 32 + 32;
|
||||
value.resize(new_size, false);
|
||||
}
|
||||
auto hash = create_hash(desc, value);
|
||||
auto r = lookup(desc, value, hash, true);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto size = static_cast<td::uint32>(value.size());
|
||||
auto data = static_cast<td::uint32*>(desc.alloc(sizeof(td::uint32) * size / 32, 8, true));
|
||||
for (td::uint32 i = 0; i < size; i++) {
|
||||
set_bit(data, i, value[i]);
|
||||
}
|
||||
|
||||
return new (desc, true) CntVector{desc, size, data, hash};
|
||||
}
|
||||
static const CntVector* create(ValidatorSessionDescription& desc, td::uint32 size, const td::uint32* value) {
|
||||
if (!size) {
|
||||
return nullptr;
|
||||
}
|
||||
CHECK(size % 32 == 0);
|
||||
auto hash = create_hash(desc, size, value);
|
||||
auto r = lookup(desc, size, value, hash, true);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return new (desc, true) CntVector{desc, size, value, hash};
|
||||
}
|
||||
static const CntVector* move_to_persistent(ValidatorSessionDescription& desc, const CntVector* b) {
|
||||
if (desc.is_persistent(b)) {
|
||||
return b;
|
||||
}
|
||||
auto r = lookup(desc, b->max_size(), b->data_, b->hash_, false);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
auto data = static_cast<td::uint32*>(desc.alloc(b->data_size_, 8, false));
|
||||
std::memcpy(data, b->data_, b->data_size_);
|
||||
|
||||
return new (desc, false) CntVector{desc, b->max_size(), data, b->hash_};
|
||||
}
|
||||
static const CntVector* merge(ValidatorSessionDescription& desc, const CntVector* l, const CntVector* r) {
|
||||
if (!l) {
|
||||
return r;
|
||||
}
|
||||
if (!r) {
|
||||
return l;
|
||||
}
|
||||
if (l == r) {
|
||||
return l;
|
||||
}
|
||||
CHECK(l->max_size() == r->max_size());
|
||||
auto sz = l->max_size() / 32;
|
||||
bool ret_left = true;
|
||||
bool ret_right = true;
|
||||
for (td::uint32 i = 0; i < sz; i++) {
|
||||
if (l->data_[i] & ~r->data_[i]) {
|
||||
ret_right = false;
|
||||
}
|
||||
if (r->data_[i] & ~l->data_[i]) {
|
||||
ret_left = false;
|
||||
}
|
||||
}
|
||||
if (ret_left) {
|
||||
return l;
|
||||
}
|
||||
if (ret_right) {
|
||||
return r;
|
||||
}
|
||||
auto v = static_cast<td::uint32*>(desc.alloc(sz * 4, 8, true));
|
||||
for (td::uint32 i = 0; i < sz; i++) {
|
||||
v[i] = l->data_[i] | r->data_[i];
|
||||
}
|
||||
|
||||
return create(desc, sz * 32, v);
|
||||
}
|
||||
static const CntVector* merge(ValidatorSessionDescription& desc, const CntVector* l, const CntVector* r,
|
||||
std::function<bool(bool, bool)> merge_f) {
|
||||
if (!l) {
|
||||
return r;
|
||||
}
|
||||
if (!r) {
|
||||
return l;
|
||||
}
|
||||
if (l == r) {
|
||||
return l;
|
||||
}
|
||||
auto sz = std::max(l->max_size(), r->max_size());
|
||||
|
||||
auto v = static_cast<td::uint32*>(desc.alloc(sz / 8, 8, true));
|
||||
std::memset(v, 0, sz / 8);
|
||||
for (td::uint32 i = 0; i < sz; i++) {
|
||||
if (i >= l->max_size()) {
|
||||
set_bit(v, i, r->at(i));
|
||||
} else if (i >= r->max_size()) {
|
||||
set_bit(v, i, l->at(i));
|
||||
} else {
|
||||
set_bit(v, i, merge_f(l->at(i), r->at(i)));
|
||||
}
|
||||
}
|
||||
|
||||
return create(desc, sz, v);
|
||||
}
|
||||
static const CntVector* change(ValidatorSessionDescription& desc, const CntVector* l, td::uint32 idx, bool value) {
|
||||
if (l->at(idx) == value) {
|
||||
return l;
|
||||
}
|
||||
auto sz = l->max_size();
|
||||
auto v = static_cast<td::uint32*>(desc.alloc(sz / 8, 8, true));
|
||||
std::memcpy(v, l->data_, l->data_size_);
|
||||
set_bit(v, idx, value);
|
||||
return create(desc, sz, v);
|
||||
}
|
||||
CntVector(ValidatorSessionDescription& desc, td::uint32 data_size, const td::uint32* data, HashType hash)
|
||||
: RootObject{sizeof(CntVector)}
|
||||
, data_size_(static_cast<td::uint32>(data_size / 8))
|
||||
, data_(data)
|
||||
, hash_(std::move(hash)) {
|
||||
desc.update_hash(this, hash_);
|
||||
CHECK(data_size % 32 == 0);
|
||||
}
|
||||
td::uint32 max_size() const {
|
||||
return data_size_ * 8;
|
||||
}
|
||||
auto data() const {
|
||||
return data_;
|
||||
}
|
||||
auto get_hash(ValidatorSessionDescription& desc) const {
|
||||
return hash_;
|
||||
}
|
||||
bool at(td::uint32 idx) const {
|
||||
CHECK(idx < max_size());
|
||||
return get_bit(data_, idx);
|
||||
}
|
||||
//const T& at(size_t idx) const;
|
||||
|
||||
private:
|
||||
const td::uint32 data_size_;
|
||||
const td::uint32* data_;
|
||||
const HashType hash_;
|
||||
};
|
||||
|
||||
template <typename T, typename Compare = std::less<T>>
|
||||
class CntSortedVector : public ValidatorSessionDescription::RootObject {
|
||||
public:
|
||||
static HashType create_hash(ValidatorSessionDescription& desc, std::vector<T>& value) {
|
||||
auto obj = create_tl_object<ton_api::hashable_cntSortedVector>(get_vs_hash(desc, value));
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
static HashType create_hash(ValidatorSessionDescription& desc, td::uint32 size, const T* value) {
|
||||
auto obj = create_tl_object<ton_api::hashable_cntSortedVector>(get_vs_hash(desc, size, value));
|
||||
return desc.compute_hash(serialize_tl_object(obj, true).as_slice());
|
||||
}
|
||||
static bool compare(const RootObject* r, td::uint32 size, const T* data, HashType hash) {
|
||||
if (!r || r->get_size() < sizeof(CntSortedVector)) {
|
||||
return false;
|
||||
}
|
||||
auto R = static_cast<const CntSortedVector*>(r);
|
||||
if (R->data_size_ != size * sizeof(T) || R->hash_ != hash) {
|
||||
return false;
|
||||
}
|
||||
for (td::uint32 i = 0; i < size; i++) {
|
||||
if (R->data_[i] != data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool compare(const RootObject* r, const std::vector<T>& data, HashType hash) {
|
||||
if (!r || r->get_size() < sizeof(CntSortedVector)) {
|
||||
return false;
|
||||
}
|
||||
auto R = static_cast<const CntSortedVector*>(r);
|
||||
if (R->data_size_ != data.size() * sizeof(T) || R->hash_ != hash) {
|
||||
return false;
|
||||
}
|
||||
for (td::uint32 i = 0; i < data.size(); i++) {
|
||||
if (R->data_[i] != data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static const CntSortedVector* lookup(ValidatorSessionDescription& desc, std::vector<T>& value, HashType hash,
|
||||
bool temp) {
|
||||
auto r = desc.get_by_hash(hash, temp);
|
||||
if (compare(r, value, hash)) {
|
||||
desc.on_reuse();
|
||||
return static_cast<const CntSortedVector*>(r);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const CntSortedVector* lookup(ValidatorSessionDescription& desc, td::uint32 size, const T* data, HashType hash,
|
||||
bool temp) {
|
||||
auto r = desc.get_by_hash(hash, temp);
|
||||
if (compare(r, size, data, hash)) {
|
||||
desc.on_reuse();
|
||||
return static_cast<const CntSortedVector*>(r);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const CntSortedVector* create(ValidatorSessionDescription& desc, std::vector<T> value) {
|
||||
if (value.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto hash = create_hash(desc, value);
|
||||
auto r = lookup(desc, value, hash, true);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto data_size = static_cast<td::uint32>(value.size());
|
||||
auto data = static_cast<T*>(desc.alloc(sizeof(T) * data_size, 8, true));
|
||||
for (td::uint32 i = 0; i < data_size; i++) {
|
||||
data[i] = value[i];
|
||||
}
|
||||
|
||||
return new (desc, true) CntSortedVector{desc, data_size, data, hash};
|
||||
}
|
||||
static const CntSortedVector* create(ValidatorSessionDescription& desc, td::uint32 size, const T* value) {
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto hash = create_hash(desc, size, value);
|
||||
auto r = lookup(desc, size, value, hash, true);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return new (desc, true) CntSortedVector{desc, size, value, hash};
|
||||
}
|
||||
static const CntSortedVector* move_to_persistent(ValidatorSessionDescription& desc, const CntSortedVector* b) {
|
||||
if (desc.is_persistent(b)) {
|
||||
return b;
|
||||
}
|
||||
std::vector<T> v;
|
||||
v.resize(b->size());
|
||||
for (td::uint32 i = 0; i < v.size(); i++) {
|
||||
v[i] = ton::validatorsession::move_to_persistent(desc, b->data_[i]);
|
||||
}
|
||||
auto r = lookup(desc, v, b->hash_, false);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
auto data = static_cast<T*>(desc.alloc(sizeof(T) * v.size(), 8, false));
|
||||
for (td::uint32 i = 0; i < v.size(); i++) {
|
||||
data[i] = v[i];
|
||||
}
|
||||
|
||||
return new (desc, false) CntSortedVector{desc, b->size(), data, b->hash_};
|
||||
}
|
||||
static const CntSortedVector* merge(ValidatorSessionDescription& desc, const CntSortedVector* l,
|
||||
const CntSortedVector* r, std::function<T(T, T)> merge_f) {
|
||||
if (!l) {
|
||||
return r;
|
||||
}
|
||||
if (!r) {
|
||||
return l;
|
||||
}
|
||||
if (l == r) {
|
||||
return l;
|
||||
}
|
||||
|
||||
bool ret_left = true;
|
||||
bool ret_right = true;
|
||||
|
||||
const T* li = l->data_;
|
||||
const T* ri = r->data_;
|
||||
td::uint32 lp = 0;
|
||||
td::uint32 rp = 0;
|
||||
while (lp < l->size() || rp < r->size()) {
|
||||
if (lp == l->size()) {
|
||||
ret_left = false;
|
||||
break;
|
||||
} else if (rp == r->size()) {
|
||||
ret_right = false;
|
||||
break;
|
||||
} else {
|
||||
if (Compare()(li[lp], ri[rp])) {
|
||||
ret_right = false;
|
||||
lp++;
|
||||
} else if (Compare()(ri[rp], li[lp])) {
|
||||
ret_left = false;
|
||||
rp++;
|
||||
} else {
|
||||
if (li[lp++] != ri[rp++]) {
|
||||
ret_left = false;
|
||||
ret_right = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret_left) {
|
||||
return l;
|
||||
}
|
||||
if (ret_right) {
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<T> v;
|
||||
|
||||
lp = 0;
|
||||
rp = 0;
|
||||
while (lp < l->size() || rp < r->size()) {
|
||||
if (lp == l->size()) {
|
||||
v.push_back(ri[rp++]);
|
||||
} else if (rp == r->size()) {
|
||||
v.push_back(li[lp++]);
|
||||
} else {
|
||||
if (Compare()(li[lp], ri[rp])) {
|
||||
v.push_back(li[lp++]);
|
||||
} else if (Compare()(ri[rp], li[lp])) {
|
||||
v.push_back(ri[rp++]);
|
||||
} else {
|
||||
v.push_back(merge_f(li[lp++], ri[rp++]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CntSortedVector::create(desc, std::move(v));
|
||||
}
|
||||
/*static const CntSortedVector* merge(ValidatorSessionDescription& desc, const CntSortedVector* l,
|
||||
const CntSortedVector* r) {
|
||||
return merge(desc, l, r, [](T l, T r) { return l; });
|
||||
}*/
|
||||
static const CntSortedVector* push(ValidatorSessionDescription& desc, const CntSortedVector* v, T value) {
|
||||
if (!v) {
|
||||
return create(desc, std::vector<T>{value});
|
||||
}
|
||||
T* res = nullptr;
|
||||
td::uint32 res_size = 0;
|
||||
|
||||
td::int32 l = -1;
|
||||
td::int32 r = v->size();
|
||||
bool found = false;
|
||||
while (r - l > 1) {
|
||||
auto x = (r + l) / 2;
|
||||
if (Compare()(v->at(x), value)) {
|
||||
l = x;
|
||||
} else if (Compare()(value, v->at(x))) {
|
||||
r = x;
|
||||
} else {
|
||||
if (v->at(x) == value) {
|
||||
return v;
|
||||
}
|
||||
res = static_cast<T*>(desc.alloc(sizeof(T) * v->size(), 8, true));
|
||||
std::memcpy(res, v->data(), sizeof(T) * v->size());
|
||||
res[x] = value;
|
||||
res_size = v->size();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
res = static_cast<T*>(desc.alloc(sizeof(T) * (v->size() + 1), 8, true));
|
||||
res_size = v->size() + 1;
|
||||
std::memcpy(res, v->data(), sizeof(T) * r);
|
||||
res[r] = value;
|
||||
std::memcpy(res + r + 1, v->data() + r, sizeof(T) * (v->size() - r));
|
||||
}
|
||||
return CntSortedVector::create(desc, res_size, res);
|
||||
}
|
||||
CntSortedVector(ValidatorSessionDescription& desc, td::uint32 data_size, const T* data, HashType hash)
|
||||
: RootObject{sizeof(CntSortedVector)}
|
||||
, data_size_(static_cast<td::uint32>(data_size * sizeof(T)))
|
||||
, data_(data)
|
||||
, hash_(std::move(hash)) {
|
||||
desc.update_hash(this, hash_);
|
||||
}
|
||||
td::uint32 size() const {
|
||||
return static_cast<td::int32>(data_size_ / sizeof(T));
|
||||
}
|
||||
auto data() const {
|
||||
return data_;
|
||||
}
|
||||
auto get_hash(ValidatorSessionDescription& desc) const {
|
||||
return hash_;
|
||||
}
|
||||
T at(td::uint32 idx) const {
|
||||
CHECK(idx < size());
|
||||
return data_[idx];
|
||||
}
|
||||
//const T& at(size_t idx) const;
|
||||
|
||||
private:
|
||||
const td::uint32 data_size_;
|
||||
const T* data_;
|
||||
const HashType hash_;
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
203
validator-session/validator-session-description.cpp
Normal file
203
validator-session/validator-session-description.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "validator-session.hpp"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "validator-session-description.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
ValidatorSessionDescriptionImpl::Source::Source(ValidatorSessionNode &node) {
|
||||
encryptor = node.pub_key.create_encryptor().move_as_ok();
|
||||
weight = node.weight;
|
||||
id = node.pub_key.compute_short_id();
|
||||
full_id = node.pub_key;
|
||||
adnl_id = node.adnl_id;
|
||||
}
|
||||
|
||||
ValidatorSessionDescriptionImpl::ValidatorSessionDescriptionImpl(ValidatorSessionOptions opts,
|
||||
std::vector<ValidatorSessionNode> &nodes,
|
||||
PublicKeyHash local_id)
|
||||
: opts_(std::move(opts)) {
|
||||
td::uint32 size = static_cast<td::uint32>(nodes.size());
|
||||
ValidatorWeight total_weight = 0;
|
||||
for (td::uint32 i = 0; i < size; i++) {
|
||||
sources_.emplace_back(nodes[i]);
|
||||
total_weight += sources_[i].weight;
|
||||
CHECK(rev_sources_.find(sources_[i].id) == rev_sources_.end());
|
||||
rev_sources_[sources_[i].id] = i;
|
||||
}
|
||||
total_weight_ = total_weight;
|
||||
cutoff_weight_ = (total_weight * 2) / 3 + 1;
|
||||
auto it = rev_sources_.find(local_id);
|
||||
CHECK(it != rev_sources_.end());
|
||||
self_idx_ = it->second;
|
||||
|
||||
pdata_temp_ptr_ = 0;
|
||||
pdata_temp_size_ = 1 << 30;
|
||||
pdata_temp_ = new td::uint8[pdata_temp_size_];
|
||||
|
||||
pdata_perm_size_ = 1 << 30;
|
||||
pdata_perm_ptr_ = 0;
|
||||
|
||||
for (auto &el : cache_) {
|
||||
Cached v{nullptr};
|
||||
el.store(v, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
td::int32 ValidatorSessionDescriptionImpl::get_node_priority(td::uint32 src_idx, td::uint32 round) const {
|
||||
round %= get_total_nodes();
|
||||
if (src_idx < round) {
|
||||
src_idx += get_total_nodes();
|
||||
}
|
||||
if (src_idx - round < opts_.round_candidates) {
|
||||
return src_idx - round;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
td::uint32 ValidatorSessionDescriptionImpl::get_max_priority() const {
|
||||
return opts_.round_candidates - 1;
|
||||
}
|
||||
|
||||
ValidatorSessionCandidateId ValidatorSessionDescriptionImpl::candidate_id(
|
||||
td::uint32 src_idx, ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash,
|
||||
ValidatorSessionCollatedDataFileHash collated_data_file_hash) const {
|
||||
auto obj = create_tl_object<ton_api::validatorSession_candidateId>(get_source_id(src_idx).tl(), root_hash, file_hash,
|
||||
collated_data_file_hash);
|
||||
return get_tl_object_sha_bits256(obj);
|
||||
}
|
||||
|
||||
td::Status ValidatorSessionDescriptionImpl::check_signature(ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash, td::uint32 src_idx,
|
||||
td::Slice signature) const {
|
||||
auto obj = create_tl_object<ton_api::ton_blockId>(root_hash, file_hash);
|
||||
auto S = serialize_tl_object(obj, true);
|
||||
|
||||
return sources_[src_idx].encryptor->check_signature(S.as_slice(), signature);
|
||||
}
|
||||
|
||||
td::Status ValidatorSessionDescriptionImpl::check_approve_signature(ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash,
|
||||
td::uint32 src_idx, td::Slice signature) const {
|
||||
auto obj = create_tl_object<ton_api::ton_blockIdApprove>(root_hash, file_hash);
|
||||
auto S = serialize_tl_object(obj, true);
|
||||
|
||||
return sources_[src_idx].encryptor->check_signature(S.as_slice(), signature);
|
||||
}
|
||||
|
||||
std::vector<PublicKeyHash> ValidatorSessionDescriptionImpl::export_nodes() const {
|
||||
std::vector<PublicKeyHash> v;
|
||||
v.resize(get_total_nodes());
|
||||
for (td::uint32 i = 0; i < get_total_nodes(); i++) {
|
||||
v[i] = sources_[i].id;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<catchain::CatChainNode> ValidatorSessionDescriptionImpl::export_catchain_nodes() const {
|
||||
std::vector<catchain::CatChainNode> v;
|
||||
v.resize(get_total_nodes());
|
||||
for (td::uint32 i = 0; i < get_total_nodes(); i++) {
|
||||
v[i].pub_key = sources_[i].full_id;
|
||||
v[i].adnl_id = sources_[i].adnl_id;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<PublicKey> ValidatorSessionDescriptionImpl::export_full_nodes() const {
|
||||
std::vector<PublicKey> v;
|
||||
v.resize(get_total_nodes());
|
||||
for (td::uint32 i = 0; i < get_total_nodes(); i++) {
|
||||
v[i] = sources_[i].full_id;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
double ValidatorSessionDescriptionImpl::get_delay(td::uint32 priority) const {
|
||||
return priority * opts_.next_candidate_delay;
|
||||
}
|
||||
|
||||
td::uint32 ValidatorSessionDescriptionImpl::get_vote_for_author(td::uint32 attempt_seqno) const {
|
||||
return attempt_seqno % get_total_nodes();
|
||||
}
|
||||
|
||||
const ValidatorSessionDescription::RootObject *ValidatorSessionDescriptionImpl::get_by_hash(HashType hash,
|
||||
bool allow_temp) const {
|
||||
auto x = hash % cache_size;
|
||||
|
||||
return cache_[x].load(std::memory_order_relaxed).ptr;
|
||||
}
|
||||
|
||||
HashType ValidatorSessionDescriptionImpl::compute_hash(td::Slice data) const {
|
||||
return td::crc32c(data);
|
||||
}
|
||||
|
||||
void ValidatorSessionDescriptionImpl::update_hash(const RootObject *obj, HashType hash) {
|
||||
if (!is_persistent(obj)) {
|
||||
return;
|
||||
}
|
||||
auto x = hash % cache_size;
|
||||
Cached p{obj};
|
||||
cache_[x].store(p, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void *ValidatorSessionDescriptionImpl::alloc(size_t size, size_t align, bool temp) {
|
||||
if (temp) {
|
||||
auto s = pdata_temp_ptr_;
|
||||
pdata_temp_ptr_ += size;
|
||||
CHECK(s + size <= pdata_temp_size_);
|
||||
return static_cast<void *>(pdata_temp_ + s);
|
||||
} else {
|
||||
while (true) {
|
||||
auto s = pdata_perm_ptr_;
|
||||
pdata_perm_ptr_ += size;
|
||||
|
||||
if (pdata_perm_ptr_ <= pdata_perm_.size() * pdata_perm_size_) {
|
||||
return static_cast<void *>(pdata_perm_[s / pdata_perm_size_] + (s % pdata_perm_size_));
|
||||
}
|
||||
|
||||
pdata_perm_.push_back(new td::uint8[pdata_perm_size_]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidatorSessionDescriptionImpl::is_persistent(const void *ptr) const {
|
||||
if (ptr == nullptr) {
|
||||
return true;
|
||||
}
|
||||
for (auto &v : pdata_perm_) {
|
||||
if (ptr >= v && ptr <= v + pdata_perm_size_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<ValidatorSessionDescription> ValidatorSessionDescription::create(
|
||||
ValidatorSessionOptions opts, std::vector<ValidatorSessionNode> &nodes, PublicKeyHash local_id) {
|
||||
return std::make_unique<ValidatorSessionDescriptionImpl>(std::move(opts), nodes, local_id);
|
||||
}
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
118
validator-session/validator-session-description.h
Normal file
118
validator-session/validator-session-description.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "crypto/common/refcnt.hpp"
|
||||
#include "crypto/common/refint.h"
|
||||
|
||||
#include "td/utils/int_types.h"
|
||||
|
||||
#include "adnl/utils.hpp"
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
#include "validator-session-types.h"
|
||||
#include "catchain/catchain.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
class ValidatorSessionDescription {
|
||||
public:
|
||||
using HashType = td::uint32;
|
||||
struct RootObject {
|
||||
public:
|
||||
void *operator new(size_t size, ValidatorSessionDescription &desc, bool temp) {
|
||||
return desc.alloc(size, 8, temp);
|
||||
}
|
||||
void *operator new[](size_t size, ValidatorSessionDescription &desc, bool temp) {
|
||||
return desc.alloc(size, 8, temp);
|
||||
}
|
||||
void operator delete(void *ptr, ValidatorSessionDescription &desc, bool temp) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void operator delete[](void *ptr, ValidatorSessionDescription &desc, bool temp) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
RootObject(td::uint32 size) : size_(size) {
|
||||
}
|
||||
td::uint32 get_size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
private:
|
||||
const td::uint32 size_;
|
||||
};
|
||||
|
||||
virtual HashType compute_hash(td::Slice data) const = 0;
|
||||
HashType zero_hash() const {
|
||||
return 0;
|
||||
}
|
||||
virtual void *alloc(size_t size, size_t align, bool temp) = 0;
|
||||
virtual bool is_persistent(const void *ptr) const = 0;
|
||||
template <typename T>
|
||||
inline bool is_persistent(const T *ptr) const {
|
||||
return is_persistent(static_cast<const void *>(ptr));
|
||||
}
|
||||
virtual void clear_temp_memory() = 0;
|
||||
|
||||
virtual ~ValidatorSessionDescription() = default;
|
||||
|
||||
virtual PublicKeyHash get_source_id(td::uint32 idx) const = 0;
|
||||
virtual PublicKey get_source_public_key(td::uint32 idx) const = 0;
|
||||
virtual adnl::AdnlNodeIdShort get_source_adnl_id(td::uint32 idx) const = 0;
|
||||
virtual td::uint32 get_source_idx(PublicKeyHash id) const = 0;
|
||||
virtual ValidatorWeight get_node_weight(td::uint32 idx) const = 0;
|
||||
virtual td::uint32 get_total_nodes() const = 0;
|
||||
virtual ValidatorWeight get_cutoff_weight() const = 0;
|
||||
virtual ValidatorWeight get_total_weight() const = 0;
|
||||
virtual td::int32 get_node_priority(td::uint32 src_idx, td::uint32 round) const = 0;
|
||||
virtual td::uint32 get_max_priority() const = 0;
|
||||
virtual td::uint32 get_unixtime(td::uint64 t) const = 0;
|
||||
virtual td::uint32 get_attempt_seqno(td::uint64 t) const = 0;
|
||||
virtual td::uint32 get_self_idx() const = 0;
|
||||
virtual td::uint64 get_ts() const = 0;
|
||||
virtual const RootObject *get_by_hash(HashType hash, bool allow_temp) const = 0;
|
||||
virtual void update_hash(const RootObject *obj, HashType hash) = 0;
|
||||
virtual void on_reuse() = 0;
|
||||
virtual td::Timestamp attempt_start_at(td::uint32 att) const = 0;
|
||||
virtual ValidatorSessionCandidateId candidate_id(
|
||||
td::uint32 src_idx, ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash,
|
||||
ValidatorSessionCollatedDataFileHash collated_data_file_hash) const = 0;
|
||||
virtual td::Status check_signature(ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash,
|
||||
td::uint32 src_idx, td::Slice signature) const = 0;
|
||||
virtual td::Status check_approve_signature(ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash,
|
||||
td::uint32 src_idx, td::Slice signature) const = 0;
|
||||
virtual double get_delay(td::uint32 priority) const = 0;
|
||||
virtual double get_empty_block_delay() const = 0;
|
||||
virtual std::vector<catchain::CatChainNode> export_catchain_nodes() const = 0;
|
||||
|
||||
virtual td::uint32 get_vote_for_author(td::uint32 attempt_seqno) const = 0;
|
||||
|
||||
virtual const ValidatorSessionOptions &opts() const = 0;
|
||||
|
||||
static std::unique_ptr<ValidatorSessionDescription> create(ValidatorSessionOptions opts,
|
||||
std::vector<ValidatorSessionNode> &nodes,
|
||||
PublicKeyHash local_id);
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
165
validator-session/validator-session-description.hpp
Normal file
165
validator-session/validator-session-description.hpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "validator-session.h"
|
||||
#include "validator-session-state.h"
|
||||
|
||||
#include "keys/encryptor.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
class ValidatorSessionDescriptionImpl : public ValidatorSessionDescription {
|
||||
private:
|
||||
ValidatorSessionOptions opts_;
|
||||
|
||||
struct Source {
|
||||
PublicKeyHash id;
|
||||
ValidatorWeight weight;
|
||||
std::unique_ptr<Encryptor> encryptor;
|
||||
PublicKey full_id;
|
||||
adnl::AdnlNodeIdShort adnl_id;
|
||||
Source(ValidatorSessionNode &node);
|
||||
};
|
||||
std::map<PublicKeyHash, td::uint32> rev_sources_;
|
||||
|
||||
std::vector<Source> sources_;
|
||||
ValidatorWeight cutoff_weight_;
|
||||
ValidatorWeight total_weight_;
|
||||
td::uint32 self_idx_;
|
||||
|
||||
static constexpr td::uint32 cache_size = (1 << 20);
|
||||
|
||||
struct Cached {
|
||||
const RootObject *ptr;
|
||||
};
|
||||
std::array<std::atomic<Cached>, cache_size> cache_;
|
||||
//std::array<std::atomic<Cached>, cache_size> temp_cache_;
|
||||
|
||||
td::uint8 *pdata_temp_;
|
||||
size_t pdata_temp_ptr_;
|
||||
size_t pdata_temp_size_;
|
||||
|
||||
size_t pdata_perm_size_;
|
||||
std::vector<td::uint8 *> pdata_perm_;
|
||||
size_t pdata_perm_ptr_;
|
||||
std::atomic<td::uint64> reuse_{0};
|
||||
|
||||
public:
|
||||
ValidatorSessionDescriptionImpl(ValidatorSessionOptions opts, std::vector<ValidatorSessionNode> &nodes,
|
||||
PublicKeyHash local_id);
|
||||
PublicKeyHash get_source_id(td::uint32 idx) const override {
|
||||
CHECK(idx < sources_.size());
|
||||
return sources_[idx].id;
|
||||
}
|
||||
virtual PublicKey get_source_public_key(td::uint32 idx) const override {
|
||||
CHECK(idx < sources_.size());
|
||||
return sources_[idx].full_id;
|
||||
}
|
||||
adnl::AdnlNodeIdShort get_source_adnl_id(td::uint32 idx) const override {
|
||||
CHECK(idx < sources_.size());
|
||||
return sources_[idx].adnl_id;
|
||||
}
|
||||
td::uint32 get_source_idx(PublicKeyHash id) const override {
|
||||
auto it = rev_sources_.find(id);
|
||||
CHECK(it != rev_sources_.end());
|
||||
return it->second;
|
||||
}
|
||||
ValidatorWeight get_node_weight(td::uint32 idx) const override {
|
||||
CHECK(idx < sources_.size());
|
||||
return sources_[idx].weight;
|
||||
}
|
||||
td::uint32 get_total_nodes() const override {
|
||||
return static_cast<td::uint32>(sources_.size());
|
||||
}
|
||||
ValidatorWeight get_cutoff_weight() const override {
|
||||
return cutoff_weight_;
|
||||
}
|
||||
ValidatorWeight get_total_weight() const override {
|
||||
return total_weight_;
|
||||
}
|
||||
td::int32 get_node_priority(td::uint32 src_idx, td::uint32 round) const override;
|
||||
td::uint32 get_max_priority() const override;
|
||||
td::uint32 get_unixtime(td::uint64 ts) const override {
|
||||
return static_cast<td::uint32>(ts >> 32);
|
||||
}
|
||||
td::uint32 get_attempt_seqno(td::uint64 ts) const override {
|
||||
return get_unixtime(ts) / opts_.round_attempt_duration;
|
||||
}
|
||||
const RootObject *get_by_hash(HashType hash, bool allow_temp) const override;
|
||||
void on_reuse() override {
|
||||
if (reuse_++ % (1 << 17) == 0) {
|
||||
LOG(INFO) << "reused " << reuse_ << " times";
|
||||
}
|
||||
}
|
||||
void update_hash(const RootObject *obj, HashType hash) override;
|
||||
void *alloc(size_t size, size_t align, bool temp) override;
|
||||
void clear_temp_memory() override {
|
||||
pdata_temp_ptr_ = 0;
|
||||
}
|
||||
bool is_persistent(const void *ptr) const override;
|
||||
HashType compute_hash(td::Slice data) const override;
|
||||
td::Timestamp attempt_start_at(td::uint32 att) const override {
|
||||
return td::Timestamp::at_unix(att * opts_.round_attempt_duration);
|
||||
}
|
||||
td::uint32 get_self_idx() const override {
|
||||
return self_idx_;
|
||||
}
|
||||
td::uint64 get_ts() const override {
|
||||
auto tm = td::Clocks::system();
|
||||
CHECK(tm >= 0);
|
||||
auto t = static_cast<td::uint32>(tm);
|
||||
auto t2 = static_cast<td::uint64>((1ll << 32) * (tm - t));
|
||||
CHECK(t2 < (1ull << 32));
|
||||
return ((t * 1ull) << 32) + t2;
|
||||
}
|
||||
ValidatorSessionCandidateId candidate_id(td::uint32 src_idx, ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash,
|
||||
ValidatorSessionCollatedDataFileHash collated_data_file_hash) const override;
|
||||
td::Status check_signature(ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash, td::uint32 src_idx,
|
||||
td::Slice signature) const override;
|
||||
td::Status check_approve_signature(ValidatorSessionRootHash root_hash, ValidatorSessionFileHash file_hash,
|
||||
td::uint32 src_idx, td::Slice signature) const override;
|
||||
double get_delay(td::uint32 priority) const override;
|
||||
double get_empty_block_delay() const override {
|
||||
return get_delay(get_max_priority() + 1);
|
||||
}
|
||||
td::uint32 get_vote_for_author(td::uint32 attempt_seqno) const override;
|
||||
std::vector<PublicKeyHash> export_nodes() const;
|
||||
std::vector<catchain::CatChainNode> export_catchain_nodes() const override;
|
||||
std::vector<PublicKey> export_full_nodes() const;
|
||||
const ValidatorSessionOptions &opts() const override {
|
||||
return opts_;
|
||||
}
|
||||
~ValidatorSessionDescriptionImpl() {
|
||||
delete[] pdata_temp_;
|
||||
for (auto &x : pdata_perm_) {
|
||||
delete[] x;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
1611
validator-session/validator-session-state.cpp
Normal file
1611
validator-session/validator-session-state.cpp
Normal file
File diff suppressed because it is too large
Load diff
1132
validator-session/validator-session-state.h
Normal file
1132
validator-session/validator-session-state.h
Normal file
File diff suppressed because it is too large
Load diff
65
validator-session/validator-session-types.h
Normal file
65
validator-session/validator-session-types.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/int_types.h"
|
||||
#include "crypto/common/bitstring.h"
|
||||
#include "adnl/adnl-node-id.hpp"
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
using ValidatorSessionRootHash = td::Bits256;
|
||||
using ValidatorSessionFileHash = td::Bits256;
|
||||
using ValidatorSessionCollatedDataFileHash = td::Bits256;
|
||||
using ValidatorSessionCandidateId = td::Bits256;
|
||||
|
||||
inline ValidatorSessionCandidateId skip_round_candidate_id() {
|
||||
return ValidatorSessionCandidateId::zero();
|
||||
}
|
||||
|
||||
struct ValidatorSessionOptions {
|
||||
ValidatorSessionOptions() {
|
||||
}
|
||||
ValidatorSessionOptions(const ValidatorSessionConfig &conf);
|
||||
td::Clocks::Duration catchain_idle_timeout = 16.0;
|
||||
td::uint32 catchain_max_deps = 4;
|
||||
|
||||
td::uint32 round_candidates = 3;
|
||||
td::Clocks::Duration next_candidate_delay = 2.0;
|
||||
td::uint32 round_attempt_duration = 16;
|
||||
td::uint32 max_round_attempts = 4;
|
||||
|
||||
td::uint32 max_block_size = 4 << 20;
|
||||
td::uint32 max_collated_data_size = 4 << 20;
|
||||
|
||||
td::Bits256 get_hash() const;
|
||||
};
|
||||
|
||||
struct ValidatorSessionNode {
|
||||
PublicKey pub_key;
|
||||
adnl::AdnlNodeIdShort adnl_id;
|
||||
ValidatorWeight weight;
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
893
validator-session/validator-session.cpp
Normal file
893
validator-session/validator-session.cpp
Normal file
|
@ -0,0 +1,893 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "validator-session.hpp"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/crypto.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
void ValidatorSessionImpl::process_blocks(std::vector<catchain::CatChainBlock *> blocks) {
|
||||
VLOG(VALIDATOR_SESSION_DEBUG) << this << ": processing " << blocks.size() << " blocks";
|
||||
requested_new_block_ = false;
|
||||
requested_new_block_now_ = false;
|
||||
|
||||
for (auto block : blocks) {
|
||||
auto e = dynamic_cast<const BlockExtra *>(block->extra());
|
||||
CHECK(e != nullptr);
|
||||
real_state_ = ValidatorSessionState::merge(description(), real_state_, e->get_ref());
|
||||
}
|
||||
|
||||
if (real_state_->cur_round_seqno() != cur_round_) {
|
||||
on_new_round(real_state_->cur_round_seqno());
|
||||
}
|
||||
|
||||
td::uint32 cnt = 0;
|
||||
auto ts = description().get_ts();
|
||||
auto att = description().get_attempt_seqno(ts);
|
||||
std::vector<tl_object_ptr<ton_api::validatorSession_round_Message>> msgs;
|
||||
|
||||
if (generated_ && !sent_generated_) {
|
||||
auto it = blocks_[0].find(generated_block_);
|
||||
CHECK(it != blocks_[0].end());
|
||||
|
||||
auto &B = it->second;
|
||||
auto file_hash = sha256_bits256(B->data_);
|
||||
auto collated_data_file_hash = sha256_bits256(B->collated_data_);
|
||||
msgs.emplace_back(create_tl_object<ton_api::validatorSession_message_submittedBlock>(
|
||||
cur_round_, B->root_hash_, file_hash, collated_data_file_hash));
|
||||
cnt++;
|
||||
sent_generated_ = true;
|
||||
}
|
||||
|
||||
auto to_approve = real_state_->choose_blocks_to_approve(description(), local_idx());
|
||||
for (auto &block : to_approve) {
|
||||
auto id = SentBlock::get_block_id(block);
|
||||
if (approved_.count(id) && approved_[id].first <= td::Clocks::system()) {
|
||||
msgs.emplace_back(create_tl_object<ton_api::validatorSession_message_approvedBlock>(
|
||||
cur_round_, id, approved_[id].second.clone()));
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
for (auto &B : pending_reject_) {
|
||||
msgs.emplace_back(
|
||||
create_tl_object<ton_api::validatorSession_message_rejectedBlock>(cur_round_, B.first, std::move(B.second)));
|
||||
}
|
||||
pending_reject_.clear();
|
||||
|
||||
if (signed_) {
|
||||
bool found = false;
|
||||
auto B = real_state_->choose_block_to_sign(description(), local_idx(), found);
|
||||
if (found) {
|
||||
CHECK(SentBlock::get_block_id(B) == signed_block_);
|
||||
msgs.emplace_back(
|
||||
create_tl_object<ton_api::validatorSession_message_commit>(cur_round_, signed_block_, std::move(signature_)));
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &msg : msgs) {
|
||||
VLOG(VALIDATOR_SESSION_INFO) << this << ": applying action: " << msg.get();
|
||||
real_state_ = ValidatorSessionState::action(description(), real_state_, local_idx(), att, msg.get());
|
||||
}
|
||||
|
||||
if (real_state_->check_need_generate_vote_for(description(), local_idx(), att)) {
|
||||
VLOG(VALIDATOR_SESSION_INFO) << this << ": generating VOTEFOR";
|
||||
auto msg = real_state_->generate_vote_for(description(), local_idx(), att);
|
||||
CHECK(msg);
|
||||
real_state_ = ValidatorSessionState::action(description(), real_state_, local_idx(), att, msg.get());
|
||||
msgs.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
auto msg = real_state_->create_action(description(), local_idx(), att);
|
||||
bool stop = false;
|
||||
if (msg->get_id() == ton_api::validatorSession_message_empty::ID) {
|
||||
stop = true;
|
||||
}
|
||||
VLOG(VALIDATOR_SESSION_INFO) << this << ": applying action: " << msg.get();
|
||||
real_state_ = ValidatorSessionState::action(description(), real_state_, local_idx(), att, msg.get());
|
||||
msgs.emplace_back(std::move(msg));
|
||||
cnt++;
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
real_state_ = ValidatorSessionState::move_to_persistent(description(), real_state_);
|
||||
|
||||
VLOG(VALIDATOR_SESSION_DEBUG) << this << ": created block: root_hash=" << real_state_->get_hash(description());
|
||||
|
||||
auto payload = create_tl_object<ton_api::validatorSession_blockUpdate>(ts, std::move(msgs),
|
||||
real_state_->get_hash(description()));
|
||||
td::actor::send_closure(catchain_, &catchain::CatChain::processed_block, serialize_tl_object(payload, true));
|
||||
|
||||
auto round = real_state_->cur_round_seqno();
|
||||
if (round > cur_round_) {
|
||||
on_new_round(round);
|
||||
}
|
||||
|
||||
virtual_state_ = ValidatorSessionState::merge(description(), virtual_state_, real_state_);
|
||||
virtual_state_ = ValidatorSessionState::move_to_persistent(description(), virtual_state_);
|
||||
description().clear_temp_memory();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::finished_processing() {
|
||||
if (virtual_state_->get_hash(description()) != real_state_->get_hash(description())) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << ": hash mismatch (maybe some node blamed)";
|
||||
}
|
||||
virtual_state_ = real_state_;
|
||||
check_all();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::preprocess_block(catchain::CatChainBlock *block) {
|
||||
auto start_time = td::Timestamp::now();
|
||||
td::PerfWarningTimer p_timer{"Loong block preprocess", 0.1};
|
||||
td::PerfWarningTimer q_timer{"Looong block preprocess", 0.1};
|
||||
|
||||
auto prev = block->prev();
|
||||
const ValidatorSessionState *state;
|
||||
if (prev) {
|
||||
auto e = dynamic_cast<const BlockExtra *>(prev->extra());
|
||||
CHECK(e != nullptr);
|
||||
state = e->get_ref();
|
||||
} else {
|
||||
state = ValidatorSessionState::create(description());
|
||||
}
|
||||
auto deps = block->deps();
|
||||
for (auto b : deps) {
|
||||
auto e = dynamic_cast<const BlockExtra *>(b->extra());
|
||||
CHECK(e != nullptr);
|
||||
state = ValidatorSessionState::merge(description(), state, e->get_ref());
|
||||
}
|
||||
|
||||
if (block->payload().size() != 0 || deps.size() != 0) {
|
||||
auto R = fetch_tl_object<ton_api::validatorSession_blockUpdate>(block->payload().clone(), true);
|
||||
if (!R.is_error()) {
|
||||
auto B = R.move_as_ok();
|
||||
auto att = description().get_attempt_seqno(B->ts_);
|
||||
for (auto &msg : B->actions_) {
|
||||
VLOG(VALIDATOR_SESSION_INFO) << this << "[node " << description().get_source_id(block->source()) << "][block "
|
||||
<< block->hash() << "]: applying action " << msg.get();
|
||||
state = ValidatorSessionState::action(description(), state, block->source(), att, msg.get());
|
||||
}
|
||||
state = ValidatorSessionState::make_all(description(), state, block->source(), att);
|
||||
if (state->get_hash(description()) != static_cast<td::uint32>(B->state_)) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << description().get_source_id(block->source())
|
||||
<< "][block " << block->hash()
|
||||
<< "]: state hash mismatch: computed=" << state->get_hash(description())
|
||||
<< " received=" << B->state_;
|
||||
for (auto &msg : B->actions_) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << description().get_source_id(block->source())
|
||||
<< "][block " << block->hash() << "]: applied action " << msg.get();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << description().get_source_id(block->source()) << "][block "
|
||||
<< block->hash() << "]: failed to parse: " << R.move_as_error();
|
||||
state = ValidatorSessionState::make_all(description(), state, block->source(), state->get_ts(block->source()));
|
||||
}
|
||||
}
|
||||
q_timer.reset();
|
||||
state = ValidatorSessionState::move_to_persistent(description(), state);
|
||||
block->set_extra(std::make_unique<BlockExtra>(state));
|
||||
if (block->source() == local_idx() && !catchain_started_) {
|
||||
real_state_ = state;
|
||||
}
|
||||
virtual_state_ = ValidatorSessionState::merge(description(), virtual_state_, state);
|
||||
virtual_state_ = ValidatorSessionState::move_to_persistent(description(), virtual_state_);
|
||||
description().clear_temp_memory();
|
||||
if (real_state_->cur_round_seqno() != cur_round_) {
|
||||
on_new_round(real_state_->cur_round_seqno());
|
||||
}
|
||||
check_all();
|
||||
VLOG(VALIDATOR_SESSION_DEBUG) << this << ": preprocessed block " << block->hash() << " in "
|
||||
<< static_cast<td::uint32>(1000 * (td::Timestamp::now().at() - start_time.at()))
|
||||
<< "ms: state=" << state->get_hash(description());
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice data) {
|
||||
auto src_idx = description().get_source_idx(src);
|
||||
auto R = fetch_tl_object<ton_api::validatorSession_candidate>(data.clone(), true);
|
||||
if (R.is_error()) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << src << "][broadcast " << sha256_bits256(data.as_slice())
|
||||
<< "]: failed to parse: " << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto candidate = R.move_as_ok();
|
||||
if (PublicKeyHash{candidate->src_} != src) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << src << "][broadcast " << sha256_bits256(data.as_slice())
|
||||
<< "]: source mismatch";
|
||||
return;
|
||||
}
|
||||
if (candidate->data_.size() > description().opts().max_block_size ||
|
||||
candidate->collated_data_.size() > description().opts().max_collated_data_size) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << src << "][broadcast " << sha256_bits256(data.as_slice())
|
||||
<< "]: too big broadcast size=" << candidate->data_.size() << " "
|
||||
<< candidate->collated_data_.size();
|
||||
return;
|
||||
}
|
||||
|
||||
auto file_hash = sha256_bits256(candidate->data_.as_slice());
|
||||
auto collated_data_file_hash = sha256_bits256(candidate->collated_data_.as_slice());
|
||||
auto block_round = static_cast<td::uint32>(candidate->round_);
|
||||
auto block_id = description().candidate_id(src_idx, candidate->root_hash_, file_hash, collated_data_file_hash);
|
||||
|
||||
if (block_round < cur_round_ || block_round >= cur_round_ + blocks_.size()) {
|
||||
VLOG(VALIDATOR_SESSION_NOTICE) << this << "[node " << src << "][broadcast " << block_id
|
||||
<< "]: bad round=" << block_round << " cur_round" << cur_round_;
|
||||
return;
|
||||
}
|
||||
auto it = blocks_[block_round - cur_round_].find(block_id);
|
||||
if (it != blocks_[block_round - cur_round_].end()) {
|
||||
VLOG(VALIDATOR_SESSION_INFO) << this << "[node " << src << "][broadcast " << block_id << "]: duplicate";
|
||||
return;
|
||||
}
|
||||
|
||||
auto priority = description().get_node_priority(src_idx, block_round);
|
||||
if (priority < 0) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << "[node " << src << "][broadcast " << block_id
|
||||
<< "]: source is not allowed to generate blocks in this round";
|
||||
return;
|
||||
}
|
||||
|
||||
blocks_[block_round - cur_round_][block_id] = std::move(candidate);
|
||||
|
||||
VLOG(VALIDATOR_SESSION_WARNING) << this << ": received broadcast " << block_id;
|
||||
if (block_round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(!pending_approve_.count(block_id));
|
||||
CHECK(!approved_.count(block_id));
|
||||
CHECK(!pending_reject_.count(block_id));
|
||||
CHECK(!rejected_.count(block_id));
|
||||
|
||||
auto v = virtual_state_->choose_blocks_to_approve(description(), local_idx());
|
||||
for (auto &b : v) {
|
||||
if (b && SentBlock::get_block_id(b) == block_id) {
|
||||
try_approve_block(b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::process_message(PublicKeyHash src, td::BufferSlice data) {
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::process_query(PublicKeyHash src, td::BufferSlice data,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
if (!started_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "not started"));
|
||||
return;
|
||||
}
|
||||
auto F = fetch_tl_object<ton_api::validatorSession_downloadCandidate>(std::move(data), true);
|
||||
if (F.is_error()) {
|
||||
promise.set_error(F.move_as_error_prefix("validator session: cannot parse query: "));
|
||||
return;
|
||||
}
|
||||
auto f = F.move_as_ok();
|
||||
|
||||
auto round_id = static_cast<td::uint32>(f->round_);
|
||||
if (round_id > real_state_->cur_round_seqno()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "too big round id"));
|
||||
return;
|
||||
}
|
||||
const SentBlock *block = nullptr;
|
||||
auto id = description().candidate_id(description().get_source_idx(PublicKeyHash{f->id_->src_}), f->id_->root_hash_,
|
||||
f->id_->file_hash_, f->id_->collated_data_file_hash_);
|
||||
if (round_id < real_state_->cur_round_seqno()) {
|
||||
block = real_state_->get_committed_block(description(), round_id);
|
||||
if (!block || SentBlock::get_block_id(block) != id) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "wrong block"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
CHECK(round_id == real_state_->cur_round_seqno());
|
||||
bool found;
|
||||
block = real_state_->get_block(description(), id, found);
|
||||
if (!found || !block) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "wrong block"));
|
||||
return;
|
||||
}
|
||||
if (!real_state_->check_block_is_approved_by(description(), local_idx(), id)) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "not approved block"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
CHECK(block);
|
||||
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[promise = std::move(promise), src = f->id_->src_, round_id](td::Result<BlockCandidate> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error_prefix("failed to get candidate: "));
|
||||
} else {
|
||||
auto c = R.move_as_ok();
|
||||
auto obj = create_tl_object<ton_api::validatorSession_candidate>(
|
||||
src, round_id, c.id.root_hash, std::move(c.data), std::move(c.collated_data));
|
||||
promise.set_value(serialize_tl_object(obj, true));
|
||||
}
|
||||
});
|
||||
|
||||
callback_->get_approved_candidate(description().get_source_public_key(block->get_src_idx()), f->id_->root_hash_,
|
||||
f->id_->file_hash_, f->id_->collated_data_file_hash_, std::move(P));
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::candidate_decision_fail(td::uint32 round, ValidatorSessionCandidateId hash,
|
||||
std::string result, td::BufferSlice proof) {
|
||||
if (round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
LOG(ERROR) << this << ": failed candidate " << hash << ": " << result;
|
||||
pending_approve_.erase(hash);
|
||||
pending_reject_.emplace(hash, td::BufferSlice{result});
|
||||
rejected_.insert(hash);
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::candidate_decision_ok(td::uint32 round, ValidatorSessionCandidateId hash, RootHash root_hash,
|
||||
FileHash file_hash, td::uint32 ok_from) {
|
||||
if (round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << this << ": approved candidate " << hash;
|
||||
|
||||
auto obj = create_tl_object<ton_api::ton_blockIdApprove>(root_hash, file_hash);
|
||||
auto data = serialize_tl_object(obj, true);
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), hash, ok_from,
|
||||
round = cur_round_](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
LOG(FATAL) << print_id << ": failed to sign: " << R.move_as_error();
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_approved_signed, round, hash, ok_from,
|
||||
R.move_as_ok());
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, local_id(), std::move(data), std::move(P));
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::candidate_approved_signed(td::uint32 round, ValidatorSessionCandidateId hash,
|
||||
td::uint32 ok_from, td::BufferSlice signature) {
|
||||
pending_approve_.erase(hash);
|
||||
approved_[hash] = std::pair<td::uint32, td::BufferSlice>{ok_from, std::move(signature)};
|
||||
|
||||
if (ok_from <= td::Clocks::system()) {
|
||||
request_new_block(false);
|
||||
} else {
|
||||
LOG(WARNING) << "too new block. ts=" << ok_from;
|
||||
alarm_timestamp().relax(td::Timestamp::at_unix(ok_from));
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::generated_block(td::uint32 round, ValidatorSessionCandidateId root_hash,
|
||||
td::BufferSlice data, td::BufferSlice collated_data) {
|
||||
if (round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
if (data.size() > description().opts().max_block_size ||
|
||||
collated_data.size() > description().opts().max_collated_data_size) {
|
||||
LOG(ERROR) << this << ": generated candidate is too big. Dropping. size=" << data.size() << " "
|
||||
<< collated_data.size();
|
||||
return;
|
||||
}
|
||||
auto file_hash = sha256_bits256(data.as_slice());
|
||||
auto collated_data_file_hash = sha256_bits256(collated_data.as_slice());
|
||||
|
||||
auto b = create_tl_object<ton_api::validatorSession_candidate>(local_id().tl(), round, root_hash, std::move(data),
|
||||
std::move(collated_data));
|
||||
|
||||
auto B = serialize_tl_object(b, true);
|
||||
|
||||
auto block_id = description().candidate_id(local_idx(), root_hash, file_hash, collated_data_file_hash);
|
||||
|
||||
td::actor::send_closure(catchain_, &catchain::CatChain::send_broadcast, std::move(B));
|
||||
|
||||
blocks_[0].emplace(block_id, std::move(b));
|
||||
pending_generate_ = false;
|
||||
generated_ = true;
|
||||
generated_block_ = block_id;
|
||||
|
||||
request_new_block(true);
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::signed_block(td::uint32 round, ValidatorSessionCandidateId hash, td::BufferSlice signature) {
|
||||
if (round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
pending_sign_ = false;
|
||||
signed_ = true;
|
||||
signed_block_ = hash;
|
||||
signature_ = std::move(signature);
|
||||
|
||||
request_new_block(false);
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::alarm() {
|
||||
alarm_timestamp() = td::Timestamp::never();
|
||||
check_all();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::check_vote_for_slot(td::uint32 att) {
|
||||
if (!catchain_started_) {
|
||||
return;
|
||||
}
|
||||
if (!started_) {
|
||||
return;
|
||||
}
|
||||
if (virtual_state_->check_need_generate_vote_for(description(), local_idx(), att)) {
|
||||
request_new_block(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::check_generate_slot() {
|
||||
if (!catchain_started_) {
|
||||
return;
|
||||
}
|
||||
if (!generated_ && !pending_generate_ && started_) {
|
||||
if (real_state_->check_block_is_sent_by(description(), local_idx())) {
|
||||
generated_ = true;
|
||||
sent_generated_ = true;
|
||||
return;
|
||||
}
|
||||
auto priority = description().get_node_priority(local_idx(), cur_round_);
|
||||
if (priority >= 0) {
|
||||
auto t = td::Timestamp::at(round_started_at_.at() + description().get_delay(priority));
|
||||
if (t.is_in_past()) {
|
||||
pending_generate_ = true;
|
||||
|
||||
td::PerfWarningTimer timer{"too long block generation", 1.0};
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), timer = std::move(timer),
|
||||
round = cur_round_](td::Result<BlockCandidate> R) {
|
||||
if (R.is_ok()) {
|
||||
auto c = R.move_as_ok();
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::generated_block, round, c.id.root_hash,
|
||||
c.data.clone(), c.collated_data.clone());
|
||||
} else {
|
||||
LOG(WARNING) << print_id << ": failed to generate block candidate: " << R.move_as_error();
|
||||
}
|
||||
});
|
||||
callback_->on_generate_slot(cur_round_, std::move(P));
|
||||
} else {
|
||||
alarm_timestamp().relax(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::try_approve_block(const SentBlock *block) {
|
||||
auto block_id = SentBlock::get_block_id(block);
|
||||
{
|
||||
auto it = approved_.find(block_id);
|
||||
if (it != approved_.end()) {
|
||||
if (it->second.first <= td::Clocks::system()) {
|
||||
request_new_block(false);
|
||||
} else {
|
||||
alarm_timestamp().relax(td::Timestamp::at_unix(it->second.first));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pending_approve_.count(block_id) || rejected_.count(block_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto delay = block ? description().get_delay(description().get_node_priority(block->get_src_idx(), cur_round_))
|
||||
: description().get_empty_block_delay();
|
||||
auto T = td::Timestamp::at(round_started_at_.at() + delay);
|
||||
if (!T.is_in_past()) {
|
||||
alarm_timestamp().relax(T);
|
||||
return;
|
||||
}
|
||||
|
||||
if (block) {
|
||||
auto T = td::Timestamp::at(round_started_at_.at() + description().get_delay(block->get_src_idx()) + 2.0);
|
||||
auto it = blocks_[0].find(block_id);
|
||||
|
||||
if (it != blocks_[0].end()) {
|
||||
td::PerfWarningTimer timer{"too long block validation", 1.0};
|
||||
auto &B = it->second;
|
||||
|
||||
auto P = td::PromiseCreator::lambda([round = cur_round_, hash = block_id, root_hash = block->get_root_hash(),
|
||||
file_hash = block->get_file_hash(), timer = std::move(timer),
|
||||
SelfId = actor_id(this)](td::Result<CandidateDecision> res) {
|
||||
auto R = res.move_as_ok();
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_ok, round, hash, root_hash,
|
||||
file_hash, R.ok_from());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_fail, round, hash, R.reason(),
|
||||
R.proof());
|
||||
}
|
||||
});
|
||||
pending_approve_.insert(block_id);
|
||||
CHECK(static_cast<td::int32>(cur_round_) == B->round_);
|
||||
|
||||
callback_->on_candidate(cur_round_, description().get_source_public_key(block->get_src_idx()), B->root_hash_,
|
||||
B->data_.clone(), B->collated_data_.clone(), std::move(P));
|
||||
} else if (T.is_in_past()) {
|
||||
if (!active_requests_.count(block_id)) {
|
||||
auto v = virtual_state_->get_block_approvers(description(), block_id);
|
||||
if (v.size() > 0) {
|
||||
auto id = description().get_source_id(v[td::Random::fast(0, static_cast<td::int32>(v.size() - 1))]);
|
||||
auto src_id = description().get_source_id(block->get_src_idx());
|
||||
active_requests_.insert(block_id);
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, src_id, print_id = print_id(),
|
||||
hash = block_id, round = cur_round_](td::Result<td::BufferSlice> R) {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::end_request, round, hash);
|
||||
if (R.is_error()) {
|
||||
VLOG(VALIDATOR_SESSION_WARNING)
|
||||
<< print_id << ": failed to get candidate " << hash << " from " << id << ": " << R.move_as_error();
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::process_broadcast, src_id, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
|
||||
get_broadcast_p2p(id, block->get_file_hash(), block->get_collated_data_file_hash(),
|
||||
description().get_source_id(block->get_src_idx()), cur_round_, block->get_root_hash(),
|
||||
std::move(P), td::Timestamp::in(2.0));
|
||||
} else {
|
||||
LOG(VALIDATOR_SESSION_DEBUG) << this << ": no nodes to download candidate " << block << " from";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alarm_timestamp().relax(T);
|
||||
}
|
||||
} else {
|
||||
approved_[block_id] = std::pair<td::uint32, td::BufferSlice>{0, td::BufferSlice{}};
|
||||
|
||||
request_new_block(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::get_broadcast_p2p(PublicKeyHash node, ValidatorSessionFileHash file_hash,
|
||||
ValidatorSessionCollatedDataFileHash collated_data_file_hash,
|
||||
PublicKeyHash src, td::uint32 round, ValidatorSessionRootHash root_hash,
|
||||
td::Promise<td::BufferSlice> promise, td::Timestamp timeout) {
|
||||
if (timeout.is_in_past()) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::timeout, "timeout"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto obj = create_tl_object<ton_api::validatorSession_downloadCandidate>(
|
||||
round,
|
||||
create_tl_object<ton_api::validatorSession_candidateId>(src.tl(), root_hash, file_hash, collated_data_file_hash));
|
||||
|
||||
td::actor::send_closure(catchain_, &catchain::CatChain::send_query_via, node, "download candidate",
|
||||
std::move(promise), timeout, serialize_tl_object(obj, true),
|
||||
description().opts().max_block_size + description().opts().max_collated_data_size + 1024,
|
||||
rldp_);
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::check_sign_slot() {
|
||||
if (!catchain_started_) {
|
||||
return;
|
||||
}
|
||||
if (pending_sign_ || !started_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (real_state_->check_block_is_signed_by(description(), local_idx())) {
|
||||
signed_ = true;
|
||||
return;
|
||||
}
|
||||
if (signed_) {
|
||||
request_new_block(false);
|
||||
return;
|
||||
}
|
||||
auto found = false;
|
||||
auto B = virtual_state_->choose_block_to_sign(description(), local_idx(), found);
|
||||
if (found) {
|
||||
if (!B) {
|
||||
signed_ = true;
|
||||
signed_block_ = skip_round_candidate_id();
|
||||
|
||||
request_new_block(false);
|
||||
} else {
|
||||
pending_sign_ = true;
|
||||
|
||||
auto obj = create_tl_object<ton_api::ton_blockId>(B->get_root_hash(), B->get_file_hash());
|
||||
auto data = serialize_tl_object(obj, true);
|
||||
|
||||
auto P =
|
||||
td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), hash = SentBlock::get_block_id(B),
|
||||
round = cur_round_](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
LOG(FATAL) << print_id << ": failed to sign: " << R.move_as_error();
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::signed_block, round, hash, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, local_id(), std::move(data), std::move(P));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::check_approve() {
|
||||
if (!catchain_started_) {
|
||||
return;
|
||||
}
|
||||
if (!started_) {
|
||||
return;
|
||||
}
|
||||
auto to_approve = virtual_state_->choose_blocks_to_approve(description(), local_idx());
|
||||
for (auto &block : to_approve) {
|
||||
try_approve_block(block);
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::check_action(td::uint32 att) {
|
||||
if (!catchain_started_) {
|
||||
return;
|
||||
}
|
||||
if (!requested_new_block_) {
|
||||
auto action = virtual_state_->create_action(description(), local_idx(), att);
|
||||
if (action && action->get_id() != ton_api::validatorSession_message_empty::ID) {
|
||||
request_new_block(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::check_all() {
|
||||
if (!catchain_started_) {
|
||||
return;
|
||||
}
|
||||
if (virtual_state_->cur_round_seqno() != cur_round_) {
|
||||
request_new_block(false);
|
||||
return;
|
||||
}
|
||||
if (round_debug_at_.is_in_past()) {
|
||||
td::BufferSlice buf{10000};
|
||||
td::StringBuilder sb{buf.as_slice()};
|
||||
real_state_->dump_cur_attempt(description(), sb);
|
||||
virtual_state_->dump_cur_attempt(description(), sb);
|
||||
auto to_approve = virtual_state_->choose_blocks_to_approve(description(), local_idx());
|
||||
sb << "to approve: \n";
|
||||
for (auto &B : to_approve) {
|
||||
if (B) {
|
||||
auto block_id = SentBlock::get_block_id(B);
|
||||
auto pending = pending_approve_.count(block_id) == 1;
|
||||
auto rejected = rejected_.count(block_id) == 1;
|
||||
auto accepted = approved_.count(block_id) == 1;
|
||||
sb << " " << block_id << " pending: " << pending << " rejected: " << rejected << " accepted: " << accepted
|
||||
<< "\n";
|
||||
} else {
|
||||
sb << " SKIP\n";
|
||||
}
|
||||
}
|
||||
LOG(ERROR) << sb.as_cslice();
|
||||
round_debug_at_ = td::Timestamp::in(60.0);
|
||||
}
|
||||
auto att = description().get_attempt_seqno(description().get_ts());
|
||||
check_sign_slot();
|
||||
check_approve();
|
||||
check_generate_slot();
|
||||
check_action(att);
|
||||
check_vote_for_slot(att);
|
||||
alarm_timestamp().relax(round_debug_at_);
|
||||
alarm_timestamp().relax(description().attempt_start_at(att + 1));
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::request_new_block(bool now) {
|
||||
if (requested_new_block_now_) {
|
||||
return;
|
||||
}
|
||||
if (!now && requested_new_block_) {
|
||||
return;
|
||||
}
|
||||
requested_new_block_ = true;
|
||||
if (now) {
|
||||
requested_new_block_now_ = true;
|
||||
td::actor::send_closure(catchain_, &catchain::CatChain::need_new_block, td::Timestamp::now());
|
||||
} else {
|
||||
double lambda = 10.0 / description().get_total_nodes();
|
||||
double x = -1 / lambda * log(td::Random::fast(1, 999) * 0.001);
|
||||
if (x > 0.5) {
|
||||
x = 0.5;
|
||||
}
|
||||
td::actor::send_closure(catchain_, &catchain::CatChain::need_new_block, td::Timestamp::in(x));
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::on_new_round(td::uint32 round) {
|
||||
if (round != 0) {
|
||||
CHECK(cur_round_ < round);
|
||||
pending_generate_ = false;
|
||||
generated_ = false;
|
||||
sent_generated_ = false;
|
||||
|
||||
pending_approve_.clear();
|
||||
rejected_.clear();
|
||||
pending_reject_.clear();
|
||||
approved_.clear();
|
||||
|
||||
pending_sign_ = false;
|
||||
signed_ = false;
|
||||
signature_ = td::BufferSlice{};
|
||||
signed_block_ = skip_round_candidate_id();
|
||||
|
||||
active_requests_.clear();
|
||||
}
|
||||
|
||||
while (cur_round_ < round) {
|
||||
auto block = real_state_->get_committed_block(description(), cur_round_);
|
||||
//CHECK(block);
|
||||
auto sigs = real_state_->get_committed_block_signatures(description(), cur_round_);
|
||||
CHECK(sigs);
|
||||
auto approve_sigs = real_state_->get_committed_block_approve_signatures(description(), cur_round_);
|
||||
CHECK(approve_sigs);
|
||||
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> export_sigs;
|
||||
CHECK(sigs->size() == description().get_total_nodes());
|
||||
for (td::uint32 i = 0; i < description().get_total_nodes(); i++) {
|
||||
auto sig = sigs->at(i);
|
||||
if (sig) {
|
||||
CHECK(description().is_persistent(sig));
|
||||
export_sigs.emplace_back(description().get_source_id(i), sig->value().clone());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> export_approve_sigs;
|
||||
CHECK(approve_sigs->size() == description().get_total_nodes());
|
||||
for (td::uint32 i = 0; i < description().get_total_nodes(); i++) {
|
||||
auto sig = approve_sigs->at(i);
|
||||
if (sig) {
|
||||
CHECK(description().is_persistent(sig));
|
||||
export_approve_sigs.emplace_back(description().get_source_id(i), sig->value().clone());
|
||||
}
|
||||
}
|
||||
|
||||
auto it = blocks_[0].find(SentBlock::get_block_id(block));
|
||||
if (!block) {
|
||||
callback_->on_block_skipped(cur_round_);
|
||||
} else if (it == blocks_[0].end()) {
|
||||
callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()),
|
||||
block->get_root_hash(), block->get_file_hash(), td::BufferSlice(),
|
||||
std::move(export_sigs), std::move(export_approve_sigs));
|
||||
} else {
|
||||
callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()),
|
||||
block->get_root_hash(), block->get_file_hash(), it->second->data_.clone(),
|
||||
std::move(export_sigs), std::move(export_approve_sigs));
|
||||
}
|
||||
cur_round_++;
|
||||
for (size_t i = 0; i < blocks_.size() - 1; i++) {
|
||||
blocks_[i] = std::move(blocks_[i + 1]);
|
||||
}
|
||||
blocks_[blocks_.size() - 1].clear();
|
||||
}
|
||||
|
||||
round_started_at_ = td::Timestamp::now();
|
||||
round_debug_at_ = td::Timestamp::in(60.0);
|
||||
check_all();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::on_catchain_started() {
|
||||
catchain_started_ = true;
|
||||
|
||||
auto X = virtual_state_->get_blocks_approved_by(description(), local_idx());
|
||||
|
||||
for (auto &x : X) {
|
||||
if (x) {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), round = virtual_state_->cur_round_seqno(),
|
||||
src = description().get_source_id(x->get_src_idx()),
|
||||
root_hash = x->get_root_hash()](td::Result<BlockCandidate> R) {
|
||||
if (R.is_error()) {
|
||||
LOG(ERROR) << "failed to get candidate: " << R.move_as_error();
|
||||
} else {
|
||||
auto B = R.move_as_ok();
|
||||
auto broadcast = create_tl_object<ton_api::validatorSession_candidate>(
|
||||
src.tl(), round, root_hash, std::move(B.data), std::move(B.collated_data));
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::process_broadcast, src,
|
||||
serialize_tl_object(broadcast, true));
|
||||
}
|
||||
});
|
||||
callback_->get_approved_candidate(description().get_source_public_key(x->get_src_idx()), x->get_root_hash(),
|
||||
x->get_file_hash(), x->get_collated_data_file_hash(), std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
check_all();
|
||||
}
|
||||
|
||||
ValidatorSessionImpl::ValidatorSessionImpl(catchain::CatChainSessionId session_id, ValidatorSessionOptions opts,
|
||||
PublicKeyHash local_id, std::vector<ValidatorSessionNode> nodes,
|
||||
std::unique_ptr<Callback> callback,
|
||||
td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<rldp::Rldp> rldp,
|
||||
td::actor::ActorId<overlay::Overlays> overlays, std::string db_root)
|
||||
: unique_hash_(session_id)
|
||||
, callback_(std::move(callback))
|
||||
, db_root_(db_root)
|
||||
, keyring_(keyring)
|
||||
, adnl_(adnl)
|
||||
, rldp_(rldp)
|
||||
, overlay_manager_(overlays) {
|
||||
description_ = ValidatorSessionDescription::create(std::move(opts), nodes, local_id);
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::start() {
|
||||
started_ = true;
|
||||
VLOG(VALIDATOR_SESSION_NOTICE) << this << ": started";
|
||||
|
||||
auto w = description().export_catchain_nodes();
|
||||
|
||||
catchain_ = catchain::CatChain::create(
|
||||
make_catchain_callback(),
|
||||
catchain::CatChainOptions{description().opts().catchain_idle_timeout, description().opts().catchain_max_deps},
|
||||
keyring_, adnl_, overlay_manager_, std::move(w), local_id(), unique_hash_, db_root_);
|
||||
|
||||
check_all();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::destroy() {
|
||||
if (!catchain_.empty()) {
|
||||
td::actor::send_closure(catchain_, &catchain::CatChain::destroy);
|
||||
catchain_.release();
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::start_up() {
|
||||
CHECK(!rldp_.empty());
|
||||
cur_round_ = 0;
|
||||
round_started_at_ = td::Timestamp::now();
|
||||
round_debug_at_ = td::Timestamp::in(60.0);
|
||||
real_state_ = ValidatorSessionState::create(description());
|
||||
real_state_ = ValidatorSessionState::move_to_persistent(description(), real_state_);
|
||||
virtual_state_ = real_state_;
|
||||
|
||||
check_all();
|
||||
td::actor::send_closure(rldp_, &rldp::Rldp::add_id, description().get_source_adnl_id(local_idx()));
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<ValidatorSession> ValidatorSession::create(
|
||||
catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<rldp::Rldp> rldp, td::actor::ActorId<overlay::Overlays> overlays, std::string db_root) {
|
||||
return td::actor::create_actor<ValidatorSessionImpl>("session", session_id, std::move(opts), local_id,
|
||||
std::move(nodes), std::move(callback), keyring, adnl, rldp,
|
||||
overlays, db_root);
|
||||
}
|
||||
|
||||
td::Bits256 ValidatorSessionOptions::get_hash() const {
|
||||
return create_hash_tl_object<ton_api::validatorSession_config>(
|
||||
catchain_idle_timeout, catchain_max_deps, round_candidates, next_candidate_delay, round_attempt_duration,
|
||||
max_round_attempts, max_block_size, max_collated_data_size);
|
||||
}
|
||||
|
||||
ValidatorSessionOptions::ValidatorSessionOptions(const ValidatorSessionConfig &conf) {
|
||||
CHECK(!conf.proto_version);
|
||||
catchain_idle_timeout = conf.catchain_idle_timeout;
|
||||
catchain_max_deps = conf.catchain_max_deps;
|
||||
max_block_size = conf.max_block_size;
|
||||
max_collated_data_size = conf.max_collated_data_size;
|
||||
max_round_attempts = conf.max_round_attempts;
|
||||
next_candidate_delay = conf.next_candidate_delay;
|
||||
round_attempt_duration = conf.round_attempt_duration;
|
||||
round_candidates = conf.round_candidates;
|
||||
}
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
119
validator-session/validator-session.h
Normal file
119
validator-session/validator-session.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "adnl/adnl.h"
|
||||
#include "adnl/utils.hpp"
|
||||
#include "rldp/rldp.h"
|
||||
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
#include "overlay/overlays.h"
|
||||
#include "catchain/catchain-types.h"
|
||||
|
||||
#include "validator-session-types.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
class ValidatorSession : public td::actor::Actor {
|
||||
public:
|
||||
struct PrintId {
|
||||
catchain::CatChainSessionId instance_;
|
||||
PublicKeyHash local_id_;
|
||||
};
|
||||
virtual PrintId print_id() const = 0;
|
||||
|
||||
class CandidateDecision {
|
||||
public:
|
||||
bool is_ok() const {
|
||||
return ok_;
|
||||
}
|
||||
td::uint32 ok_from() const {
|
||||
return ok_from_;
|
||||
}
|
||||
std::string reason() const {
|
||||
return reason_;
|
||||
}
|
||||
td::BufferSlice proof() const {
|
||||
return proof_.clone();
|
||||
}
|
||||
CandidateDecision(td::uint32 ok_from) {
|
||||
ok_ = true;
|
||||
ok_from_ = ok_from;
|
||||
}
|
||||
CandidateDecision(std::string reason, td::BufferSlice proof)
|
||||
: ok_(false), reason_(reason), proof_(std::move(proof)) {
|
||||
}
|
||||
|
||||
private:
|
||||
bool ok_ = false;
|
||||
td::uint32 ok_from_ = 0;
|
||||
std::string reason_;
|
||||
td::BufferSlice proof_;
|
||||
};
|
||||
|
||||
class Callback {
|
||||
public:
|
||||
virtual void on_candidate(td::uint32 round, PublicKey source, ValidatorSessionRootHash root_hash,
|
||||
td::BufferSlice data, td::BufferSlice collated_data,
|
||||
td::Promise<CandidateDecision> promise) = 0;
|
||||
virtual void on_generate_slot(td::uint32 round, td::Promise<BlockCandidate> promise) = 0;
|
||||
virtual void on_block_committed(td::uint32 round, PublicKey source, ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash, td::BufferSlice data,
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> signatures,
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> approve_signatures) = 0;
|
||||
virtual void on_block_skipped(td::uint32 round) = 0;
|
||||
virtual void get_approved_candidate(PublicKey source, ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash,
|
||||
ValidatorSessionCollatedDataFileHash collated_data_file_hash,
|
||||
td::Promise<BlockCandidate> promise) = 0;
|
||||
virtual ~Callback() = default;
|
||||
};
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void destroy() = 0;
|
||||
|
||||
static td::actor::ActorOwn<ValidatorSession> create(
|
||||
catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<rldp::Rldp> rldp, td::actor::ActorId<overlay::Overlays> overlays, std::string db_root);
|
||||
virtual ~ValidatorSession() = default;
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb,
|
||||
const ton::validatorsession::ValidatorSession::PrintId &print_id) {
|
||||
sb << "[validatorsession " << print_id.instance_ << "@" << print_id.local_id_ << "]";
|
||||
return sb;
|
||||
}
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::validatorsession::ValidatorSession *session) {
|
||||
sb << session->print_id();
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
210
validator-session/validator-session.hpp
Normal file
210
validator-session/validator-session.hpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <atomic>
|
||||
|
||||
#include "validator-session.h"
|
||||
#include "validator-session-state.h"
|
||||
|
||||
#include "keys/encryptor.h"
|
||||
|
||||
#include "catchain/catchain.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validatorsession {
|
||||
|
||||
class ValidatorSessionImpl : public ValidatorSession {
|
||||
private:
|
||||
class BlockExtra : public catchain::CatChainBlock::Extra {
|
||||
public:
|
||||
const ValidatorSessionState *get_ref() const {
|
||||
return state_;
|
||||
}
|
||||
BlockExtra(const ValidatorSessionState *state) : state_(std::move(state)) {
|
||||
}
|
||||
|
||||
private:
|
||||
const ValidatorSessionState *state_;
|
||||
};
|
||||
|
||||
bool requested_new_block_ = false;
|
||||
bool requested_new_block_now_ = false;
|
||||
const ValidatorSessionState *real_state_ = nullptr;
|
||||
const ValidatorSessionState *virtual_state_ = nullptr;
|
||||
|
||||
td::uint32 cur_round_ = 0;
|
||||
td::Timestamp round_started_at_ = td::Timestamp::never();
|
||||
td::Timestamp round_debug_at_ = td::Timestamp::never();
|
||||
std::set<ValidatorSessionCandidateId> pending_approve_;
|
||||
std::map<ValidatorSessionCandidateId, td::BufferSlice> pending_reject_;
|
||||
std::set<ValidatorSessionCandidateId> rejected_;
|
||||
std::map<ValidatorSessionCandidateId, std::pair<UnixTime, td::BufferSlice>> approved_;
|
||||
|
||||
std::set<ValidatorSessionCandidateId> active_requests_;
|
||||
|
||||
bool pending_generate_ = false;
|
||||
bool generated_ = false;
|
||||
bool sent_generated_ = false;
|
||||
ValidatorSessionCandidateId generated_block_;
|
||||
|
||||
bool pending_sign_ = false;
|
||||
bool signed_ = false;
|
||||
ValidatorSessionCandidateId signed_block_;
|
||||
td::BufferSlice signature_;
|
||||
|
||||
std::array<std::map<ValidatorSessionCandidateId, tl_object_ptr<ton_api::validatorSession_candidate>>, 100> blocks_;
|
||||
|
||||
catchain::CatChainSessionId unique_hash_;
|
||||
|
||||
std::unique_ptr<Callback> callback_;
|
||||
std::string db_root_;
|
||||
|
||||
td::actor::ActorId<keyring::Keyring> keyring_;
|
||||
td::actor::ActorId<adnl::Adnl> adnl_;
|
||||
td::actor::ActorId<rldp::Rldp> rldp_;
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager_;
|
||||
td::actor::ActorOwn<catchain::CatChain> catchain_;
|
||||
std::unique_ptr<ValidatorSessionDescription> description_;
|
||||
|
||||
void on_new_round(td::uint32 round);
|
||||
void on_catchain_started();
|
||||
void check_vote_for_slot(td::uint32 att);
|
||||
void check_generate_slot();
|
||||
void check_sign_slot();
|
||||
void check_approve();
|
||||
void check_action(td::uint32 att);
|
||||
void check_all();
|
||||
|
||||
std::unique_ptr<catchain::CatChain::Callback> make_catchain_callback() {
|
||||
class cb : public catchain::CatChain::Callback {
|
||||
public:
|
||||
void process_blocks(std::vector<catchain::CatChainBlock *> blocks) override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::process_blocks, std::move(blocks));
|
||||
}
|
||||
void finished_processing() override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::finished_processing);
|
||||
}
|
||||
void preprocess_block(catchain::CatChainBlock *block) override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::preprocess_block, block);
|
||||
}
|
||||
void process_broadcast(PublicKeyHash src, td::BufferSlice data) override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::process_broadcast, src, std::move(data));
|
||||
}
|
||||
void process_message(PublicKeyHash src, td::BufferSlice data) override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::process_message, src, std::move(data));
|
||||
}
|
||||
void process_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::process_query, src, std::move(data), std::move(promise));
|
||||
}
|
||||
void started() override {
|
||||
td::actor::send_closure(id_, &ValidatorSessionImpl::on_catchain_started);
|
||||
}
|
||||
|
||||
cb(td::actor::ActorId<ValidatorSessionImpl> id) : id_(id) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::actor::ActorId<ValidatorSessionImpl> id_;
|
||||
};
|
||||
|
||||
return std::make_unique<cb>(actor_id(this));
|
||||
}
|
||||
|
||||
auto &description() {
|
||||
return *description_.get();
|
||||
}
|
||||
|
||||
td::uint32 local_idx() {
|
||||
return description_->get_self_idx();
|
||||
}
|
||||
ton::PublicKeyHash local_id() {
|
||||
return description_->get_source_id(description_->get_self_idx());
|
||||
}
|
||||
|
||||
void request_new_block(bool now);
|
||||
void get_broadcast_p2p(PublicKeyHash node, ValidatorSessionFileHash file_hash,
|
||||
ValidatorSessionCollatedDataFileHash collated_data_file_hash, PublicKeyHash src,
|
||||
td::uint32 round, ValidatorSessionRootHash root_hash, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout);
|
||||
|
||||
bool started_ = false;
|
||||
bool catchain_started_ = false;
|
||||
|
||||
public:
|
||||
ValidatorSessionImpl(catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<rldp::Rldp> rldp, td::actor::ActorId<overlay::Overlays> overlays,
|
||||
std::string db_root);
|
||||
void start_up() override;
|
||||
void alarm() override;
|
||||
|
||||
void start() override;
|
||||
void destroy() override;
|
||||
|
||||
void process_blocks(std::vector<catchain::CatChainBlock *> blocks);
|
||||
void finished_processing();
|
||||
void preprocess_block(catchain::CatChainBlock *block);
|
||||
void process_broadcast(PublicKeyHash src, td::BufferSlice data);
|
||||
void process_message(PublicKeyHash src, td::BufferSlice data);
|
||||
void process_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
|
||||
|
||||
void try_approve_block(const SentBlock *block);
|
||||
void try_sign();
|
||||
|
||||
void candidate_decision_fail(td::uint32 round, ValidatorSessionCandidateId hash, std::string result,
|
||||
td::BufferSlice proof);
|
||||
void candidate_decision_ok(td::uint32 round, ValidatorSessionCandidateId hash, RootHash root_hash, FileHash file_hash,
|
||||
td::uint32 ok_from);
|
||||
void candidate_approved_signed(td::uint32 round, ValidatorSessionCandidateId hash, td::uint32 ok_from,
|
||||
td::BufferSlice signature);
|
||||
|
||||
void generated_block(td::uint32 round, ValidatorSessionRootHash root_hash, td::BufferSlice data,
|
||||
td::BufferSlice collated);
|
||||
void signed_block(td::uint32 round, ValidatorSessionCandidateId hash, td::BufferSlice signature);
|
||||
|
||||
void end_request(td::uint32 round, ValidatorSessionCandidateId block_id) {
|
||||
if (cur_round_ == round) {
|
||||
active_requests_.erase(block_id);
|
||||
}
|
||||
}
|
||||
|
||||
PrintId print_id() const override {
|
||||
return PrintId{unique_hash_, description_->get_source_id(description_->get_self_idx())};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb,
|
||||
const ton::validatorsession::ValidatorSessionImpl *session) {
|
||||
sb << session->print_id();
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
Loading…
Add table
Add a link
Reference in a new issue