1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

View 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)

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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