1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00

emergency update

This commit is contained in:
ton 2020-03-24 03:32:16 +04:00
parent 5d846e0aaf
commit 9f351fc29f
87 changed files with 2486 additions and 655 deletions

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once

View file

@ -19,6 +19,10 @@
#include "adnl-network-manager.hpp"
#include "adnl-peer-table.h"
#include "auto/tl/ton_api.hpp"
#include "td/utils/overloaded.h"
namespace ton {
namespace adnl {
@ -27,25 +31,84 @@ td::actor::ActorOwn<AdnlNetworkManager> AdnlNetworkManager::create(td::uint16 po
return td::actor::create_actor<AdnlNetworkManagerImpl>("NetworkManager", port);
}
void AdnlNetworkManagerImpl::add_listening_udp_port(td::uint16 port) {
AdnlNetworkManagerImpl::OutDesc *AdnlNetworkManagerImpl::choose_out_iface(td::uint8 cat, td::uint32 priority) {
auto it = out_desc_.upper_bound(priority);
while (true) {
if (it == out_desc_.begin()) {
return nullptr;
}
it--;
auto &v = it->second;
for (auto &x : v) {
if (x.cat_mask.test(cat)) {
return &x;
}
}
}
}
size_t AdnlNetworkManagerImpl::add_listening_udp_port(td::uint16 port) {
auto it = port_2_socket_.find(port);
if (it != port_2_socket_.end()) {
return it->second;
}
class Callback : public td::UdpServer::Callback {
public:
Callback(td::actor::ActorShared<AdnlNetworkManagerImpl> manager) : manager_(std::move(manager)) {
Callback(td::actor::ActorShared<AdnlNetworkManagerImpl> manager, size_t idx)
: manager_(std::move(manager)), idx_(idx) {
}
private:
td::actor::ActorShared<AdnlNetworkManagerImpl> manager_;
size_t idx_;
void on_udp_message(td::UdpMessage udp_message) override {
td::actor::send_closure_later(manager_, &AdnlNetworkManagerImpl::receive_udp_message, std::move(udp_message));
td::actor::send_closure_later(manager_, &AdnlNetworkManagerImpl::receive_udp_message, std::move(udp_message),
idx_);
}
};
auto X = td::UdpServer::create("udp server", port, std::make_unique<Callback>(actor_shared(this)));
auto idx = udp_sockets_.size();
auto X = td::UdpServer::create("udp server", port, std::make_unique<Callback>(actor_shared(this), idx));
X.ensure();
udp_servers_.emplace(port, X.move_as_ok());
port_2_socket_[port] = idx;
udp_sockets_.push_back(UdpSocketDesc{port, X.move_as_ok()});
return idx;
}
void AdnlNetworkManagerImpl::receive_udp_message(td::UdpMessage message) {
void AdnlNetworkManagerImpl::add_self_addr(td::IPAddress addr, AdnlCategoryMask cat_mask, td::uint32 priority) {
auto port = td::narrow_cast<td::uint16>(addr.get_port());
size_t idx = add_listening_udp_port(port);
add_in_addr(InDesc{port, nullptr, cat_mask}, idx);
auto d = OutDesc{port, td::IPAddress{}, nullptr, idx};
for (auto &it : out_desc_[priority]) {
if (it == d) {
it.cat_mask |= cat_mask;
return;
}
}
d.cat_mask = cat_mask;
out_desc_[priority].push_back(std::move(d));
}
void AdnlNetworkManagerImpl::add_proxy_addr(td::IPAddress addr, td::uint16 local_port, std::shared_ptr<AdnlProxy> proxy,
AdnlCategoryMask cat_mask, td::uint32 priority) {
size_t idx = add_listening_udp_port(local_port);
add_in_addr(InDesc{local_port, proxy, cat_mask}, idx);
auto d = OutDesc{local_port, addr, proxy, idx};
for (auto &it : out_desc_[priority]) {
if (it == d) {
it.cat_mask |= cat_mask;
return;
}
}
d.cat_mask = cat_mask;
proxy_register(d);
out_desc_[priority].push_back(std::move(d));
}
void AdnlNetworkManagerImpl::receive_udp_message(td::UdpMessage message, size_t idx) {
if (!callback_) {
LOG(ERROR) << this << ": dropping IN message [?->?]: peer table unitialized";
return;
@ -54,6 +117,96 @@ void AdnlNetworkManagerImpl::receive_udp_message(td::UdpMessage message) {
VLOG(ADNL_WARNING) << this << ": dropping ERROR message: " << message.error;
return;
}
if (message.data.size() < 32) {
VLOG(ADNL_WARNING) << this << ": received too small proxy packet of size " << message.data.size();
return;
}
if (message.data.size() >= get_mtu() + 128) {
VLOG(ADNL_NOTICE) << this << ": received huge packet of size " << message.data.size();
}
CHECK(idx < udp_sockets_.size());
auto &socket = udp_sockets_[idx];
AdnlCategoryMask cat_mask;
bool from_proxy = false;
if (socket.allow_proxy) {
td::Bits256 x;
x.as_slice().copy_from(message.data.as_slice().truncate(32));
auto it = proxy_addrs_.find(x);
if (it != proxy_addrs_.end()) {
from_proxy = true;
CHECK(it->second < in_desc_.size());
auto &proxy_iface = in_desc_[it->second];
CHECK(proxy_iface.is_proxy());
auto R = in_desc_[it->second].proxy->decrypt(std::move(message.data));
if (R.is_error()) {
VLOG(ADNL_WARNING) << this << ": failed to decrypt proxy mesage: " << R.move_as_error();
return;
}
auto packet = R.move_as_ok();
if (packet.flags & 1) {
message.address.init_host_port(td::IPAddress::ipv4_to_str(packet.ip), packet.port).ensure();
} else {
message.address = td::IPAddress{};
}
if ((packet.flags & 6) == 6) {
if (proxy_iface.received.packet_is_delivered(packet.adnl_start_time, packet.seqno)) {
VLOG(ADNL_WARNING) << this << ": dropping duplicate proxy packet";
return;
}
}
if (packet.flags & 8) {
if (packet.date < td::Clocks::system() - 60 || packet.date > td::Clocks::system() + 60) {
VLOG(ADNL_WARNING) << this << ": dropping proxy packet: bad time " << packet.date;
return;
}
}
if (!(packet.flags & (1 << 16))) {
VLOG(ADNL_WARNING) << this << ": dropping proxy packet: packet has outbound flag";
return;
}
if (packet.flags & (1 << 17)) {
auto F = fetch_tl_object<ton_api::adnl_ProxyControlPacket>(std::move(packet.data), true);
if (F.is_error()) {
VLOG(ADNL_WARNING) << this << ": dropping proxy packet: bad control packet";
return;
}
ton_api::downcast_call(*F.move_as_ok().get(),
td::overloaded(
[&](const ton_api::adnl_proxyControlPacketPing &f) {
auto &v = *proxy_iface.out_desc;
auto data =
create_serialize_tl_object<ton_api::adnl_proxyControlPacketPong>(f.id_);
AdnlProxy::Packet p;
p.flags = 6 | (1 << 17);
p.ip = 0;
p.port = 0;
p.data = std::move(data);
p.adnl_start_time = Adnl::adnl_start_time();
p.seqno = ++v.out_seqno;
auto enc = v.proxy->encrypt(std::move(p));
td::UdpMessage M;
M.address = v.proxy_addr;
M.data = std::move(enc);
td::actor::send_closure(socket.server, &td::UdpServer::send, std::move(M));
},
[&](const ton_api::adnl_proxyControlPacketPong &f) {},
[&](const ton_api::adnl_proxyControlPacketRegister &f) {}));
return;
}
message.data = std::move(packet.data);
cat_mask = in_desc_[it->second].cat_mask;
}
}
if (!from_proxy) {
if (socket.in_desc == std::numeric_limits<size_t>::max()) {
VLOG(ADNL_WARNING) << this << ": received bad packet to proxy-only listenung port";
return;
}
cat_mask = in_desc_[socket.in_desc].cat_mask;
}
if (message.data.size() >= get_mtu()) {
VLOG(ADNL_NOTICE) << this << ": received huge packet of size " << message.data.size();
}
@ -63,49 +216,81 @@ void AdnlNetworkManagerImpl::receive_udp_message(td::UdpMessage message) {
}
VLOG(ADNL_EXTRA_DEBUG) << this << ": received message of size " << message.data.size();
callback_->receive_packet(message.address, std::move(message.data));
callback_->receive_packet(message.address, cat_mask, std::move(message.data));
}
void AdnlNetworkManagerImpl::send_udp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
td::uint32 priority, td::BufferSlice data) {
auto randseed = 1; // use DST?
while (priority > 0) {
if (out_desc_[priority].size() > 0) {
break;
}
priority--;
}
if (out_desc_[priority].size() == 0) {
VLOG(ADNL_WARNING) << this << ": dropping OUT message [" << src_id << "->" << dst_id << "]: no out desc";
auto it = adnl_id_2_cat_.find(src_id);
if (it == adnl_id_2_cat_.end()) {
VLOG(ADNL_WARNING) << this << ": dropping OUT message [" << src_id << "->" << dst_id << "]: unknown src";
return;
}
auto &dv = out_desc_[priority];
auto &v = dv[randseed % dv.size()];
auto out = choose_out_iface(it->second, priority);
if (!out) {
VLOG(ADNL_WARNING) << this << ": dropping OUT message [" << src_id << "->" << dst_id << "]: no out rules";
return;
}
auto &v = *out;
auto &socket = udp_sockets_[v.socket_idx];
if (!v.is_proxy()) {
auto it = udp_servers_.find(static_cast<td::uint16>(v.addr.get_port()));
CHECK(it != udp_servers_.end());
td::UdpMessage M;
M.address = dst_addr;
M.data = std::move(data);
CHECK(M.data.size() <= get_mtu());
td::actor::send_closure(it->second, &td::UdpServer::send, std::move(M));
td::actor::send_closure(socket.server, &td::UdpServer::send, std::move(M));
} else {
auto it = udp_servers_.find(out_udp_port_);
CHECK(it != udp_servers_.end());
AdnlProxy::Packet p;
p.flags = 7;
p.ip = dst_addr.get_ipv4();
p.port = static_cast<td::uint16>(dst_addr.get_port());
p.data = std::move(data);
p.adnl_start_time = Adnl::adnl_start_time();
p.seqno = ++v.out_seqno;
auto enc = v.proxy->encrypt(
AdnlProxy::Packet{dst_addr.get_ipv4(), static_cast<td::uint16>(dst_addr.get_port()), std::move(data)});
auto enc = v.proxy->encrypt(std::move(p));
td::UdpMessage M;
M.address = v.addr;
M.address = v.proxy_addr;
M.data = std::move(enc);
td::actor::send_closure(it->second, &td::UdpServer::send, std::move(M));
td::actor::send_closure(socket.server, &td::UdpServer::send, std::move(M));
}
}
void AdnlNetworkManagerImpl::proxy_register(OutDesc &desc) {
auto data = create_serialize_tl_object<ton_api::adnl_proxyControlPacketRegister>(0, 0);
AdnlProxy::Packet p;
p.flags = 6 | (1 << 17);
p.ip = 0;
p.port = 0;
p.data = std::move(data);
p.adnl_start_time = Adnl::adnl_start_time();
p.seqno = ++desc.out_seqno;
auto enc = desc.proxy->encrypt(std::move(p));
td::UdpMessage M;
M.address = desc.proxy_addr;
M.data = std::move(enc);
auto &socket = udp_sockets_[desc.socket_idx];
td::actor::send_closure(socket.server, &td::UdpServer::send, std::move(M));
}
void AdnlNetworkManagerImpl::alarm() {
alarm_timestamp() = td::Timestamp::in(60.0);
for (auto &vec : out_desc_) {
for (auto &desc : vec.second) {
if (desc.is_proxy()) {
proxy_register(desc);
}
}
}
}

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -26,6 +26,8 @@
#include "adnl-node-id.hpp"
#include "adnl-proxy-types.h"
#include <bitset>
namespace td {
class UdpServer;
}
@ -36,6 +38,8 @@ namespace adnl {
class AdnlPeerTable;
using AdnlCategoryMask = std::bitset<256>;
class AdnlNetworkConnection : public td::actor::Actor {
public:
class Callback {
@ -56,7 +60,7 @@ class AdnlNetworkManager : public td::actor::Actor {
public:
virtual ~Callback() = default;
//virtual void receive_packet(td::IPAddress addr, ConnHandle conn_handle, td::BufferSlice data) = 0;
virtual void receive_packet(td::IPAddress addr, td::BufferSlice data) = 0;
virtual void receive_packet(td::IPAddress addr, AdnlCategoryMask cat_mask, td::BufferSlice data) = 0;
};
static td::actor::ActorOwn<AdnlNetworkManager> create(td::uint16 out_port);
@ -64,14 +68,16 @@ class AdnlNetworkManager : public td::actor::Actor {
virtual void install_callback(std::unique_ptr<Callback> callback) = 0;
virtual void add_self_addr(td::IPAddress addr, td::uint32 priority) = 0;
virtual void add_proxy_addr(td::IPAddress addr, std::shared_ptr<AdnlProxy> proxy, td::uint32 priority) = 0;
virtual void add_self_addr(td::IPAddress addr, AdnlCategoryMask cat_mask, td::uint32 priority) = 0;
virtual void add_proxy_addr(td::IPAddress addr, td::uint16 local_port, std::shared_ptr<AdnlProxy> proxy,
AdnlCategoryMask cat_mask, td::uint32 priority) = 0;
virtual void send_udp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
td::uint32 priority, td::BufferSlice data) = 0;
//virtual void send_tcp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
// td::uint32 priority, td::BufferSlice data) = 0;
//virtual void send_answer_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
// ConnHandle conn_handle, td::uint32 priority, td::BufferSlice data) = 0;
virtual void set_local_id_category(AdnlNodeIdShort id, td::uint8 cat) = 0;
static constexpr td::uint32 get_mtu() {
return 1440;

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -25,6 +25,7 @@
#include "td/actor/PromiseFuture.h"
#include "adnl-network-manager.h"
#include "adnl-received-mask.h"
#include <map>
@ -41,16 +42,61 @@ class AdnlPeerTable;
class AdnlNetworkManagerImpl : public AdnlNetworkManager {
public:
struct OutDesc {
td::IPAddress addr;
td::uint16 port;
td::IPAddress proxy_addr;
std::shared_ptr<AdnlProxy> proxy;
size_t socket_idx;
td::int64 out_seqno{0};
AdnlCategoryMask cat_mask{0};
bool is_proxy() const {
return proxy != nullptr;
}
bool operator==(const OutDesc &with) const {
return addr == with.addr && is_proxy() == with.is_proxy();
if (port != with.port) {
return false;
}
if (!is_proxy()) {
return !with.is_proxy();
}
if (!with.is_proxy()) {
return false;
}
return proxy_addr == with.proxy_addr && proxy->id() == with.proxy->id();
}
};
struct InDesc {
td::uint16 port;
std::shared_ptr<AdnlProxy> proxy;
AdnlCategoryMask cat_mask;
AdnlReceivedMaskVersion received{};
OutDesc *out_desc = nullptr;
bool is_proxy() const {
return proxy != nullptr;
}
bool operator==(const InDesc &with) const {
if (port != with.port) {
return false;
}
if (!is_proxy()) {
return !with.is_proxy();
}
if (!with.is_proxy()) {
return false;
}
return proxy->id() == with.proxy->id();
}
};
struct UdpSocketDesc {
UdpSocketDesc(td::uint16 port, td::actor::ActorOwn<td::UdpServer> server) : port(port), server(std::move(server)) {
}
td::uint16 port;
td::actor::ActorOwn<td::UdpServer> server;
size_t in_desc{std::numeric_limits<size_t>::max()};
bool allow_proxy{false};
};
OutDesc *choose_out_iface(td::uint8 cat, td::uint32 priority);
AdnlNetworkManagerImpl(td::uint16 out_udp_port) : out_udp_port_(out_udp_port) {
}
@ -59,45 +105,60 @@ class AdnlNetworkManagerImpl : public AdnlNetworkManager {
callback_ = std::move(callback);
}
void add_self_addr(td::IPAddress addr, td::uint32 priority) override {
auto x = OutDesc{addr, nullptr};
auto &v = out_desc_[priority];
for (auto &y : v) {
if (x == y) {
void alarm() override;
void start_up() override {
alarm_timestamp() = td::Timestamp::in(60.0);
}
void add_in_addr(InDesc desc, size_t socket_idx) {
for (size_t idx = 0; idx < in_desc_.size(); idx++) {
if (in_desc_[idx] == desc) {
in_desc_[idx].cat_mask |= desc.cat_mask;
return;
}
}
out_desc_[priority].push_back(std::move(x));
add_listening_udp_port(static_cast<td::uint16>(addr.get_port()));
}
void add_proxy_addr(td::IPAddress addr, std::shared_ptr<AdnlProxy> proxy, td::uint32 priority) override {
auto x = OutDesc{addr, std::move(proxy)};
auto &v = out_desc_[priority];
for (auto &y : v) {
if (x == y) {
return;
}
}
out_desc_[priority].push_back(std::move(x));
if (!udp_servers_.count(out_udp_port_)) {
add_listening_udp_port(out_udp_port_);
if (desc.is_proxy()) {
udp_sockets_[socket_idx].allow_proxy = true;
proxy_addrs_[desc.proxy->id()] = in_desc_.size();
} else {
CHECK(udp_sockets_[socket_idx].in_desc == std::numeric_limits<size_t>::max());
udp_sockets_[socket_idx].in_desc = in_desc_.size();
}
in_desc_.push_back(std::move(desc));
}
void add_self_addr(td::IPAddress addr, AdnlCategoryMask cat_mask, td::uint32 priority) override;
void add_proxy_addr(td::IPAddress addr, td::uint16 local_port, std::shared_ptr<AdnlProxy> proxy,
AdnlCategoryMask cat_mask, td::uint32 priority) override;
void send_udp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr, td::uint32 priority,
td::BufferSlice data) override;
void add_listening_udp_port(td::uint16 port);
void receive_udp_message(td::UdpMessage message);
void set_local_id_category(AdnlNodeIdShort id, td::uint8 cat) override {
if (cat == 255) {
adnl_id_2_cat_.erase(id);
} else {
adnl_id_2_cat_[id] = cat;
}
}
size_t add_listening_udp_port(td::uint16 port);
void receive_udp_message(td::UdpMessage message, size_t idx);
void proxy_register(OutDesc &desc);
private:
std::unique_ptr<Callback> callback_;
std::map<td::uint32, std::vector<OutDesc>> out_desc_;
std::vector<InDesc> in_desc_;
std::map<td::Bits256, size_t> proxy_addrs_;
td::uint64 received_messages_ = 0;
td::uint64 sent_messages_ = 0;
std::map<td::uint16, td::actor::ActorOwn<td::UdpServer>> udp_servers_;
std::vector<UdpSocketDesc> udp_sockets_;
std::map<td::uint16, size_t> port_2_socket_;
std::map<AdnlNodeIdShort, td::uint8> adnl_id_2_cat_;
td::uint16 out_udp_port_;
};

View file

@ -30,6 +30,7 @@
#include "utils.hpp"
#include "adnl-query.h"
#include "adnl-ext-client.h"
#include "adnl-tunnel.h"
namespace ton {
@ -49,7 +50,7 @@ td::actor::ActorOwn<Adnl> Adnl::create(std::string db, td::actor::ActorId<keyrin
return td::actor::ActorOwn<Adnl>(td::actor::create_actor<AdnlPeerTableImpl>("PeerTable", db, keyring));
}
void AdnlPeerTableImpl::receive_packet(td::IPAddress addr, td::BufferSlice data) {
void AdnlPeerTableImpl::receive_packet(td::IPAddress addr, AdnlCategoryMask cat_mask, td::BufferSlice data) {
if (data.size() < 32) {
VLOG(ADNL_WARNING) << this << ": dropping IN message [?->?]: message too short: len=" << data.size();
return;
@ -60,14 +61,22 @@ void AdnlPeerTableImpl::receive_packet(td::IPAddress addr, td::BufferSlice data)
auto it = local_ids_.find(dst);
if (it != local_ids_.end()) {
td::actor::send_closure(it->second.first, &AdnlLocalId::receive, addr, std::move(data));
if (!cat_mask.test(it->second.cat)) {
VLOG(ADNL_WARNING) << this << ": dropping IN message [?->" << dst << "]: category mismatch";
return;
}
td::actor::send_closure(it->second.local_id, &AdnlLocalId::receive, addr, std::move(data));
return;
}
AdnlChannelIdShort dst_chan_id{dst.pubkey_hash()};
auto it2 = channels_.find(dst_chan_id);
if (it2 != channels_.end()) {
td::actor::send_closure(it2->second, &AdnlChannel::receive, addr, std::move(data));
if (!cat_mask.test(it2->second.second)) {
VLOG(ADNL_WARNING) << this << ": dropping IN message to channel [?->" << dst << "]: category mismatch";
return;
}
td::actor::send_closure(it2->second.first, &AdnlChannel::receive, addr, std::move(data));
return;
}
@ -109,7 +118,7 @@ void AdnlPeerTableImpl::receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket
<< "]: unknown dst (but how did we decrypt message?)";
return;
}
td::actor::send_closure(it->second, &AdnlPeer::receive_packet, dst, it2->second.second, it2->second.first.get(),
td::actor::send_closure(it->second, &AdnlPeer::receive_packet, dst, it2->second.mode, it2->second.local_id.get(),
std::move(packet));
}
@ -127,8 +136,8 @@ void AdnlPeerTableImpl::add_peer(AdnlNodeIdShort local_id, AdnlNodeIdFull id, Ad
}
td::actor::send_closure(it->second, &AdnlPeer::update_id, std::move(id));
if (!addr_list.empty()) {
td::actor::send_closure(it->second, &AdnlPeer::update_addr_list, local_id, it2->second.second,
it2->second.first.get(), std::move(addr_list));
td::actor::send_closure(it->second, &AdnlPeer::update_addr_list, local_id, it2->second.mode,
it2->second.local_id.get(), std::move(addr_list));
}
}
@ -152,7 +161,7 @@ void AdnlPeerTableImpl::send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst
return;
}
td::actor::send_closure(it->second, &AdnlPeer::send_one_message, src, it2->second.second, it2->second.first.get(),
td::actor::send_closure(it->second, &AdnlPeer::send_one_message, src, it2->second.mode, it2->second.local_id.get(),
OutboundAdnlMessage{std::move(message), flags});
}
@ -185,23 +194,32 @@ void AdnlPeerTableImpl::send_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, std
return;
}
td::actor::send_closure(it->second, &AdnlPeer::send_query, src, it2->second.second, it2->second.first.get(), name,
td::actor::send_closure(it->second, &AdnlPeer::send_query, src, it2->second.mode, it2->second.local_id.get(), name,
std::move(promise), timeout, std::move(data), 0);
}
void AdnlPeerTableImpl::add_id_ex(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint32 mode) {
void AdnlPeerTableImpl::add_id_ex(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint8 cat, td::uint32 mode) {
auto a = id.compute_short_id();
VLOG(ADNL_INFO) << "adnl: adding local id " << a;
auto it = local_ids_.find(a);
if (it != local_ids_.end()) {
td::actor::send_closure(it->second.first, &AdnlLocalId::update_address_list, std::move(addr_list));
if (it->second.cat != cat) {
it->second.cat = cat;
if (!network_manager_.empty()) {
td::actor::send_closure(network_manager_, &AdnlNetworkManager::set_local_id_category, a, cat);
}
}
td::actor::send_closure(it->second.local_id, &AdnlLocalId::update_address_list, std::move(addr_list));
} else {
local_ids_.emplace(
a, std::make_pair(td::actor::create_actor<AdnlLocalId>("localid", std::move(id), std::move(addr_list), mode,
actor_id(this), keyring_, dht_node_),
mode));
a, LocalIdInfo{td::actor::create_actor<AdnlLocalId>("localid", std::move(id), std::move(addr_list), mode,
actor_id(this), keyring_, dht_node_),
cat, mode});
if (!network_manager_.empty()) {
td::actor::send_closure(network_manager_, &AdnlNetworkManager::set_local_id_category, a, cat);
}
}
}
@ -215,14 +233,14 @@ void AdnlPeerTableImpl::subscribe(AdnlNodeIdShort dst, std::string prefix, std::
auto it = local_ids_.find(dst);
LOG_CHECK(it != local_ids_.end()) << "dst=" << dst;
td::actor::send_closure(it->second.first, &AdnlLocalId::subscribe, prefix, std::move(callback));
td::actor::send_closure(it->second.local_id, &AdnlLocalId::subscribe, prefix, std::move(callback));
}
void AdnlPeerTableImpl::unsubscribe(AdnlNodeIdShort dst, std::string prefix) {
auto it = local_ids_.find(dst);
if (it != local_ids_.end()) {
td::actor::send_closure(it->second.first, &AdnlLocalId::unsubscribe, prefix);
td::actor::send_closure(it->second.local_id, &AdnlLocalId::unsubscribe, prefix);
}
}
@ -233,7 +251,7 @@ void AdnlPeerTableImpl::register_dht_node(td::actor::ActorId<dht::Dht> dht_node)
td::actor::send_closure(peer.second, &AdnlPeer::update_dht_node, dht_node_);
}
for (auto &local_id : local_ids_) {
td::actor::send_closure(local_id.second.first, &AdnlLocalId::update_dht_node, dht_node_);
td::actor::send_closure(local_id.second.local_id, &AdnlLocalId::update_dht_node, dht_node_);
}
}
@ -242,8 +260,8 @@ void AdnlPeerTableImpl::register_network_manager(td::actor::ActorId<AdnlNetworkM
class Cb : public AdnlNetworkManager::Callback {
public:
void receive_packet(td::IPAddress addr, td::BufferSlice data) override {
td::actor::send_closure(id_, &AdnlPeerTableImpl::receive_packet, addr, std::move(data));
void receive_packet(td::IPAddress addr, AdnlCategoryMask cat_mask, td::BufferSlice data) override {
td::actor::send_closure(id_, &AdnlPeerTableImpl::receive_packet, addr, std::move(cat_mask), std::move(data));
}
Cb(td::actor::ActorId<AdnlPeerTableImpl> id) : id_(id) {
}
@ -254,6 +272,10 @@ void AdnlPeerTableImpl::register_network_manager(td::actor::ActorId<AdnlNetworkM
auto cb = std::make_unique<Cb>(actor_id(this));
td::actor::send_closure(network_manager_, &AdnlNetworkManager::install_callback, std::move(cb));
for (auto &id : local_ids_) {
td::actor::send_closure(network_manager_, &AdnlNetworkManager::set_local_id_category, id.first, id.second.cat);
}
}
void AdnlPeerTableImpl::get_addr_list(AdnlNodeIdShort id, td::Promise<AdnlAddressList> promise) {
@ -262,7 +284,7 @@ void AdnlPeerTableImpl::get_addr_list(AdnlNodeIdShort id, td::Promise<AdnlAddres
promise.set_error(td::Status::Error(ErrorCode::notready));
return;
}
td::actor::send_closure(it->second.first, &AdnlLocalId::get_addr_list_async, std::move(promise));
td::actor::send_closure(it->second.local_id, &AdnlLocalId::get_addr_list_async, std::move(promise));
}
void AdnlPeerTableImpl::get_self_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) {
@ -271,11 +293,14 @@ void AdnlPeerTableImpl::get_self_node(AdnlNodeIdShort id, td::Promise<AdnlNode>
promise.set_error(td::Status::Error(ErrorCode::notready));
return;
}
td::actor::send_closure(it->second.first, &AdnlLocalId::get_self_node, std::move(promise));
td::actor::send_closure(it->second.local_id, &AdnlLocalId::get_self_node, std::move(promise));
}
void AdnlPeerTableImpl::register_channel(AdnlChannelIdShort id, td::actor::ActorId<AdnlChannel> channel) {
auto success = channels_.emplace(id, channel).second;
void AdnlPeerTableImpl::register_channel(AdnlChannelIdShort id, AdnlNodeIdShort local_id,
td::actor::ActorId<AdnlChannel> channel) {
auto it = local_ids_.find(local_id);
auto cat = (it != local_ids_.end()) ? it->second.cat : 255;
auto success = channels_.emplace(id, std::make_pair(channel, cat)).second;
CHECK(success);
}
@ -317,14 +342,14 @@ AdnlPeerTableImpl::AdnlPeerTableImpl(std::string db_root, td::actor::ActorId<key
void AdnlPeerTableImpl::deliver(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) {
auto it = local_ids_.find(dst);
if (it != local_ids_.end()) {
td::actor::send_closure(it->second.first, &AdnlLocalId::deliver, src, std::move(data));
td::actor::send_closure(it->second.local_id, &AdnlLocalId::deliver, src, std::move(data));
}
}
void AdnlPeerTableImpl::deliver_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
auto it = local_ids_.find(dst);
if (it != local_ids_.end()) {
td::actor::send_closure(it->second.first, &AdnlLocalId::deliver_query, src, std::move(data), std::move(promise));
td::actor::send_closure(it->second.local_id, &AdnlLocalId::deliver_query, src, std::move(data), std::move(promise));
} else {
LOG(WARNING) << "deliver query: unknown dst " << dst;
promise.set_error(td::Status::Error(ErrorCode::notready, "cannot deliver: unknown DST"));
@ -335,7 +360,7 @@ void AdnlPeerTableImpl::decrypt_message(AdnlNodeIdShort dst, td::BufferSlice dat
td::Promise<td::BufferSlice> promise) {
auto it = local_ids_.find(dst);
if (it != local_ids_.end()) {
td::actor::send_closure(it->second.first, &AdnlLocalId::decrypt_message, std::move(data), std::move(promise));
td::actor::send_closure(it->second.local_id, &AdnlLocalId::decrypt_message, std::move(data), std::move(promise));
} else {
LOG(WARNING) << "decrypt message: unknown dst " << dst;
promise.set_error(td::Status::Error(ErrorCode::notready, "cannot decrypt: unknown DST"));
@ -347,6 +372,10 @@ void AdnlPeerTableImpl::create_ext_server(std::vector<AdnlNodeIdShort> ids, std:
promise.set_value(AdnlExtServerCreator::create(actor_id(this), std::move(ids), std::move(ports)));
}
void AdnlPeerTableImpl::create_tunnel(AdnlNodeIdShort dst, td::uint32 size,
td::Promise<std::pair<td::actor::ActorOwn<AdnlTunnel>, AdnlAddress>> promise) {
}
} // namespace adnl
} // namespace ton

View file

@ -89,11 +89,12 @@ class AdnlPeerTable : public Adnl {
virtual void answer_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlQueryId query_id, td::BufferSlice data) = 0;
virtual void receive_packet(td::IPAddress addr, td::BufferSlice data) = 0;
virtual void receive_packet(td::IPAddress addr, AdnlCategoryMask cat_mask, td::BufferSlice data) = 0;
virtual void receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket packet) = 0;
virtual void send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message, td::uint32 flags) = 0;
virtual void register_channel(AdnlChannelIdShort id, td::actor::ActorId<AdnlChannel> channel) = 0;
virtual void register_channel(AdnlChannelIdShort id, AdnlNodeIdShort local_id,
td::actor::ActorId<AdnlChannel> channel) = 0;
virtual void unregister_channel(AdnlChannelIdShort id) = 0;
virtual void add_static_node(AdnlNode node) = 0;

View file

@ -43,7 +43,7 @@ class AdnlPeerTableImpl : public AdnlPeerTable {
void add_peer(AdnlNodeIdShort local_id, AdnlNodeIdFull id, AdnlAddressList addr_list) override;
void add_static_nodes_from_config(AdnlNodesList nodes) override;
void receive_packet(td::IPAddress addr, td::BufferSlice data) override;
void receive_packet(td::IPAddress addr, AdnlCategoryMask cat_mask, td::BufferSlice data) override;
void receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket data) override;
void send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message, td::uint32 flags) override;
void send_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) override {
@ -64,7 +64,7 @@ class AdnlPeerTableImpl : public AdnlPeerTable {
td::Timestamp timeout, td::BufferSlice data, td::uint64 max_answer_size) override {
send_query(src, dst, name, std::move(promise), timeout, std::move(data));
}
void add_id_ex(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint32 mode) override;
void add_id_ex(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint8 cat, td::uint32 mode) override;
void del_id(AdnlNodeIdShort id, td::Promise<td::Unit> promise) override;
void subscribe(AdnlNodeIdShort dst, std::string prefix, std::unique_ptr<Callback> callback) override;
void unsubscribe(AdnlNodeIdShort dst, std::string prefix) override;
@ -73,7 +73,8 @@ class AdnlPeerTableImpl : public AdnlPeerTable {
void get_addr_list(AdnlNodeIdShort id, td::Promise<AdnlAddressList> promise) override;
void get_self_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) override;
void start_up() override;
void register_channel(AdnlChannelIdShort id, td::actor::ActorId<AdnlChannel> channel) override;
void register_channel(AdnlChannelIdShort id, AdnlNodeIdShort local_id,
td::actor::ActorId<AdnlChannel> channel) override;
void unregister_channel(AdnlChannelIdShort id) override;
void write_new_addr_list_to_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem node,
@ -99,12 +100,20 @@ class AdnlPeerTableImpl : public AdnlPeerTable {
void create_ext_server(std::vector<AdnlNodeIdShort> ids, std::vector<td::uint16> ports,
td::Promise<td::actor::ActorOwn<AdnlExtServer>> promise) override;
void create_tunnel(AdnlNodeIdShort dst, td::uint32 size,
td::Promise<std::pair<td::actor::ActorOwn<AdnlTunnel>, AdnlAddress>> promise) override;
struct PrintId {};
PrintId print_id() const {
return PrintId{};
}
private:
struct LocalIdInfo {
td::actor::ActorOwn<AdnlLocalId> local_id;
td::uint8 cat;
td::uint32 mode;
};
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<AdnlNetworkManager> network_manager_;
@ -114,13 +123,14 @@ class AdnlPeerTableImpl : public AdnlPeerTable {
void deliver_one_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message);
std::map<AdnlNodeIdShort, td::actor::ActorOwn<AdnlPeer>> peers_;
std::map<AdnlNodeIdShort, std::pair<td::actor::ActorOwn<AdnlLocalId>, td::uint32>> local_ids_;
std::map<AdnlChannelIdShort, td::actor::ActorId<AdnlChannel>> channels_;
std::map<AdnlNodeIdShort, LocalIdInfo> local_ids_;
std::map<AdnlChannelIdShort, std::pair<td::actor::ActorId<AdnlChannel>, td::uint8>> channels_;
td::actor::ActorOwn<AdnlDb> db_;
td::actor::ActorOwn<AdnlExtServer> ext_server_;
AdnlNodeIdShort proxy_addr_;
//std::map<td::uint64, td::actor::ActorId<AdnlQuery>> out_queries_;
//td::uint64 last_query_id_ = 1;
};

View file

@ -482,7 +482,8 @@ void AdnlPeerPairImpl::create_channel(pubkeys::Ed25519 pub, td::uint32 date) {
channel_ = R.move_as_ok();
channel_inited_ = true;
td::actor::send_closure_later(peer_table_, &AdnlPeerTable::register_channel, channel_in_id_, channel_.get());
td::actor::send_closure_later(peer_table_, &AdnlPeerTable::register_channel, channel_in_id_, local_id_,
channel_.get());
} else {
VLOG(ADNL_WARNING) << this << ": failed to create channel: " << R.move_as_error();
}

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/actor/actor.h"
#include "td/utils/buffer.h"
@ -170,7 +170,10 @@ int main(int argc, char *argv[]) {
network_manager = ton::adnl::AdnlNetworkManager::create(static_cast<td::uint16>(addr.get_port()));
td::actor::send_closure(network_manager, &ton::adnl::AdnlNetworkManager::add_self_addr, addr, 0);
ton::adnl::AdnlCategoryMask cat_mask;
cat_mask[0] = true;
td::actor::send_closure(network_manager, &ton::adnl::AdnlNetworkManager::add_self_addr, addr, std::move(cat_mask),
0);
auto tladdr = ton::create_tl_object<ton::ton_api::adnl_address_udp>(addr.get_ipv4(), addr.get_port());
auto addr_vec = std::vector<ton::tl_object_ptr<ton::ton_api::adnl_Address>>();
@ -179,7 +182,8 @@ int main(int argc, char *argv[]) {
std::move(addr_vec), ton::adnl::Adnl::adnl_start_time(), ton::adnl::Adnl::adnl_start_time(), 0, 2000000000);
auto addrlist = ton::adnl::AdnlAddressList::create(tladdrlist).move_as_ok();
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, std::move(addrlist));
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, std::move(addrlist),
static_cast<td::uint8>(0));
td::actor::send_closure(adnl, &ton::adnl::Adnl::subscribe, ton::adnl::AdnlNodeIdShort{pub.compute_short_id()},
ton::adnl::Adnl::int_to_bytestring(ton::ton_api::adnl_ping::ID),
std::make_unique<ton::adnl::Callback>());

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-proxy-types.hpp"
#include "tl-utils/tl-utils.hpp"
@ -27,59 +27,84 @@ namespace ton {
namespace adnl {
td::Result<AdnlProxy::Packet> AdnlProxyNone::decrypt(td::BufferSlice packet) const {
if (packet.size() < 32) {
return td::Status::Error(ErrorCode::protoviolation, "bad signature");
}
if (packet.as_slice().truncate(32) != id_.as_slice()) {
return td::Status::Error(ErrorCode::protoviolation, "bad proxy id");
}
Packet p{};
p.flags = 0;
p.ip = 0;
p.port = 0;
p.adnl_start_time = 0;
p.seqno = 0;
p.date = 0;
p.data = std::move(packet);
p.data.confirm_read(32);
return std::move(p);
}
td::BufferSlice AdnlProxyFast::encrypt(Packet packet) const {
auto date = static_cast<td::uint32>(td::Clocks::system());
auto signature = create_hash_tl_object<ton_api::adnl_proxyToFastHash>(
packet.ip, packet.port, date, sha256_bits256(packet.data.as_slice()), shared_secret_);
auto obj = create_serialize_tl_object<ton_api::adnl_proxyToFast>(packet.ip, packet.port, date, signature);
td::BufferSlice res{32 + obj.size() + packet.data.size()};
auto S = res.as_slice();
S.copy_from(td::Bits256::zero().as_slice());
if (!packet.date) {
packet.date = static_cast<td::int32>(td::Clocks::system());
packet.flags |= 8;
}
auto obj = create_tl_object<ton_api::adnl_proxyPacketHeader>(id_, packet.flags, packet.ip, packet.port,
packet.adnl_start_time, packet.seqno, packet.date,
td::sha256_bits256(packet.data.as_slice()));
char data[64];
td::MutableSlice S{data, 64};
S.copy_from(get_tl_object_sha256(obj).as_slice());
S.remove_prefix(32);
S.copy_from(obj.as_slice());
S.remove_prefix(obj.size());
S.copy_from(packet.data.as_slice());
S.copy_from(shared_secret_.as_slice());
return res;
obj->signature_ = td::sha256_bits256(td::Slice(data, 64));
return serialize_tl_object(obj, false, std::move(packet.data));
}
td::Result<AdnlProxy::Packet> AdnlProxyFast::decrypt(td::BufferSlice packet) const {
if (packet.size() < 36) {
return td::Status::Error(ErrorCode::protoviolation, "too short packet");
TRY_RESULT(obj, fetch_tl_prefix<ton_api::adnl_proxyPacketHeader>(packet, false));
if (obj->proxy_id_ != id_) {
return td::Status::Error(ErrorCode::protoviolation, "bad proxy id");
}
td::Bits256 v;
v.as_slice().copy_from(packet.as_slice().truncate(32));
if (!v.is_zero()) {
return td::Status::Error(ErrorCode::protoviolation, "non-zero DST");
}
packet.confirm_read(32);
auto signature = std::move(obj->signature_);
obj->signature_ = td::sha256_bits256(packet.as_slice());
TRY_RESULT(R, fetch_tl_prefix<ton_api::adnl_proxyToFast>(packet, true));
char data[64];
td::MutableSlice S{data, 64};
S.copy_from(get_tl_object_sha256(obj).as_slice());
S.remove_prefix(32);
S.copy_from(shared_secret_.as_slice());
if (R->date_ < td::Clocks::system() - 8) {
return td::Status::Error(ErrorCode::protoviolation, "too old date");
}
auto signature = create_hash_tl_object<ton_api::adnl_proxyToFastHash>(
R->ip_, R->port_, R->date_, sha256_bits256(packet.as_slice()), shared_secret_);
if (signature != R->signature_) {
if (td::sha256_bits256(td::Slice(data, 64)) != signature) {
return td::Status::Error(ErrorCode::protoviolation, "bad signature");
}
return Packet{static_cast<td::uint32>(R->ip_), static_cast<td::uint16>(R->port_), std::move(packet)};
Packet p;
p.flags = obj->flags_;
p.ip = (p.flags & 1) ? obj->ip_ : 0;
p.port = (p.flags & 1) ? static_cast<td::uint16>(obj->port_) : 0;
p.adnl_start_time = (p.flags & 2) ? obj->adnl_start_time_ : 0;
p.seqno = (p.flags & 4) ? obj->seqno_ : 0;
p.date = (p.flags & 8) ? obj->date_ : 0;
p.data = std::move(packet);
return std::move(p);
}
td::Result<std::shared_ptr<AdnlProxy>> AdnlProxy::create(const ton_api::adnl_Proxy &proxy_type) {
std::shared_ptr<AdnlProxy> R;
ton_api::downcast_call(
const_cast<ton_api::adnl_Proxy &>(proxy_type),
td::overloaded([&](const ton_api::adnl_proxy_none &x) { R = std::make_shared<AdnlProxyNone>(); },
td::overloaded([&](const ton_api::adnl_proxy_none &x) { R = std::make_shared<AdnlProxyNone>(x.id_); },
[&](const ton_api::adnl_proxy_fast &x) {
R = std::make_shared<AdnlProxyFast>(x.shared_secret_.as_slice());
R = std::make_shared<AdnlProxyFast>(x.id_, x.shared_secret_.as_slice());
}));
return R;
return std::move(R);
}
} // namespace adnl

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -28,14 +28,19 @@ namespace adnl {
class AdnlProxy {
public:
struct Packet {
td::uint32 flags;
td::uint32 ip;
td::uint16 port;
td::int32 adnl_start_time;
td::int64 seqno;
td::int32 date{0};
td::BufferSlice data;
};
virtual ~AdnlProxy() = default;
virtual td::BufferSlice encrypt(Packet packet) const = 0;
virtual td::Result<Packet> decrypt(td::BufferSlice packet) const = 0;
virtual tl_object_ptr<ton_api::adnl_Proxy> tl() const = 0;
virtual const td::Bits256 &id() const = 0;
static td::Result<std::shared_ptr<AdnlProxy>> create(const ton_api::adnl_Proxy &proxy_type);
};

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -28,31 +28,42 @@ namespace adnl {
class AdnlProxyNone : public AdnlProxy {
public:
AdnlProxyNone() {
AdnlProxyNone(td::Bits256 id) : id_(id) {
}
td::BufferSlice encrypt(Packet packet) const override {
return std::move(packet.data);
}
td::Result<Packet> decrypt(td::BufferSlice packet) const override {
return Packet{0, 0, std::move(packet)};
td::BufferSlice d{packet.data.size() + 32};
d.as_slice().copy_from(id_.as_slice());
d.as_slice().remove_prefix(32).copy_from(packet.data.as_slice());
return d;
}
td::Result<Packet> decrypt(td::BufferSlice packet) const override;
tl_object_ptr<ton_api::adnl_Proxy> tl() const override {
return create_tl_object<ton_api::adnl_proxy_none>();
return create_tl_object<ton_api::adnl_proxy_none>(id_);
}
const td::Bits256 &id() const override {
return id_;
}
private:
td::Bits256 id_;
};
class AdnlProxyFast : public AdnlProxy {
public:
AdnlProxyFast(td::Slice shared_secret)
: shared_secret_(sha256_bits256(shared_secret)), shared_secret_raw_(shared_secret) {
AdnlProxyFast(td::Bits256 id, td::Slice shared_secret)
: id_(id), shared_secret_(sha256_bits256(shared_secret)), shared_secret_raw_(shared_secret) {
}
td::BufferSlice encrypt(Packet packet) const override;
td::Result<Packet> decrypt(td::BufferSlice packet) const override;
tl_object_ptr<ton_api::adnl_Proxy> tl() const override {
return create_tl_object<ton_api::adnl_proxy_fast>(shared_secret_raw_.clone_as_buffer_slice());
return create_tl_object<ton_api::adnl_proxy_fast>(id_, shared_secret_raw_.clone_as_buffer_slice());
}
const td::Bits256 &id() const override {
return id_;
}
private:
td::Bits256 id_;
td::Bits256 shared_secret_;
td::SharedSlice shared_secret_raw_;
};

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/actor/actor.h"
#include "td/utils/buffer.h"
@ -35,11 +35,13 @@
#include "td/utils/port/path.h"
#include "td/utils/port/user.h"
#include "td/utils/filesystem.h"
#include "td/utils/overloaded.h"
#include "common/checksum.h"
#include "common/errorcode.h"
#include "tl-utils/tl-utils.hpp"
#include "auto/tl/ton_api_json.h"
#include "adnl-proxy-types.h"
#include "adnl-received-mask.h"
#include <map>
#if TD_DARWIN || TD_LINUX
@ -50,12 +52,19 @@ namespace ton {
namespace adnl {
namespace {
td::int32 start_time() {
static td::int32 t = static_cast<td::int32>(td::Clocks::system());
return t;
}
} // namespace
class Receiver : public td::actor::Actor {
public:
void start_up() override;
void receive_common(td::BufferSlice data);
void receive_from_client(td::BufferSlice data);
void receive_to_client(td::BufferSlice data);
void receive_common(td::IPAddress addr, td::BufferSlice data);
void receive_from_client(td::IPAddress addr, td::BufferSlice data);
void receive_to_client(td::IPAddress addr, td::BufferSlice data);
Receiver(td::uint16 in_port, td::uint16 out_port, std::shared_ptr<AdnlProxy> proxy, td::IPAddress client_addr)
: in_port_(in_port), out_port_(out_port), proxy_(std::move(proxy)), addr_(client_addr) {
@ -68,6 +77,10 @@ class Receiver : public td::actor::Actor {
td::IPAddress addr_;
td::actor::ActorOwn<td::UdpServer> out_udp_server_;
td::actor::ActorOwn<td::UdpServer> in_udp_server_;
td::int32 client_start_time_{0};
td::uint64 out_seqno_{0};
AdnlReceivedMaskVersion received_;
};
void Receiver::start_up() {
@ -81,15 +94,18 @@ void Receiver::start_up() {
const td::uint32 mode_;
void on_udp_message(td::UdpMessage udp_message) override {
if (udp_message.error.is_error()) {
LOG(DEBUG) << udp_message.error;
LOG(INFO) << "receifed udp message with error: " << udp_message.error;
return;
}
if (mode_ == 0) {
td::actor::send_closure_later(manager_, &Receiver::receive_common, std::move(udp_message.data));
td::actor::send_closure_later(manager_, &Receiver::receive_common, udp_message.address,
std::move(udp_message.data));
} else if (mode_ == 1) {
td::actor::send_closure_later(manager_, &Receiver::receive_from_client, std::move(udp_message.data));
td::actor::send_closure_later(manager_, &Receiver::receive_from_client, udp_message.address,
std::move(udp_message.data));
} else {
td::actor::send_closure_later(manager_, &Receiver::receive_to_client, std::move(udp_message.data));
td::actor::send_closure_later(manager_, &Receiver::receive_to_client, udp_message.address,
std::move(udp_message.data));
}
}
};
@ -108,46 +124,152 @@ void Receiver::start_up() {
}
}
void Receiver::receive_common(td::BufferSlice data) {
void Receiver::receive_common(td::IPAddress addr, td::BufferSlice data) {
if (data.size() <= 32) {
LOG(INFO) << "dropping too short packet: size=" << data.size();
return;
}
td::Bits256 id;
id.as_slice().copy_from(data.as_slice().truncate(32));
if (id.is_zero()) {
receive_from_client(std::move(data));
if (proxy_->id().as_slice() == data.as_slice().truncate(32)) {
receive_from_client(addr, std::move(data));
} else {
receive_to_client(std::move(data));
receive_to_client(addr, std::move(data));
}
}
void Receiver::receive_from_client(td::BufferSlice data) {
void Receiver::receive_from_client(td::IPAddress addr, td::BufferSlice data) {
auto F = proxy_->decrypt(std::move(data));
if (F.is_error()) {
LOG(INFO) << "proxy: failed to decrypt message from client: " << F.move_as_error();
return;
}
auto f = F.move_as_ok();
if (f.flags & (1 << 16)) {
LOG(INFO) << "proxy: dropping message from client: flag 16 is set";
return;
}
if (f.date) {
if (f.date + 60.0 < td::Clocks::system() || f.date - 60.0 > td::Clocks::system()) {
LOG(INFO) << "proxy: dropping message from client: date mismatch";
return;
}
}
if ((f.flags & 6) == 6) {
if (received_.packet_is_delivered(f.adnl_start_time, f.seqno)) {
LOG(INFO) << "proxy: dropping message from client: duplicate packet (or old seqno/start_time)";
return;
}
received_.deliver_packet(f.adnl_start_time, f.seqno);
}
if (f.flags & (1 << 17)) {
auto F = fetch_tl_object<ton_api::adnl_ProxyControlPacket>(std::move(f.data), true);
if (F.is_error()) {
LOG(INFO) << this << ": dropping proxy packet: bad control packet: " << F.move_as_error();
return;
}
ton_api::downcast_call(*F.move_as_ok().get(),
td::overloaded(
[&](const ton_api::adnl_proxyControlPacketPing &f) {
auto data = create_serialize_tl_object<ton_api::adnl_proxyControlPacketPong>(f.id_);
AdnlProxy::Packet p;
p.flags = 6 | (1 << 16) | (1 << 17);
if (addr.is_valid() && addr.is_ipv4()) {
p.flags |= 1;
p.ip = addr.get_ipv4();
p.port = static_cast<td::uint16>(addr.get_port());
} else {
p.ip = 0;
p.port = 0;
}
p.data = std::move(data);
p.adnl_start_time = start_time();
p.seqno = out_seqno_;
p.data = std::move(data);
auto enc = proxy_->encrypt(std::move(p));
td::UdpMessage M;
M.address = addr;
M.data = std::move(enc);
td::actor::send_closure(
in_udp_server_.empty() ? out_udp_server_.get() : in_udp_server_.get(),
&td::UdpServer::send, std::move(M));
},
[&](const ton_api::adnl_proxyControlPacketPong &f) {},
[&](const ton_api::adnl_proxyControlPacketRegister &f) {
if (f.ip_ == 0 && f.port_ == 0) {
if (addr.is_valid() && addr.is_ipv4()) {
addr_ = addr;
}
} else {
td::IPAddress a;
auto S = a.init_host_port(td::IPAddress::ipv4_to_str(f.ip_), f.port_);
if (S.is_ok()) {
addr_ = a;
} else {
LOG(INFO) << "failed to init remote addr: " << S.move_as_error();
}
}
}));
return;
}
if (!(f.flags & 1)) {
LOG(INFO) << this << ": dropping proxy packet: no destination";
return;
}
td::IPAddress a;
if (a.init_ipv4_port(td::IPAddress::ipv4_to_str(f.ip), f.port).is_error()) {
LOG(INFO) << this << ": dropping proxy packet: invalid destination";
return;
}
if (!a.is_valid()) {
LOG(INFO) << this << ": dropping proxy packet: invalid destination";
return;
}
td::UdpMessage M;
M.address = a;
M.data = std::move(f.data);
LOG(DEBUG) << this << ": proxying DOWN packet of length " << M.data.size() << " to " << a;
td::actor::send_closure(out_udp_server_.empty() ? in_udp_server_.get() : out_udp_server_.get(), &td::UdpServer::send,
std::move(M));
}
void Receiver::receive_to_client(td::BufferSlice data) {
void Receiver::receive_to_client(td::IPAddress addr, td::BufferSlice data) {
LOG(DEBUG) << "proxying to " << addr_;
if (!addr_.is_valid() || !addr_.is_ipv4() || !addr_.get_ipv4()) {
LOG(INFO) << this << ": dropping external packet: client not inited";
return;
}
AdnlProxy::Packet p;
p.flags = (1 << 16);
if (addr.is_valid() && addr.is_ipv4()) {
p.flags |= 1;
p.ip = addr.get_ipv4();
p.port = static_cast<td::uint16>(addr.get_port());
} else {
p.ip = 0;
p.port = 0;
}
p.flags |= 2;
p.adnl_start_time = start_time();
p.flags |= 4;
p.seqno = ++out_seqno_;
p.data = std::move(data);
LOG(DEBUG) << this << ": proxying UP packet of length " << p.data.size() << " to " << addr_;
td::UdpMessage M;
M.address = addr_;
M.data = std::move(data);
M.data = proxy_->encrypt(std::move(p));
td::actor::send_closure(in_udp_server_.empty() ? out_udp_server_.get() : in_udp_server_.get(), &td::UdpServer::send,
std::move(M));
@ -204,7 +326,7 @@ int main(int argc, char *argv[]) {
});
p.add_option('l', "logname", "log to file", [&](td::Slice fname) {
auto F = std::make_unique<td::FileLog>();
TRY_STATUS(F->init(fname.str(), std::numeric_limits<td::uint64>::max(), true));
TRY_STATUS(F->init(fname.str(), std::numeric_limits<td::int64>::max(), true));
logger_ = std::move(F);
td::log_interface = logger_.get();
return td::Status::OK();
@ -248,7 +370,9 @@ int main(int argc, char *argv[]) {
}
TRY_RESULT(proxy, ton::adnl::AdnlProxy::create(*y->proxy_type_.get()));
td::IPAddress a;
a.init_ipv4_port(td::IPAddress::ipv4_to_str(y->dst_ip_), static_cast<td::uint16>(y->dst_port_)).ensure();
if (y->dst_ip_ || y->dst_port_) {
a.init_ipv4_port(td::IPAddress::ipv4_to_str(y->dst_ip_), static_cast<td::uint16>(y->dst_port_)).ensure();
}
scheduler.run_in_context([&] {
x.push_back(td::actor::create_actor<ton::adnl::Receiver>("adnl-proxy", in_port, out_port, std::move(proxy), a));

84
adnl/adnl-received-mask.h Normal file
View file

@ -0,0 +1,84 @@
#pragma once
#include "td/utils/int_types.h"
#include "td/utils/logging.h"
namespace ton {
namespace adnl {
class AdnlReceivedMask {
public:
void reset() {
seqno_ = 0;
mask_ = 0;
}
bool packet_is_delivered(td::int64 seqno) const {
if (seqno <= 0) {
return false;
}
if (seqno + 64 <= seqno_) {
return true;
}
if (seqno > seqno_) {
return false;
}
return mask_ & (1ull << (seqno_ - seqno));
}
void deliver_packet(td::int64 seqno) {
CHECK(!packet_is_delivered(seqno));
CHECK(seqno > 0);
if (seqno <= seqno_) {
mask_ |= (1ull << (seqno_ - seqno));
} else {
auto old = seqno_;
seqno_ = seqno;
if (seqno_ - old >= 64) {
mask_ = 1;
} else {
mask_ = mask_ << (seqno_ - old);
mask_ |= 1;
}
}
}
private:
td::int64 seqno_{0};
td::uint64 mask_{0};
};
class AdnlReceivedMaskVersion {
public:
bool packet_is_delivered(td::int32 utime, td::uint64 seqno) {
if (utime < utime_) {
return true;
} else if (utime == utime_) {
return mask_.packet_is_delivered(seqno);
} else {
return false;
}
}
void deliver_packet(td::int32 utime, td::uint64 seqno) {
CHECK(utime >= utime_);
if (utime == utime_) {
mask_.deliver_packet(seqno);
} else {
utime_ = utime;
mask_.reset();
mask_.deliver_packet(seqno);
}
}
void reset() {
mask_.reset();
utime_ = 0;
}
private:
AdnlReceivedMask mask_;
td::int32 utime_{0};
};
} // namespace adnl
} // namespace ton

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -34,9 +34,10 @@ class TestLoopbackNetworkManager : public ton::adnl::AdnlNetworkManager {
callback_ = std::move(callback);
}
void add_self_addr(td::IPAddress addr, td::uint32 priority) override {
void add_self_addr(td::IPAddress addr, AdnlCategoryMask cat_mask, td::uint32 priority) override {
}
void add_proxy_addr(td::IPAddress addr, std::shared_ptr<AdnlProxy> proxy, td::uint32 priority) override {
void add_proxy_addr(td::IPAddress addr, td::uint16 local_port, std::shared_ptr<AdnlProxy> proxy,
AdnlCategoryMask cat_mask, td::uint32 priority) override {
}
void send_udp_packet(ton::adnl::AdnlNodeIdShort src_id, ton::adnl::AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
td::uint32 priority, td::BufferSlice data) override {
@ -48,7 +49,9 @@ class TestLoopbackNetworkManager : public ton::adnl::AdnlNetworkManager {
return;
}
CHECK(callback_);
callback_->receive_packet(dst_addr, std::move(data));
AdnlCategoryMask m;
m[0] = true;
callback_->receive_packet(dst_addr, std::move(m), std::move(data));
}
void add_node_id(AdnlNodeIdShort id, bool allow_send, bool allow_receive) {
@ -68,6 +71,8 @@ class TestLoopbackNetworkManager : public ton::adnl::AdnlNetworkManager {
CHECK(p >= 0 && p <= 1);
loss_probability_ = p;
}
void set_local_id_category(AdnlNodeIdShort id, td::uint8 cat) override {
}
TestLoopbackNetworkManager() {
}

97
adnl/adnl-tunnel.cpp Normal file
View file

@ -0,0 +1,97 @@
#include "adnl-tunnel.h"
#include "adnl-peer-table.h"
namespace ton {
namespace adnl {
void AdnlInboundTunnelEndpoint::receive_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice datagram) {
receive_packet_cont(src, src_addr, std::move(datagram), 0);
}
void AdnlInboundTunnelEndpoint::receive_packet_cont(AdnlNodeIdShort src, td::IPAddress src_addr,
td::BufferSlice datagram, size_t idx) {
if (datagram.size() <= 32) {
VLOG(ADNL_INFO) << "dropping too short datagram";
return;
}
if (datagram.as_slice().truncate(32) != decrypt_via_[idx].as_slice()) {
VLOG(ADNL_INFO) << "invalid tunnel midpoint";
return;
}
datagram.confirm_read(32);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), src, src_addr, idx](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
VLOG(ADNL_INFO) << "dropping tunnel packet: failed to decrypt: " << R.move_as_error();
return;
} else {
td::actor::send_closure(SelfId, &AdnlInboundTunnelEndpoint::decrypted_packet, src, src_addr, R.move_as_ok(), idx);
}
});
td::actor::send_closure(keyring_, &keyring::Keyring::decrypt_message, decrypt_via_[idx], std::move(datagram),
std::move(P));
}
void AdnlInboundTunnelEndpoint::decrypted_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice data,
size_t idx) {
if (idx == decrypt_via_.size() - 1) {
td::actor::send_closure(adnl_, &AdnlPeerTable::receive_packet, src_addr, std::move(data));
return;
}
auto F = fetch_tl_object<ton_api::adnl_tunnelPacketContents>(std::move(data), true);
if (F.is_error()) {
VLOG(ADNL_INFO) << "dropping tunnel packet: failed to fetch: " << F.move_as_error();
return;
}
auto packet = F.move_as_ok();
td::IPAddress addr;
if (packet->flags_ & 1) {
addr.init_host_port(td::IPAddress::ipv4_to_str(packet->from_ip_), packet->from_port_).ignore();
}
if (packet->flags_ & 2) {
receive_packet_cont(src, addr, std::move(packet->message_), idx + 1);
}
}
void AdnlInboundTunnelMidpoint::start_up() {
encrypt_key_hash_ = encrypt_via_.compute_short_id();
auto R = encrypt_via_.create_encryptor();
if (R.is_error()) {
return;
}
encryptor_ = R.move_as_ok();
}
void AdnlInboundTunnelMidpoint::receive_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice datagram) {
if (!encryptor_) {
return;
}
auto obj = create_tl_object<ton_api::adnl_tunnelPacketContents>();
obj->flags_ = 2;
obj->message_ = std::move(datagram);
if (src_addr.is_valid() && src_addr.is_ipv4()) {
obj->flags_ |= 1;
obj->from_ip_ = src_addr.get_ipv4();
obj->from_port_ = src_addr.get_port();
}
auto packet = serialize_tl_object(std::move(obj), true);
auto dataR = encryptor_->encrypt(packet.as_slice());
if (dataR.is_error()) {
return;
}
auto data = dataR.move_as_ok();
td::BufferSlice enc{data.size() + 32};
auto S = enc.as_slice();
S.copy_from(encrypt_key_hash_.as_slice());
S.remove_prefix(32);
S.copy_from(data.as_slice());
td::actor::send_closure(adnl_, &Adnl::send_message_ex, proxy_as_, proxy_to_, std::move(enc),
Adnl::SendFlags::direct_only);
}
} // namespace adnl
} // namespace ton

73
adnl/adnl-tunnel.h Normal file
View file

@ -0,0 +1,73 @@
#pragma once
#include "adnl.h"
#include "adnl-peer-table.h"
#include "keys/encryptor.h"
#include <map>
namespace ton {
namespace adnl {
class AdnlInboundTunnelPoint : public AdnlTunnel {
public:
virtual ~AdnlInboundTunnelPoint() = default;
virtual void receive_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice datagram) = 0;
};
class AdnlInboundTunnelEndpoint : public AdnlInboundTunnelPoint {
public:
AdnlInboundTunnelEndpoint(PublicKeyHash pubkey_hash, std::vector<PublicKeyHash> decrypt_via, AdnlNodeIdShort proxy_to,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<AdnlPeerTable> adnl)
: pubkey_hash_(std::move(pubkey_hash))
, decrypt_via_(std::move(decrypt_via))
, proxy_to_(std::move(proxy_to))
, keyring_(std::move(keyring))
, adnl_(std::move(adnl)) {
}
void receive_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice datagram) override;
void receive_packet_cont(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice datagram, size_t idx);
void decrypted_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice data, size_t idx);
private:
PublicKeyHash pubkey_hash_;
std::vector<PublicKeyHash> decrypt_via_;
AdnlNodeIdShort proxy_to_;
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<AdnlPeerTable> adnl_;
};
class AdnlInboundTunnelMidpoint : public AdnlInboundTunnelPoint {
public:
AdnlInboundTunnelMidpoint(ton::PublicKey encrypt_via, AdnlNodeIdShort proxy_to, AdnlNodeIdShort proxy_as,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<AdnlPeerTable> adnl)
: encrypt_via_(std::move(encrypt_via)), proxy_to_(proxy_to), proxy_as_(proxy_as), keyring_(keyring), adnl_(adnl) {
}
void start_up() override;
void receive_packet(AdnlNodeIdShort src, td::IPAddress src_addr, td::BufferSlice datagram) override;
private:
ton::PublicKeyHash encrypt_key_hash_;
ton::PublicKey encrypt_via_;
std::unique_ptr<Encryptor> encryptor_;
AdnlNodeIdShort proxy_to_;
AdnlNodeIdShort proxy_as_;
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<AdnlPeerTable> adnl_;
};
class AdnlProxyNode : public td::actor::Actor {
public:
void receive_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data);
void receive_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise);
private:
std::map<PublicKeyHash, td::actor::ActorOwn<AdnlInboundTunnelMidpoint>> mid_;
};
} // namespace adnl
} // namespace ton

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -58,6 +58,8 @@ class AdnlSenderInterface : public td::actor::Actor {
td::uint64 max_answer_size) = 0;
};
class AdnlTunnel : public td::actor::Actor {};
class Adnl : public AdnlSenderInterface {
public:
class Callback {
@ -88,10 +90,10 @@ class Adnl : public AdnlSenderInterface {
virtual void add_static_nodes_from_config(AdnlNodesList nodes) = 0;
// adds local id. After that you can send/receive messages from/to this id
void add_id(AdnlNodeIdFull id, AdnlAddressList addr_list) {
add_id_ex(std::move(id), std::move(addr_list), 0);
void add_id(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint8 cat) {
add_id_ex(std::move(id), std::move(addr_list), cat, 0);
}
virtual void add_id_ex(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint32 mode) = 0;
virtual void add_id_ex(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint8 cat, td::uint32 mode) = 0;
virtual void del_id(AdnlNodeIdShort id, td::Promise<td::Unit> promise) = 0;
// subscribe to (some) messages(+queries) to this local id
@ -113,6 +115,8 @@ class Adnl : public AdnlSenderInterface {
virtual void create_ext_server(std::vector<AdnlNodeIdShort> ids, std::vector<td::uint16> ports,
td::Promise<td::actor::ActorOwn<AdnlExtServer>> promise) = 0;
virtual void create_tunnel(AdnlNodeIdShort dst, td::uint32 size,
td::Promise<std::pair<td::actor::ActorOwn<AdnlTunnel>, AdnlAddress>> promise) = 0;
static td::actor::ActorOwn<Adnl> create(std::string db, td::actor::ActorId<keyring::Keyring> keyring);

View file

@ -391,7 +391,7 @@ endif()
add_executable(create-state block/create-state.cpp)
target_include_directories(create-state PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block)
target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block tonlib)
if (WINGETOPT_FOUND)
target_link_libraries_system(create-state wingetopt)
endif()

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#include "crypto/Ed25519.h"
@ -310,6 +310,10 @@ Result<SecureString> Ed25519::compute_shared_secret(const PublicKey &public_key,
return std::move(result);
}
int Ed25519::version() {
return OPENSSL_VERSION_NUMBER;
}
#else
Result<Ed25519::PrivateKey> Ed25519::generate_private_key() {
@ -387,6 +391,10 @@ Result<SecureString> Ed25519::compute_shared_secret(const PublicKey &public_key,
return std::move(shared_secret);
}
int Ed25519::version() {
return 0;
}
#endif
} // namespace td

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -65,6 +65,8 @@ class Ed25519 {
static Result<PrivateKey> generate_private_key();
static Result<SecureString> compute_shared_secret(const PublicKey &public_key, const PrivateKey &private_key);
static int version();
};
} // namespace td

View file

@ -1633,15 +1633,19 @@ bool valid_config_data(Ref<vm::Cell> cell, const td::BitArray<256>& addr, bool c
bool config_params_present(vm::Dictionary& dict, Ref<vm::Cell> param_dict_root) {
auto res = block::Config::unpack_param_dict(std::move(param_dict_root));
if (res.is_error()) {
LOG(ERROR)
<< "invalid mandatory parameters dictionary while checking existence of all mandatory configuration parameters";
return false;
}
for (int x : res.move_as_ok()) {
// LOG(DEBUG) << "checking whether mandatory configuration parameter #" << x << " exists";
if (!dict.int_key_exists(x)) {
LOG(ERROR) << "configuration parameter #" << x
<< " (declared as mandatory in configuration parameter #9) is missing";
return false;
}
}
// LOG(DEBUG) << "all mandatory configuration parameters present";
return true;
}

View file

@ -414,7 +414,7 @@ block_info#9bc7a987 version:uint32
after_split:(## 1)
want_split:Bool want_merge:Bool
key_block:Bool vert_seqno_incr:(## 1)
flags:(## 8)
flags:(## 8) { flags <= 1 }
seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr }
{ prev_seq_no:# } { ~prev_seq_no + 1 = seq_no }
shard:ShardIdent gen_utime:uint32
@ -423,6 +423,7 @@ block_info#9bc7a987 version:uint32
gen_catchain_seqno:uint32
min_ref_mc_seqno:uint32
prev_key_block_seqno:uint32
gen_software:flags . 0?GlobalVersion
master_ref:not_master?^BlkMasterInfo
prev_ref:^(BlkPrevInfo after_merge)
prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0)

View file

@ -55,6 +55,8 @@
#include "td/utils/port/path.h"
#include "td/utils/port/signals.h"
#include "tonlib/keys/Mnemonic.h"
#include "block.h"
#include "block-parse.h"
#include "block-auto.h"
@ -634,7 +636,25 @@ void interpret_sub_extra_currencies(vm::Stack& stack) {
stack.push_bool(ok);
}
void interpret_mnemonic_to_privkey(vm::Stack& stack, int mode) {
td::SecureString str{td::Slice{stack.pop_string()}};
auto res = tonlib::Mnemonic::create(std::move(str), td::SecureString());
if (res.is_error()) {
throw fift::IntError{res.move_as_error().to_string()};
}
auto privkey = res.move_as_ok().to_private_key();
td::SecureString key;
if (mode & 1) {
auto pub = privkey.get_public_key();
key = pub.move_as_ok().as_octet_string();
} else {
key = privkey.as_octet_string();
}
stack.push_bytes(key.as_slice());
}
void init_words_custom(fift::Dictionary& d) {
using namespace std::placeholders;
d.def_stack_word("verb@ ", interpret_get_verbosity);
d.def_stack_word("verb! ", interpret_set_verbosity);
d.def_stack_word("wcid@ ", interpret_get_workchain);
@ -651,6 +671,8 @@ void init_words_custom(fift::Dictionary& d) {
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
d.def_stack_word("CC+? ", interpret_add_extra_currencies);
d.def_stack_word("CC-? ", interpret_sub_extra_currencies);
d.def_stack_word("mnemo>priv ", std::bind(interpret_mnemonic_to_privkey, _1, 0));
d.def_stack_word("mnemo>pub ", std::bind(interpret_mnemonic_to_privkey, _1, 1));
}
tlb::TypenameLookup tlb_dict;
@ -738,7 +760,8 @@ void init_words_tlb(fift::Dictionary& d) {
d.def_stack_word("(tlb-dump-str?) ", interpret_tlb_dump_to_str);
d.def_stack_word("tlb-skip ", interpret_tlb_skip);
d.def_stack_word("tlb-validate-skip ", interpret_tlb_validate_skip);
d.def_stack_word("ExtraCurrencyCollection", std::bind(interpret_tlb_type_const, _1, &block::tlb::t_ExtraCurrencyCollection));
d.def_stack_word("ExtraCurrencyCollection",
std::bind(interpret_tlb_type_const, _1, &block::tlb::t_ExtraCurrencyCollection));
}
void usage(const char* progname) {

View file

@ -1561,10 +1561,10 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
return skip_invalid ? 0 : 38; // not enough (extra) funds
}
if (ap.remaining_balance.extra.not_null() || req.extra.not_null()) {
LOG(WARNING) << "subtracting extra currencies: "
<< block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " minus "
<< block::CurrencyCollection{0, req.extra}.to_str() << " equals "
<< block::CurrencyCollection{0, new_extra}.to_str();
LOG(DEBUG) << "subtracting extra currencies: "
<< block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " minus "
<< block::CurrencyCollection{0, req.extra}.to_str() << " equals "
<< block::CurrencyCollection{0, new_extra}.to_str();
}
auto fwd_fee_mine = msg_prices.get_first_part(fwd_fee);
@ -1730,11 +1730,17 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
}
bounce_phase = std::make_unique<BouncePhase>();
BouncePhase& bp = *bounce_phase;
block::gen::Message::Record msg;
block::gen::CommonMsgInfo::Record_int_msg_info info;
if (!tlb::unpack_cell_inexact(in_msg, info)) {
auto cs = vm::load_cell_slice(in_msg);
if (!(tlb::unpack(cs, info) && gen::t_Maybe_Either_StateInit_Ref_StateInit.skip(cs) && cs.have(1) &&
cs.have_refs((int)cs.prefetch_ulong(1)))) {
bounce_phase.reset();
return false;
}
if (cs.fetch_ulong(1)) {
cs = vm::load_cell_slice(cs.prefetch_ref());
}
info.ihr_disabled = true;
info.bounce = false;
info.bounced = true;
@ -1786,10 +1792,26 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
&& block::tlb::t_Grams.store_long(cb, bp.fwd_fees) // fwd_fee:Grams
&& cb.store_long_bool(info.created_lt, 64) // created_lt:uint64
&& cb.store_long_bool(info.created_at, 32) // created_at:uint32
&& cb.store_long_bool(0, 2) // init:(Maybe ...) state:(Either ..)
&& cb.finalize_to(bp.out_msg));
&& cb.store_bool_bool(false)); // init:(Maybe ...)
if (cfg.bounce_msg_body) {
int body_bits = std::min((int)cs.size(), cfg.bounce_msg_body);
if (cb.remaining_bits() >= body_bits + 33u) {
CHECK(cb.store_bool_bool(false) // body:(Either X ^X) -> left X
&& cb.store_long_bool(-1, 32) // int = -1 ("message type")
&& cb.append_bitslice(cs.prefetch_bits(body_bits))); // truncated message body
} else {
vm::CellBuilder cb2;
CHECK(cb.store_bool_bool(true) // body:(Either X ^X) -> right ^X
&& cb2.store_long_bool(-1, 32) // int = -1 ("message type")
&& cb2.append_bitslice(cs.prefetch_bits(body_bits)) // truncated message body
&& cb.store_builder_ref_bool(std::move(cb2))); // ^X
}
} else {
CHECK(cb.store_bool_bool(false)); // body:(Either ..)
}
CHECK(cb.finalize_to(bp.out_msg));
if (verbosity > 2) {
std::cerr << "generated bounced message: ";
LOG(INFO) << "generated bounced message: ";
block::gen::t_Message_Any.print_ref(std::cerr, bp.out_msg);
}
out_msgs.push_back(bp.out_msg);

View file

@ -139,6 +139,7 @@ struct ComputePhaseConfig {
struct ActionPhaseConfig {
int max_actions{255};
int bounce_msg_body{0}; // usually 0 or 256 bits
MsgPrices fwd_std;
MsgPrices fwd_mc; // from/to masterchain
const WorkchainSet* workchains{nullptr};

View file

@ -118,6 +118,24 @@ std::pair<RefInt256, RefInt256> divmod(RefInt256 x, RefInt256 y, int round_mode)
return std::make_pair(std::move(quot), std::move(x));
}
RefInt256 muldiv(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode) {
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
RefInt256 quot{true};
tmp.mod_div(*z, quot.unique_write(), round_mode);
quot.write().normalize();
return quot;
}
std::pair<RefInt256, RefInt256> muldivmod(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode) {
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
RefInt256 quot{true};
tmp.mod_div(*z, quot.unique_write(), round_mode);
quot.write().normalize();
return std::make_pair(std::move(quot), td::make_refint(tmp));
}
RefInt256 operator&(RefInt256 x, RefInt256 y) {
x.write() &= *y;
return x;

View file

@ -45,6 +45,8 @@ extern RefInt256 operator%(RefInt256 x, RefInt256 y);
extern RefInt256 div(RefInt256 x, RefInt256 y, int round_mode = -1);
extern RefInt256 mod(RefInt256 x, RefInt256 y, int round_mode = -1);
extern std::pair<RefInt256, RefInt256> divmod(RefInt256 x, RefInt256 y, int round_mode = -1);
extern RefInt256 muldiv(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode = -1);
extern std::pair<RefInt256, RefInt256> muldivmod(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode = -1);
extern RefInt256 operator-(RefInt256 x);
extern RefInt256 operator&(RefInt256 x, RefInt256 y);
extern RefInt256 operator|(RefInt256 x, RefInt256 y);

View file

@ -1,7 +1,7 @@
library TVM_Asm
// simple TVM Assembler
variable @atend
'nop @atend !
{ "not in asm context" abort } @atend !
{ `normal eq? not abort"must be terminated by }>" } : @normal?
{ @atend @ 1 { @atend ! @normal? } does @atend ! } : @pushatend
{ @pushatend <b } : <{
@ -12,10 +12,11 @@ variable @atend
{ @atend @ 2 { @atend ! rot b> ref, swap @endblk } does @atend ! <b } : @|
{ @atend @ 3 { @atend ! 2swap rot execute } does @atend ! <b } : @doafter<{
{ over brembits <= } : @havebits
{ rot + -rot + swap } : pair+
{ rot >= -rot <= and } : 2x<=
{ 2 pick brembitrefs 1- 2x<= } : @havebitrefs
{ @havebits not ' @| if } : @ensurebits
{ @havebitrefs not ' @| if } : @ensurebitrefs
{ @havebits ' @| ifnot } : @ensurebits
{ @havebitrefs ' @| ifnot } : @ensurebitrefs
{ rot over @ensurebits -rot u, } : @simpleuop
{ tuck sbitrefs @ensurebitrefs swap s, } : @addop
{ tuck bbitrefs @ensurebitrefs swap b+ } : @addopb
@ -27,6 +28,7 @@ variable @atend
{ 1 { <b swap s, swap 4 u, @addopb } does create } : @Defop(4u)
{ 1 { <b swap s, rot 4 u, swap 4 u, @addopb } does create } : @Defop(4u,4u)
{ 1 { <b swap s, swap ref, @addopb } does create } : @Defop(ref)
{ 1 { <b swap s, rot ref, swap ref, @addopb } does create } : @Defop(ref*2)
{ <b 0xef 8 u, swap 12 i, b> } : si()
// x mi ma -- ?
{ rot tuck >= -rot <= and } : @range
@ -272,10 +274,27 @@ x{8A} @Defop(ref) PUSHREFCONT
} cond
} cond
} dup : PUSHSLICE : SLICE
{ tuck bbitrefs swap 16 + dup 7 and 3 -roll swap @havebitrefs
not rot or
{ swap b> PUSHREFCONT }
{ over bbitrefs 2dup 120 0 2x<=
// ( b' -- ? )
{ bbitrefs or 0= } : @cont-empty?
{ bbits 7 and 0= } : @cont-aligned?
// ( b b' -- ? )
{ bbitrefs over 7 and { 2drop drop false } {
swap 16 + swap @havebitrefs nip
} cond
} : @cont-fits?
// ( b b' -- ? )
{ bbitrefs over 7 and { 2drop drop false } {
32 1 pair+ @havebitrefs nip
} cond
} : @cont-ref-fit?
// ( b b' b'' -- ? )
{ over @cont-aligned? over @cont-aligned? and not { 2drop drop false } {
bbitrefs rot bbitrefs pair+ swap 32 + swap @havebitrefs nip
} cond
} : @two-cont-fit?
{ 2dup @cont-fits? not
{ b> PUSHREFCONT }
{ swap over bbitrefs 2dup 120 0 2x<=
{ drop swap x{9} s, swap 3 >> 4 u, swap b+ }
{ rot x{8F_} s, swap 2 u, swap 3 >> 7 u, swap b+ } cond
} cond
@ -320,12 +339,16 @@ x{A985} @Defop MULDIVR
x{A98C} @Defop MULDIVMOD
x{A9A4} @Defop MULRSHIFT
x{A9A5} @Defop MULRSHIFTR
x{A9A6} @Defop MULRSHIFTC
x{A9B4} @Defop(8u+1) MULRSHIFT#
x{A9B5} @Defop(8u+1) MULRSHIFTR#
x{A9B6} @Defop(8u+1) MULRSHIFTC#
x{A9C4} @Defop LSHIFTDIV
x{A9C5} @Defop LSHIFTDIVR
x{A9C6} @Defop LSHIFTDIVC
x{A9D4} @Defop(8u+1) LSHIFT#DIV
x{A9D5} @Defop(8u+1) LSHIFT#DIVR
x{A9D6} @Defop(8u+1) LSHIFT#DIVC
x{AA} @Defop(8u+1) LSHIFT#
x{AB} @Defop(8u+1) RSHIFT#
x{AC} @Defop LSHIFT
@ -628,46 +651,80 @@ x{DB3F} @Defop RETDATA
x{DC} @Defop IFRET
x{DD} @Defop IFNOTRET
x{DE} @Defop IF
{ }> PUSHCONT IF } : }>IF
x{DF} @Defop IFNOT
{ }> PUSHCONT IFNOT } : }>IFNOT
' IFNOTRET : IF:
' IFRET : IFNOT:
x{E0} @Defop IFJMP
{ }> PUSHCONT IFJMP } : }>IFJMP
{ { @normal? PUSHCONT IFJMP } @doafter<{ } : IFJMP:<{
x{E1} @Defop IFNOTJMP
{ }> PUSHCONT IFNOTJMP } : }>IFNOTJMP
{ { @normal? PUSHCONT IFNOTJMP } @doafter<{ } : IFNOTJMP:<{
x{E2} @Defop IFELSE
{ `else @endblk } : }>ELSE<{
{ `else: @endblk } : }>ELSE:
{ PUSHCONT { @normal? PUSHCONT IFELSE } @doafter<{ } : @doifelse
{ 1 { swap @normal? -rot PUSHCONT swap PUSHCONT IFELSE } does @doafter<{ } : @doifnotelse
{
{ dup `else eq?
{ drop @doifelse }
{ dup `else: eq?
{ drop PUSHCONT IFJMP }
{ @normal? PUSHCONT IF
} cond
} cond
} @doafter<{
} : IF:<{
{
{ dup `else eq?
{ drop @doifnotelse }
{ dup `else: eq?
{ drop PUSHCONT IFNOTJMP }
{ @normal? PUSHCONT IFNOT
} cond
} cond
} @doafter<{
} : IFNOT:<{
x{E300} @Defop(ref) IFREF
x{E301} @Defop(ref) IFNOTREF
x{E302} @Defop(ref) IFJMPREF
x{E303} @Defop(ref) IFNOTJMPREF
x{E30D} @Defop(ref) IFREFELSE
x{E30E} @Defop(ref) IFELSEREF
x{E30F} @Defop(ref*2) IFREFELSEREF
{ 16 1 @havebitrefs nip } : @refop-fits?
// b b1 [e0 e1 e2] -- b'
{ -rot dup @cont-empty? { drop swap 0 } {
2dup @cont-fits? { rot 1 } {
over @refop-fits? { b> rot 2 } {
swap @| swap 2dup @cont-fits? { rot 1 } {
b> rot 2
} cond } cond } cond } cond
[] execute
} : @run-cont-op
{ triple 1 ' @run-cont-op does create } : @def-cont-op
{ } { PUSHCONT IF } { IFREF } @def-cont-op IF-cont
{ IFRET } { PUSHCONT IFJMP } { IFJMPREF } @def-cont-op IFJMP-cont
{ } { PUSHCONT IFNOT } { IFNOTREF } @def-cont-op IFNOT-cont
{ IFNOTRET } { PUSHCONT IFNOTJMP } { IFNOTJMPREF } @def-cont-op IFNOTJMP-cont
{ dup 2over rot } : 3dup
recursive IFELSE-cont2 {
dup @cont-empty? { drop IF-cont } {
over @cont-empty? { nip IFNOT-cont } {
3dup @two-cont-fit? { -rot PUSHCONT swap PUSHCONT IFELSE } {
3dup nip @cont-ref-fit? { rot swap PUSHCONT swap b> IFREFELSE } {
3dup drop @cont-ref-fit? { -rot PUSHCONT swap b> IFELSEREF } {
rot 32 2 @havebitrefs { rot b> rot b> IFREFELSEREF } {
@| -rot IFELSE-cont2
} cond } cond } cond } cond } cond } cond
} swap !
{ }> IF-cont } : }>IF
{ }> IFNOT-cont } : }>IFNOT
{ }> IFJMP-cont } : }>IFJMP
{ }> IFNOTJMP-cont } : }>IFNOTJMP
{ { @normal? IFJMP-cont } @doafter<{ } : IFJMP:<{
{ { @normal? IFNOTJMP-cont } @doafter<{ } : IFNOTJMP:<{
{ `else @endblk } : }>ELSE<{
{ `else: @endblk } : }>ELSE:
{ 1 { swap @normal? swap IFELSE-cont2 } does @doafter<{ } : @doifelse
{ 1 { swap @normal? IFELSE-cont2 } does @doafter<{ } : @doifnotelse
{
{ dup `else eq?
{ drop @doifelse }
{ dup `else: eq?
{ drop IFJMP-cont }
{ @normal? IF-cont
} cond
} cond
} @doafter<{
} : IF:<{
{
{ dup `else eq?
{ drop @doifnotelse }
{ dup `else: eq?
{ drop IFNOTJMP-cont }
{ @normal? IFNOT-cont
} cond
} cond
} @doafter<{
} : IFNOT:<{
x{E304} @Defop CONDSEL
x{E305} @Defop CONDSELCHK
x{E308} @Defop IFRETALT
@ -676,18 +733,22 @@ x{E309} @Defop IFNOTRETALT
{ <b x{E3B_} swap 5 u, @addopb } : IFNBITJMP
{ <b x{E3D_} swap 5 u, swap ref, @addopb } : IFBITJMPREF
{ <b x{E3F_} swap 5 u, swap ref, @addopb } : IFNBITJMPREF
x{E4} @Defop REPEAT
{ }> PUSHCONT REPEAT } : }>REPEAT
{ { @normal? PUSHCONT REPEAT } @doafter<{ } : REPEAT:<{
x{E5} dup @Defop REPEATEND @Defop REPEAT:
x{E6} @Defop UNTIL
{ }> PUSHCONT UNTIL } : }>UNTIL
{ { @normal? PUSHCONT UNTIL } @doafter<{ } : UNTIL:<{
x{E7} dup @Defop UNTILEND @Defop UNTIL:
x{E8} @Defop WHILE
x{E9} @Defop WHILEEND
x{EA} @Defop AGAIN
x{EB} dup @Defop AGAINEND @Defop AGAIN:
{ `do @endblk } : }>DO<{
{ `do: @endblk } : }>DO:
{ }> PUSHCONT REPEAT } : }>REPEAT
{ { @normal? PUSHCONT REPEAT } @doafter<{ } : REPEAT:<{
{ }> PUSHCONT UNTIL } : }>UNTIL
{ { @normal? PUSHCONT UNTIL } @doafter<{ } : UNTIL:<{
{ PUSHCONT { @normal? PUSHCONT WHILE } @doafter<{ } : @dowhile
{
{ dup `do eq?
@ -696,10 +757,34 @@ x{E9} @Defop WHILEEND
} cond
} @doafter<{
} : WHILE:<{
x{EA} @Defop AGAIN
{ }> PUSHCONT AGAIN } : }>AGAIN
{ { @normal? PUSHCONT AGAIN } @doafter<{ } : AGAIN:<{
x{EB} dup @Defop AGAINEND @Defop AGAIN:
x{E314} @Defop REPEATBRK
x{E315} @Defop REPEATENDBRK
x{E316} @Defop UNTILBRK
x{E317} dup @Defop UNTILENDBRK @Defop UNTILBRK:
x{E318} @Defop WHILEBRK
x{E319} @Defop WHILEENDBRK
x{E31A} @Defop AGAINBRK
x{E31B} dup @Defop AGAINENDBRK @Defop AGAINBRK:
{ }> PUSHCONT REPEATBRK } : }>REPEATBRK
{ { @normal? PUSHCONT REPEATBRK } @doafter<{ } : REPEATBRK:<{
{ }> PUSHCONT UNTILBRK } : }>UNTILBRK
{ { @normal? PUSHCONT UNTILBRK } @doafter<{ } : UNTILBRK:<{
{ PUSHCONT { @normal? PUSHCONT WHILEBRK } @doafter<{ } : @dowhile
{
{ dup `do eq?
{ drop @dowhile }
{ `do: eq? not abort"`}>DO<{` expected" PUSHCONT WHILEENDBRK
} cond
} @doafter<{
} : WHILEBRK:<{
{ }> PUSHCONT AGAINBRK } : }>AGAINBRK
{ { @normal? PUSHCONT AGAINBRK } @doafter<{ } : AGAINBRK:<{
//
// continuation stack manipulation and continuation creation
//
@ -745,6 +830,8 @@ x{EDF6} @Defop THENRET
x{EDF7} @Defop THENRETALT
x{EDF8} @Defop INVERT
x{EDF9} @Defop BOOLEVAL
x{EDFA} @Defop SAMEALT
x{EDFB} @Defop SAMEALTSAVE
// x{EE} is BLESSARGS
//
// dictionary subroutine call/jump primitives

View file

@ -111,6 +111,42 @@ recursive list-map {
swap uncons -rot over execute -rot list-map cons
} cond
} swap !
variable ctxdump variable curctx
// (a1 .. an) e -- executes e for a1, ..., an
{ ctxdump @ curctx @ ctxdump 2! curctx 2!
{ curctx 2@ over null? not } { swap uncons rot tuck curctx 2! execute }
while 2drop ctxdump 2@ curctx ! ctxdump !
} : list-foreach
forget ctxdump forget curctx
//
// Experimental implementation of `for` loops with index
//
variable loopdump variable curloop
{ curloop @ loopdump @ loopdump 2! } : push-loop-ctx
{ loopdump 2@ loopdump ! curloop ! } : pop-loop-ctx
// ilast i0 e -- executes e for i=i0,i0+1,...,ilast-1
{ -rot 2dup > {
push-loop-ctx {
triple dup curloop ! first execute curloop @ untriple 1+ 2dup <=
} until pop-loop-ctx
} if 2drop drop
} : for
// ilast i0 e -- same as 'for', but pushes current index i before executing e
{ -rot 2dup > {
push-loop-ctx {
triple dup curloop ! untriple nip swap execute curloop @ untriple 1+ 2dup <=
} until pop-loop-ctx
} if 2drop drop
} : for-i
// ( -- i ) Returns innermost loop index
{ curloop @ third } : i
// ( -- j ) Returns outer loop index
{ loopdump @ car third } : j
{ loopdump @ cadr third } : k
forget curloop forget loopdump
//
// create Lisp-style lists using words "(" and ")"
//

View file

@ -177,28 +177,15 @@ void interpret_divmod(vm::Stack& stack, int round_mode) {
}
void interpret_times_div(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
stack.push_int(std::move(q));
auto z = stack.pop_int(), y = stack.pop_int(), x = stack.pop_int();
stack.push_int(muldiv(std::move(x), std::move(y), std::move(z), round_mode));
}
void interpret_times_divmod(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
stack.push_int(std::move(q));
stack.push_int(td::make_refint(tmp));
auto z = stack.pop_int(), y = stack.pop_int(), x = stack.pop_int();
auto dm = muldivmod(std::move(x), std::move(y), std::move(z));
stack.push_int(std::move(dm.first));
stack.push_int(std::move(dm.second));
}
void interpret_times_mod(vm::Stack& stack, int round_mode) {

View file

@ -427,9 +427,7 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
return exec_op("NEGATE", 1);
}
AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const * y.int_const);
x.unused();
@ -492,6 +490,11 @@ AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
return exec_op("MUL", 2);
}
AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
return compile_mul_internal(res[0], args[0], args[1]);
}
AsmOp compile_lshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
@ -566,9 +569,7 @@ AsmOp compile_rshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, in
return exec_op(rshift, 2);
}
AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
AsmOp compile_div_internal(VarDescr& r, VarDescr& x, VarDescr& y, int round_mode) {
if (x.is_int_const() && y.is_int_const()) {
r.set_const(div(x.int_const, y.int_const, round_mode));
x.unused();
@ -608,6 +609,11 @@ AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
return exec_op(op, 2);
}
AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
return compile_div_internal(res[0], args[0], args[1], round_mode);
}
AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
@ -648,6 +654,87 @@ AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
return exec_op(op, 2);
}
AsmOp compile_muldiv(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 3);
VarDescr &r = res[0], &x = args[0], &y = args[1], &z = args[2];
if (x.is_int_const() && y.is_int_const() && z.is_int_const()) {
r.set_const(muldiv(x.int_const, y.int_const, z.int_const, round_mode));
x.unused();
y.unused();
z.unused();
return push_const(r.int_const);
}
if (x.always_zero() || y.always_zero()) {
// dubious optimization for z=0...
x.unused();
y.unused();
z.unused();
r.set_const(td::make_refint(0));
return push_const(r.int_const);
}
char c = (round_mode < 0) ? 0 : (round_mode > 0 ? 'C' : 'R');
r.val = emulate_div(emulate_mul(x.val, y.val), z.val);
if (z.is_int_const()) {
if (*z.int_const == 0) {
x.unused();
y.unused();
z.unused();
r.set_const(div(z.int_const, z.int_const));
return push_const(r.int_const);
}
if (*z.int_const == 1) {
z.unused();
return compile_mul_internal(r, x, y);
}
}
if (y.is_int_const() && *y.int_const == 1) {
y.unused();
return compile_div_internal(r, x, z, round_mode);
}
if (x.is_int_const() && *x.int_const == 1) {
x.unused();
return compile_div_internal(r, y, z, round_mode);
}
if (z.is_int_const()) {
int k = is_pos_pow2(z.int_const);
if (k > 0) {
z.unused();
std::string op = "MULRSHIFT";
if (c) {
op += c;
}
return exec_arg_op(op + '#', k, 2);
}
}
if (y.is_int_const()) {
int k = is_pos_pow2(y.int_const);
if (k > 0) {
y.unused();
std::string op = "LSHIFT#DIV";
if (c) {
op += c;
}
return exec_arg_op(op, k, 2);
}
}
if (x.is_int_const()) {
int k = is_pos_pow2(x.int_const);
if (k > 0) {
x.unused();
std::string op = "LSHIFT#DIV";
if (c) {
op += c;
}
return exec_arg_op(op, k, 2);
}
}
std::string op = "MULDIV";
if (c) {
op += c;
}
return exec_op(op, 3);
}
int compute_compare(td::RefInt256 x, td::RefInt256 y, int mode) {
int s = td::cmp(x, y);
if (mode == 7) {
@ -933,8 +1020,9 @@ void define_builtins() {
define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2));
define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2));
define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2));
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIVR", 3));
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIV", 3));
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, -1));
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 0));
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 1));
define_builtin_func("muldivmod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2));
define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5));

View file

@ -575,9 +575,9 @@ bool Optimizer::find_at_least(int pb) {
(is_push_rotrev(&i) && rewrite(AsmOp::Push(i), AsmOp::Custom("-ROT"))) ||
(is_push_xchg(&i, &j, &k) && rewrite(AsmOp::Push(i), AsmOp::Xchg(j, k))) ||
(is_reverse(&i, &j) && rewrite(AsmOp::BlkReverse(i, j))) ||
(is_blkdrop2(&i, &j) && rewrite(AsmOp::BlkDrop2(i, j))) ||
(is_nip_seq(&i, &j) && rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
(is_pop_blkdrop(&i, &k) && rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
(is_blkdrop2(&i, &j) && rewrite(AsmOp::BlkDrop2(i, j))) ||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
? rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
: rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||

View file

@ -23,6 +23,25 @@
.end_cell());
}
;; [min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price]
_ parse_vote_config(cell c) inline {
var cs = c.begin_parse();
throw_unless(44, cs~load_uint(8) == 0x36);
var res = [cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)];
cs.end_parse();
return res;
}
;; cfg_vote_setup#91 normal_params:^ConfigProposalSetup critical_params:^ConfigProposalSetup = ConfigVotingSetup;
_ get_vote_config(int critical?) inline_ref {
var cs = config_param(11).begin_parse();
throw_unless(44, cs~load_uint(8) == 0x91);
if (critical?) {
cs~load_ref();
}
return parse_vote_config(cs.preload_ref());
}
(int, int) check_validator_set(cell vset) {
var cs = vset.begin_parse();
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
@ -46,53 +65,14 @@
.end_cell(), mode);
}
() send_confirmation(addr, query_id, ans_tag) impure {
() send_confirmation(addr, query_id, ans_tag) impure inline {
return send_answer(addr, query_id, ans_tag, 64);
}
() send_error(addr, query_id, ans_tag) impure {
() send_error(addr, query_id, ans_tag) impure inline {
return send_answer(addr, query_id, ans_tag, 64);
}
() recv_internal(cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
var s_addr = cs~load_msg_addr();
(int src_wc, int src_addr) = s_addr.parse_std_addr();
if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
;; source not in masterchain, or a bounced message, or a simple transfer
return ();
}
int tag = in_msg~load_uint(32);
int query_id = in_msg~load_uint(64);
if (tag == 0x4e565354) {
;; set next validator set
var vset = in_msg~load_ref();
in_msg.end_parse();
var elector_param = config_param(1);
var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
var ok = false;
if (src_addr == elector_addr) {
;; message from elector smart contract
;; set next validator set
(var t_since, var t_until) = check_validator_set(vset);
var t = now();
ok = (t_since > t) & (t_until > t_since);
}
if (ok) {
set_conf_param(36, vset);
;; send confirmation
return send_confirmation(s_addr, query_id, 0xee764f4b);
} else {
return send_error(s_addr, query_id, 0xee764f6f);
}
}
;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
;; to bounce message back to sender
throw_unless(37, (tag == 0) | (tag & (1 << 31)));
;; do nothing for other internal messages
}
;; forward a message to elector smart contract to make it upgrade its code
() change_elector_code(slice cs) impure {
var dest_addr = config_param(1).begin_parse().preload_uint(256);
@ -114,7 +94,7 @@
_ perform_action(cfg_dict, public_key, action, cs) {
if (action == 0x43665021) {
;; change one configuration parameter
var param_index = cs~load_uint(32);
var param_index = cs~load_int(32);
var param_value = cs~load_ref();
cs.end_parse();
cfg_dict~idict_set_ref(32, param_index, param_value);
@ -143,11 +123,8 @@ _ perform_action(cfg_dict, public_key, action, cs) {
}
}
(slice, int) get_validator_descr(int idx) inline_ref {
(cell, int, slice) get_current_vset() inline_ref {
var vset = config_param(34);
if (vset.null?()) {
return (null(), 0);
}
var cs = begin_parse(vset);
;; validators_ext#12 utime_since:uint32 utime_until:uint32
;; total:(## 16) main:(## 16) { main <= total } { main >= 1 }
@ -155,6 +132,11 @@ _ perform_action(cfg_dict, public_key, action, cs) {
throw_unless(40, cs~load_uint(8) == 0x12);
cs~skip_bits(32 + 32 + 16 + 16);
int total_weight = cs~load_uint(64);
return (vset, total_weight, cs);
}
(slice, int) get_validator_descr(int idx) inline_ref {
var (vset, total_weight, cs) = get_current_vset();
var dict = begin_cell().store_slice(cs).end_cell();
var (value, _) = dict.udict_get?(16, idx);
return (value, total_weight);
@ -169,6 +151,40 @@ _ perform_action(cfg_dict, public_key, action, cs) {
return (cs~load_uint(256), cs~load_uint(64));
}
;; cfg_proposal#f3 param_id:int32 param_value:(Maybe ^Cell) if_hash_equal:(Maybe uint256)
;; c -> (param-id param-cell maybe-hash)
(int, cell, int) parse_config_proposal(cell c) inline_ref {
var cs = c.begin_parse();
throw_unless(44, cs~load_int(8) == 0xf3 - 0x100);
var (id, val, hash) = (cs~load_int(32), cs~load_maybe_ref(), cs~load_int(1));
if (hash) {
hash = cs~load_uint(256);
} else {
hash = -1;
}
cs.end_parse();
return (id, val, hash);
}
;; cfg_proposal_status#ce expires:uint32 proposal:^ConfigProposal is_critical:Bool
;; voters:(HashmapE 16 True) remaining_weight:int64 validator_set_id:uint256
;; rounds_remaining:uint8 wins:uint8 losses:uint8 = ConfigProposalStatus;
(int, cell, int, cell, int, int, slice) unpack_proposal_status(slice cs) inline_ref {
throw_unless(44, cs~load_int(8) == 0xce - 0x100);
return (cs~load_uint(32), cs~load_ref(), cs~load_int(1), cs~load_dict(), cs~load_int(64), cs~load_uint(256), cs);
}
builder begin_pack_proposal_status(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id) inline {
return begin_cell()
.store_int(0xce - 0x100, 8)
.store_uint(expires, 32)
.store_ref(proposal)
.store_int(critical?, 1)
.store_dict(voters)
.store_int(weight_remaining, 64)
.store_uint(vset_id, 256);
}
(cell, int, int, slice) new_proposal(cs) inline {
return (null(), 0, 0, cs);
}
@ -216,6 +232,141 @@ builder pack_proposal(cell voters, int sum_weight, int vset_id, slice body) inli
}
}
int register_voting_proposal(slice cs, int msg_value) inline_ref {
var (expire_at, proposal, critical?) = (cs~load_uint(32), cs~load_ref(), cs~load_int(1));
if (expire_at >> 30) {
expire_at -= now();
}
var (param_id, param_val, hash) = parse_config_proposal(proposal);
if (hash >= 0) {
cell cur_val = config_param(param_id);
int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
if (cur_hash != hash) {
hash = -0xe2646356; ;; bad current value
}
} else {
var m_params = config_param(9);
var (_, found?) = m_params.idict_get?(32, param_id);
if (found?) {
hash = -0xcd506e6c; ;; cannot set mandatory parameter to null
}
}
if (param_val.cell_depth() >= 256) {
hash = -0xc2616456; ;; bad value
}
if (hash < -1) {
return hash; ;; return error if any
}
ifnot (critical?) {
var crit_params = config_param(10);
var (_, found?) = crit_params.idict_get?(32, param_id);
if (found?) {
hash = -0xc3726954; ;; trying to set a critical parameter without critical flag
}
}
if (hash < -1) {
return hash;
}
;; obtain vote proposal configuration
var vote_cfg = get_vote_config(critical?);
var [min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price] = vote_cfg;
if (expire_at < min_store_sec) {
return -0xc5787069; ;; expired
}
expire_at = min(expire_at, max_store_sec);
;; compute price
var (_, bits, refs) = compute_data_size(param_val, 1024);
var pps = bit_price * (bits + 1024) + cell_price * (refs + 2);
var price = pps * expire_at;
expire_at += now();
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
int phash = proposal.cell_hash();
var (pstatus, found?) = vote_dict.udict_get?(256, phash);
if (found?) {
;; proposal already exists; we can only extend it
var (expires, r_proposal, r_critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
if (r_critical? != critical?) {
return -0xc3726955; ;; cannot upgrade critical parameter to non-critical...
}
if (expires >= expire_at) {
return -0xc16c7245; ;; proposal already exists
}
;; recompute price
price = pps * (expire_at - expires + 16384);
if (msg_value - price < (1 << 30)) {
return -0xf0617924; ;; need more money
}
;; update expiration time
vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expire_at, r_proposal, r_critical?, voters, weight_remaining, vset_id).store_slice(rest));
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return price;
}
if (msg_value - price < (1 << 30)) {
return -0xf0617924; ;; need more money
}
;; obtain current validator set data
var (vset, total_weight, _) = get_current_vset();
int weight_remaining = muldiv(total_weight, 2, 3) + 1;
;; create new proposal
vote_dict~udict_set_builder(256, phash,
begin_pack_proposal_status(expire_at, proposal, critical?, null(), weight_remaining, vset.cell_hash())
.store_uint(max_tot_rounds, 8).store_uint(0, 16));
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return price;
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
var s_addr = cs~load_msg_addr();
(int src_wc, int src_addr) = s_addr.parse_std_addr();
if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
;; source not in masterchain, or a bounced message, or a simple transfer
return ();
}
int tag = in_msg~load_uint(32);
int query_id = in_msg~load_uint(64);
if (tag == 0x4e565354) {
;; set next validator set
var vset = in_msg~load_ref();
in_msg.end_parse();
var elector_param = config_param(1);
var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
var ok = false;
if (src_addr == elector_addr) {
;; message from elector smart contract
;; set next validator set
(var t_since, var t_until) = check_validator_set(vset);
var t = now();
ok = (t_since > t) & (t_until > t_since);
}
if (ok) {
set_conf_param(36, vset);
;; send confirmation
return send_confirmation(s_addr, query_id, 0xee764f4b);
} else {
return send_error(s_addr, query_id, 0xee764f6f);
}
}
if (tag == 0x6e565052) {
;; new voting proposal
var price = register_voting_proposal(cs, msg_value);
int mode = 64;
int ans_tag = - price;
if (price >= 0) {
;; ok, debit price
raw_reserve(price, 4);
ans_tag = 0xee565052;
mode = 128;
}
return send_answer(s_addr, query_id, ans_tag, mode);
}
;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
;; to bounce message back to sender
throw_unless(37, (tag == 0) | (tag & (1 << 31)));
;; do nothing for other internal messages
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;

View file

@ -201,8 +201,8 @@ smc1_addr config.minter_smc!
1000000000000 -17 of-cc 666666666666 239 of-cc cc+ config.to_mint!
( 9 10 18 20 21 22 23 24 25 28 34 ) config.mandatory_params!
( -1000 -1001 9 10 32 34 36 ) config.critical_params!
( 0 1 9 10 12 14 15 16 17 18 20 21 22 23 24 25 28 34 ) config.mandatory_params!
( -1000 -1001 0 1 9 10 12 14 15 16 17 32 34 36 ) config.critical_params!
// [ min_tot_rounds max_tot_rounds min_wins max_losses min_store_sec max_store_sec bit_price cell_price ]
// first for ordinary proposals, then for critical proposals

View file

@ -66,6 +66,13 @@ _ unpack_owner_info(slice cs) inline_ref {
return (root_i, 0, 0, in_msg);
}
(cell, ()) dec_flood(cell owner_infos, int creator_i) {
(slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
return (owner_infos, ());
}
() try_init() impure inline_ref {
;; first query without signatures is always accepted
(int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state();
@ -82,10 +89,7 @@ _ unpack_owner_info(slice cs) inline_ref {
send_raw_message(msg~load_ref(), mode);
}
pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
(slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
owner_infos~dec_flood(creator_i);
} else {
pending_queries~udict_set_builder(64, query_id, begin_cell()
.store_uint(1, 1)
@ -123,8 +127,8 @@ _ unpack_owner_info(slice cs) inline_ref {
last_cleaned -= last_cleaned == 0;
(slice owner_info, var found?) = owner_infos.udict_get?(8, root_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
throw_unless(31, found?);
(int public_key, int flood) = unpack_owner_info(owner_info);
throw_unless(32, check_signature(root_hash, root_signature, public_key));
cell signatures = in_msg~load_dict();
@ -154,11 +158,12 @@ _ unpack_owner_info(slice cs) inline_ref {
cnt_bits |= mask;
cnt += 1;
throw_if(41, ~ found? & (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
set_gas_limit(100000);
ifnot (found?) {
owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood));
throw_if(41, (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
}
(pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
@ -178,12 +183,15 @@ _ unpack_owner_info(slice cs) inline_ref {
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
int old_last_cleaned = last_cleaned;
do {
var (pending_queries', i, _, f) = pending_queries.udict_delete_get_min(64);
var (pending_queries', i, query, f) = pending_queries.udict_delete_get_min(64);
f~touch();
if (f) {
f = (i < bound);
}
if (f) {
if (query~load_int(1)) {
owner_infos~dec_flood(query~load_uint(8));
}
pending_queries = pending_queries';
last_cleaned = i;
need_save = -1;

View file

@ -64,6 +64,8 @@ cont bless(slice s) impure asm "BLESS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";

View file

@ -44,7 +44,7 @@ td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
return stack_ref;
}
td::Ref<vm::Tuple> prepare_vm_c7() {
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
// TODO: fix initialization of c7
td::BitArray<256> rand_seed;
rand_seed.as_slice().fill(0);
@ -54,7 +54,7 @@ td::Ref<vm::Tuple> prepare_vm_c7() {
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(0), // unixtime:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(0), // block_lt:Integer
td::make_refint(0), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
@ -166,8 +166,12 @@ td::Ref<vm::Cell> SmartContract::get_init_state() const {
}
SmartContract::Answer SmartContract::run_method(Args args) {
td::uint32 now = 0;
if (args.now) {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7();
args.c7 = prepare_vm_c7(now);
}
if (!args.limits) {
args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000};
@ -182,8 +186,12 @@ SmartContract::Answer SmartContract::run_method(Args args) {
}
SmartContract::Answer SmartContract::run_get_method(Args args) const {
td::uint32 now = 0;
if (args.now) {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7();
args.c7 = prepare_vm_c7(now);
}
if (!args.limits) {
args.limits = vm::GasLimits{1000000};

View file

@ -55,6 +55,7 @@ class SmartContract : public td::CntObject {
td::optional<vm::GasLimits> limits;
td::optional<td::Ref<vm::Tuple>> c7;
td::optional<td::Ref<vm::Stack>> stack;
td::optional<td::int32> now;
bool ignore_chksig{false};
Args() {
@ -62,6 +63,10 @@ class SmartContract : public td::CntObject {
Args(std::initializer_list<vm::StackEntry> stack)
: stack(td::Ref<vm::Stack>(true, std::vector<vm::StackEntry>(std::move(stack)))) {
}
Args&& set_now(int now) {
this->now = now;
return std::move(*this);
}
Args&& set_method_id(td::Slice method_name) {
unsigned crc = td::crc16(method_name);
return set_method_id((crc & 0xffff) | 0x10000);

View file

@ -583,16 +583,58 @@ TEST(Smartcon, Multisig) {
wallet_id, td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
auto ms = ton::MultisigWallet::create(init_state);
td::uint64 query_id = 123 | ((100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
// first empty query (init)
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0);
// first empty query
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code > 0);
td::uint32 now = 0;
auto args = [&now]() -> ton::SmartContract::Args { return ton::SmartContract::Args().set_now(now); };
// first empty query (init)
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize(), args()).code == 0);
// first empty query
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize(), args()).code > 0);
{
td::uint64 query_id = 123 | ((now + 10 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(0, keys[0]);
auto res = ms.write().send_external_message(query, args());
CHECK(!res.accepted);
CHECK(res.code == 41);
}
{
for (int i = 1; i <= 11; i++) {
td::uint64 query_id = i | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(5, keys[5]);
auto res = ms.write().send_external_message(query, args());
if (i <= 10) {
CHECK(res.accepted);
} else {
CHECK(!res.accepted);
}
}
now += 100 * 60 + 100;
{
td::uint64 query_id = 200 | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(6, keys[6]);
auto res = ms.write().send_external_message(query, args());
CHECK(res.accepted);
}
{
td::uint64 query_id = 300 | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(5, keys[5]);
auto res = ms.write().send_external_message(query, args());
CHECK(res.accepted);
}
}
td::uint64 query_id = 123 | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
for (int i = 0; i < 10; i++) {
auto query = qb.create(i, keys[i]);
auto ans = ms.write().send_external_message(query);
auto ans = ms.write().send_external_message(query, args());
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
}
@ -602,12 +644,12 @@ TEST(Smartcon, Multisig) {
auto query = qb.create(49, keys[49]);
CHECK(ms->get_n_k() == std::make_pair(n, k));
auto ans = ms.write().send_external_message(query);
auto ans = ms.write().send_external_message(query, args());
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
CHECK(ans.success);
ASSERT_EQ(0, ms->processed(query_id));
CHECK(ms.write().send_external_message(query).code > 0);
CHECK(ms.write().send_external_message(query, args()).code > 0);
ASSERT_EQ(0, ms->processed(query_id));
{
@ -618,7 +660,7 @@ TEST(Smartcon, Multisig) {
query = qb.create(99, keys[99]);
}
ans = ms.write().send_external_message(query);
ans = ms.write().send_external_message(query, args());
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
ASSERT_EQ(-1, ms->processed(query_id));

View file

@ -789,7 +789,7 @@ void register_shift_logic_ops(OpcodeTable& cp0) {
int exec_minmax(VmState* st, int mode) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute MINMAXOP " << mode;
VM_LOG(st) << "execute " << (mode & 1 ? "Q" : "") << (mode & 2 ? "MIN" : "") << (mode & 4 ? "MAX" : "");
stack.check_underflow(2);
auto x = stack.pop_int();
auto y = stack.pop_int();
@ -811,7 +811,7 @@ int exec_minmax(VmState* st, int mode) {
int exec_abs(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ABS";
VM_LOG(st) << "execute " << (quiet ? "QABS" : "ABS");
stack.check_underflow(1);
auto x = stack.pop_int();
if (x->is_valid() && x->sgn() < 0) {

View file

@ -58,14 +58,27 @@ std::string dump_push_ref(CellSlice& cs, unsigned args, int pfx_bits, std::strin
return "";
}
cs.advance(pfx_bits);
cs.advance_refs(1);
return name;
auto cell = cs.fetch_ref();
return name + " (" + cell->get_hash().to_hex() + ")";
}
int compute_len_push_ref(const CellSlice& cs, unsigned args, int pfx_bits) {
return cs.have_refs(1) ? (0x10000 + pfx_bits) : 0;
}
std::string dump_push_ref2(CellSlice& cs, unsigned args, int pfx_bits, std::string name) {
if (!cs.have_refs(2)) {
return "";
}
cs.advance(pfx_bits);
auto cell1 = cs.fetch_ref(), cell2 = cs.fetch_ref();
return name + " (" + cell1->get_hash().to_hex() + ") (" + cell2->get_hash().to_hex() + ")";
}
int compute_len_push_ref2(const CellSlice& cs, unsigned args, int pfx_bits) {
return cs.have_refs(2) ? (0x20000 + pfx_bits) : 0;
}
int exec_push_slice_common(VmState* st, CellSlice& cs, unsigned data_bits, unsigned refs, int pfx_bits) {
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a PUSHSLICE instruction"};

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "vm/cellslice.h"
@ -28,4 +28,7 @@ void register_cell_ops(OpcodeTable &cp0);
std::string dump_push_ref(CellSlice &cs, unsigned args, int pfx_bits, std::string name);
int compute_len_push_ref(const CellSlice &cs, unsigned args, int pfx_bits);
std::string dump_push_ref2(CellSlice &cs, unsigned args, int pfx_bits, std::string name);
int compute_len_push_ref2(const CellSlice &cs, unsigned args, int pfx_bits);
} // namespace vm

View file

@ -26,10 +26,12 @@
#include "vm/excno.hpp"
#include "vm/vm.h"
using namespace std::literals::string_literals;
namespace vm {
int exec_execute(VmState* st) {
VM_LOG(st) << "execute EXECUTE\n";
VM_LOG(st) << "execute EXECUTE";
auto cont = st->get_stack().pop_cont();
return st->call(std::move(cont));
}
@ -150,12 +152,58 @@ int exec_callcc_varargs(VmState* st) {
int exec_do_with_ref(VmState* st, CellSlice& cs, int pfx_bits, const std::function<int(VmState*, Ref<OrdCont>)>& func,
const char* name) {
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a CALLREF instruction"};
throw VmError{Excno::inv_opcode, "no references left for a "s + name + " instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
VM_LOG(st) << "execute " << name << " (" << cell->get_hash().to_hex() << ")";
return func(st, Ref<OrdCont>{true, load_cell_slice_ref(std::move(cell)), st->get_cp()});
return func(st, st->ref_to_cont(std::move(cell)));
}
int exec_do_with_cell(VmState* st, CellSlice& cs, int pfx_bits, const std::function<int(VmState*, Ref<Cell>)>& func,
const char* name) {
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a "s + name + " instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
VM_LOG(st) << "execute " << name << " (" << cell->get_hash().to_hex() << ")";
return func(st, std::move(cell));
}
int exec_ifelse_ref(VmState* st, CellSlice& cs, int pfx_bits, bool mode) {
const char* name = mode ? "IFREFELSE" : "IFELSEREF";
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a "s + name + " instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name << " (" << cell->get_hash().to_hex() << ")";
stack.check_underflow(2);
auto cont = stack.pop_cont();
if (stack.pop_bool() == mode) {
cont = st->ref_to_cont(std::move(cell));
} else {
cell.clear();
}
return st->call(std::move(cont));
}
int exec_ifref_elseref(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
if (!cs.have_refs(2)) {
throw VmError{Excno::inv_opcode, "no references left for a IFREFELSEREF instruction"};
}
cs.advance(pfx_bits);
auto cell1 = cs.fetch_ref(), cell2 = cs.fetch_ref();
Stack& stack = st->get_stack();
VM_LOG(st) << "execute IFREFELSEREF (" << cell1->get_hash().to_hex() << ") (" << cell2->get_hash().to_hex() << ")";
if (!stack.pop_bool()) {
cell1 = std::move(cell2);
} else {
cell2.clear();
}
return st->call(st->ref_to_cont(std::move(cell1)));
}
int exec_ret_data(VmState* st) {
@ -349,7 +397,7 @@ int exec_if_bit_jmpref(VmState* st, CellSlice& cs, unsigned args, int pfx_bits)
bool val = x->get_bit(bit);
stack.push_int(std::move(x));
if (val ^ negate) {
return st->jump(Ref<OrdCont>{true, load_cell_slice_ref(std::move(cell)), st->get_cp()});
return st->jump(st->ref_to_cont(std::move(cell)));
}
return 0;
}
@ -365,66 +413,72 @@ std::string dump_if_bit_jmpref(CellSlice& cs, unsigned args, int pfx_bits) {
return os.str();
}
int exec_repeat(VmState* st) {
int exec_repeat(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute REPEAT\n";
VM_LOG(st) << "execute REPEAT" << (brk ? "BRK" : "");
stack.check_underflow(2);
auto cont = stack.pop_cont();
int c = stack.pop_smallint_range(0x7fffffff, 0x80000000);
if (c <= 0) {
return 0;
}
return st->repeat(std::move(cont), st->extract_cc(1), c);
return st->repeat(std::move(cont), st->c1_envelope_if(brk, st->extract_cc(1)), c);
}
int exec_repeat_end(VmState* st) {
int exec_repeat_end(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute REPEATEND\n";
VM_LOG(st) << "execute REPEATEND" << (brk ? "BRK" : "");
stack.check_underflow(1);
int c = stack.pop_smallint_range(0x7fffffff, 0x80000000);
if (c <= 0) {
return st->ret();
}
auto cont = st->extract_cc(0);
return st->repeat(std::move(cont), st->get_c0(), c);
return st->repeat(std::move(cont), st->c1_envelope_if(brk, st->get_c0()), c);
}
int exec_until(VmState* st) {
int exec_until(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute UNTIL\n";
VM_LOG(st) << "execute UNTIL" << (brk ? "BRK" : "");
auto cont = stack.pop_cont();
return st->until(std::move(cont), st->extract_cc(1));
return st->until(std::move(cont), st->c1_envelope_if(brk, st->extract_cc(1)));
}
int exec_until_end(VmState* st) {
VM_LOG(st) << "execute UNTILEND\n";
int exec_until_end(VmState* st, bool brk) {
VM_LOG(st) << "execute UNTILEND" << (brk ? "BRK" : "");
auto cont = st->extract_cc(0);
return st->until(std::move(cont), st->get_c0());
return st->until(std::move(cont), st->c1_envelope_if(brk, st->get_c0()));
}
int exec_while(VmState* st) {
int exec_while(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute WHILE\n";
VM_LOG(st) << "execute WHILE" << (brk ? "BRK" : "");
stack.check_underflow(2);
auto body = stack.pop_cont();
auto cond = stack.pop_cont();
return st->loop_while(std::move(cond), std::move(body), st->extract_cc(1));
return st->loop_while(std::move(cond), std::move(body), st->c1_envelope_if(brk, st->extract_cc(1)));
}
int exec_while_end(VmState* st) {
VM_LOG(st) << "execute WHILEEND\n";
int exec_while_end(VmState* st, bool brk) {
VM_LOG(st) << "execute WHILEEND" << (brk ? "BRK" : "");
auto cond = st->get_stack().pop_cont();
auto body = st->extract_cc(0);
return st->loop_while(std::move(cond), std::move(body), st->get_c0());
return st->loop_while(std::move(cond), std::move(body), st->c1_envelope_if(brk, st->get_c0()));
}
int exec_again(VmState* st) {
VM_LOG(st) << "execute AGAIN\n";
int exec_again(VmState* st, bool brk) {
VM_LOG(st) << "execute AGAIN" << (brk ? "BRK" : "");
if (brk) {
st->set_c1(st->extract_cc(3));
}
return st->again(st->get_stack().pop_cont());
}
int exec_again_end(VmState* st) {
VM_LOG(st) << "execute AGAINEND\n";
int exec_again_end(VmState* st, bool brk) {
VM_LOG(st) << "execute AGAINEND" << (brk ? "BRK" : "");
if (brk) {
st->c1_save_set();
}
return st->again(st->extract_cc(0));
}
@ -437,44 +491,70 @@ void register_continuation_cond_loop_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xe0, 8, "IFJMP", exec_if_jmp))
.insert(OpcodeInstr::mksimple(0xe1, 8, "IFNOTJMP", exec_ifnot_jmp))
.insert(OpcodeInstr::mksimple(0xe2, 8, "IFELSE", exec_if_else))
.insert(OpcodeInstr::mkext(
0xe300, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? st->call(std::move(cont)) : 0; },
"IFREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(
0xe301, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? 0 : st->call(std::move(cont)); },
"IFNOTREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(
0xe302, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFJMPREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? st->jump(std::move(cont)) : 0; },
"IFJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(
0xe303, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTJMPREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? 0 : st->jump(std::move(cont)); },
"IFNOTJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe300, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? st->call(st->ref_to_cont(std::move(cell)))
: 0;
},
"IFREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe301, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? 0
: st->call(st->ref_to_cont(std::move(cell)));
},
"IFNOTREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe302, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFJMPREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? st->jump(st->ref_to_cont(std::move(cell)))
: 0;
},
"IFJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe303, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTJMPREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? 0
: st->jump(st->ref_to_cont(std::move(cell)));
},
"IFNOTJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mksimple(0xe304, 16, "CONDSEL", exec_condsel))
.insert(OpcodeInstr::mksimple(0xe305, 16, "CONDSELCHK", exec_condsel_chk))
.insert(OpcodeInstr::mksimple(0xe308, 16, "IFRETALT", exec_ifretalt))
.insert(OpcodeInstr::mksimple(0xe309, 16, "IFNOTRETALT", exec_ifnotretalt))
.insert(OpcodeInstr::mkext(0xe30d, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFREFELSE"),
std::bind(exec_ifelse_ref, _1, _2, _4, true), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe30e, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFELSEREF"),
std::bind(exec_ifelse_ref, _1, _2, _4, false), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe30f, 16, 0, std::bind(dump_push_ref2, _1, _2, _3, "IFREFELSEREF"),
exec_ifref_elseref, compute_len_push_ref2))
.insert(OpcodeInstr::mkfixed(0xe380 >> 6, 10, 6, dump_if_bit_jmp, exec_if_bit_jmp))
.insert(OpcodeInstr::mkext(0xe3c0 >> 6, 10, 6, dump_if_bit_jmpref, exec_if_bit_jmpref, compute_len_push_ref))
.insert(OpcodeInstr::mksimple(0xe4, 8, "REPEAT", exec_repeat))
.insert(OpcodeInstr::mksimple(0xe5, 8, "REPEATEND", exec_repeat_end))
.insert(OpcodeInstr::mksimple(0xe6, 8, "UNTIL", exec_until))
.insert(OpcodeInstr::mksimple(0xe7, 8, "UNTILEND", exec_until_end))
.insert(OpcodeInstr::mksimple(0xe8, 8, "WHILE", exec_while))
.insert(OpcodeInstr::mksimple(0xe9, 8, "WHILEEND", exec_while_end))
.insert(OpcodeInstr::mksimple(0xea, 8, "AGAIN", exec_again))
.insert(OpcodeInstr::mksimple(0xeb, 8, "AGAINEND", exec_again_end));
.insert(OpcodeInstr::mksimple(0xe4, 8, "REPEAT", std::bind(exec_repeat, _1, false)))
.insert(OpcodeInstr::mksimple(0xe5, 8, "REPEATEND", std::bind(exec_repeat_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xe6, 8, "UNTIL", std::bind(exec_until, _1, false)))
.insert(OpcodeInstr::mksimple(0xe7, 8, "UNTILEND", std::bind(exec_until_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xe8, 8, "WHILE", std::bind(exec_while, _1, false)))
.insert(OpcodeInstr::mksimple(0xe9, 8, "WHILEEND", std::bind(exec_while_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xea, 8, "AGAIN", std::bind(exec_again, _1, false)))
.insert(OpcodeInstr::mksimple(0xeb, 8, "AGAINEND", std::bind(exec_again_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xe314, 16, "REPEATBRK", std::bind(exec_repeat, _1, true)))
.insert(OpcodeInstr::mksimple(0xe315, 16, "REPEATENDBRK", std::bind(exec_repeat_end, _1, true)))
.insert(OpcodeInstr::mksimple(0xe316, 16, "UNTILBRK", std::bind(exec_until, _1, true)))
.insert(OpcodeInstr::mksimple(0xe317, 16, "UNTILENDBRK", std::bind(exec_until_end, _1, true)))
.insert(OpcodeInstr::mksimple(0xe318, 16, "WHILEBRK", std::bind(exec_while, _1, true)))
.insert(OpcodeInstr::mksimple(0xe319, 16, "WHILEENDBRK", std::bind(exec_while_end, _1, true)))
.insert(OpcodeInstr::mksimple(0xe31a, 16, "AGAINBRK", std::bind(exec_again, _1, true)))
.insert(OpcodeInstr::mksimple(0xe31b, 16, "AGAINENDBRK", std::bind(exec_again_end, _1, true)));
}
int exec_setcontargs_common(VmState* st, int copy, int more) {
@ -706,6 +786,17 @@ int exec_save_ctr(VmState* st, unsigned args) {
return 0;
}
int exec_samealt(VmState* st, bool save) {
VM_LOG(st) << "execute SAMEALT" << (save ? "SAVE" : "");
auto c0 = st->get_c0();
if (save) {
force_cregs(c0)->define_c1(st->get_c1());
st->set_c0(c0);
}
st->set_c1(std::move(c0));
return 0;
}
int exec_savealt_ctr(VmState* st, unsigned args) {
unsigned idx = args & 15;
VM_LOG(st) << "execute SAVEALTCTR c" << idx;
@ -883,6 +974,8 @@ void register_continuation_change_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xedf7, 16, "THENRETALT", exec_thenret_alt))
.insert(OpcodeInstr::mksimple(0xedf8, 16, "INVERT", exec_invert))
.insert(OpcodeInstr::mksimple(0xedf9, 16, "BOOLEVAL", exec_booleval))
.insert(OpcodeInstr::mksimple(0xedfa, 16, "SAMEALT", std::bind(exec_samealt, _1, false)))
.insert(OpcodeInstr::mksimple(0xedfb, 16, "SAMEALTSAVE", std::bind(exec_samealt, _1, true)))
.insert(OpcodeInstr::mkfixed(0xee, 8, 8, std::bind(dump_setcontargs, _1, _2, "BLESSARGS"), exec_bless_args));
}

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -45,6 +45,7 @@ const char* get_exception_msg(Excno exc_no);
class VmError {
Excno exc_no;
bool msg_alloc = false;
const char* msg;
long long arg;
@ -55,6 +56,18 @@ class VmError {
}
VmError(Excno _excno, const char* _msg, long long _arg) : exc_no(_excno), msg(_msg), arg(_arg) {
}
VmError(Excno _excno, std::string _msg, long long _arg = 0) : exc_no(_excno), msg_alloc(true), arg(_arg) {
msg_alloc = true;
char* p = (char*)malloc(_msg.size() + 1);
memcpy(p, _msg.data(), _msg.size());
p[_msg.size()] = 0;
msg = p;
}
~VmError() {
if (msg_alloc) {
free(const_cast<char*>(msg));
}
}
int get_errno() const {
return static_cast<int>(exc_no);
}

View file

@ -333,6 +333,22 @@ int VmState::ret_alt(int ret_args) {
return jump(std::move(cont), ret_args);
}
Ref<Continuation> VmState::c1_envelope(Ref<Continuation> cont, bool save) {
if (save) {
force_cregs(cont)->define_c1(cr.c[1]);
force_cregs(cont)->define_c0(cr.c[0]);
}
set_c1(cont);
return cont;
}
void VmState::c1_save_set(bool save) {
if (save) {
force_cregs(cr.c[0])->define_c1(cr.c[1]);
}
set_c1(cr.c[0]);
}
Ref<OrdCont> VmState::extract_cc(int save_cr, int stack_copy, int cc_args) {
Ref<Stack> new_stk;
if (stack_copy < 0 || stack_copy == stack->depth()) {

View file

@ -297,6 +297,11 @@ class VmState final : public VmStateInterface {
int throw_exception(int excno, StackEntry&& arg);
int throw_exception(int excno);
Ref<OrdCont> extract_cc(int save_cr = 1, int stack_copy = -1, int cc_args = -1);
Ref<Continuation> c1_envelope(Ref<Continuation> cont, bool save = true);
Ref<Continuation> c1_envelope_if(bool cond, Ref<Continuation> cont, bool save = true) {
return cond ? c1_envelope(std::move(cont), save) : std::move(cont);
}
void c1_save_set(bool save = true);
void fatal(void) const {
throw VmFatal{};
}
@ -313,6 +318,9 @@ class VmState final : public VmStateInterface {
bool get_chksig_always_succeed() const {
return chksig_always_succeed;
}
Ref<OrdCont> ref_to_cont(Ref<Cell> cell) const {
return td::make_ref<OrdCont>(load_cell_slice_ref(std::move(cell)), get_cp());
}
private:
void init_cregs(bool same_c3 = false, bool push_0 = true);

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "dht-server.hpp"
@ -70,8 +70,12 @@ Config::Config(ton::ton_api::engine_validator_config &config) {
[&](const ton::ton_api::engine_addr &obj) {
in_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.ip_), static_cast<td::uint16>(obj.port_)).ensure();
out_ip = in_ip;
categories = obj.categories_;
priority_categories = obj.priority_categories_;
for (auto &cat : obj.categories_) {
categories.push_back(td::narrow_cast<td::uint8>(cat));
}
for (auto &cat : obj.priority_categories_) {
priority_categories.push_back(td::narrow_cast<td::uint8>(cat));
}
},
[&](const ton::ton_api::engine_addrProxy &obj) {
in_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.in_ip_), static_cast<td::uint16>(obj.in_port_))
@ -82,15 +86,19 @@ Config::Config(ton::ton_api::engine_validator_config &config) {
auto R = ton::adnl::AdnlProxy::create(*obj.proxy_type_.get());
R.ensure();
proxy = R.move_as_ok();
categories = obj.categories_;
priority_categories = obj.priority_categories_;
for (auto &cat : obj.categories_) {
categories.push_back(td::narrow_cast<td::uint8>(cat));
}
for (auto &cat : obj.priority_categories_) {
priority_categories.push_back(td::narrow_cast<td::uint8>(cat));
}
}
}));
config_add_network_addr(in_ip, out_ip, std::move(proxy), categories, priority_categories).ensure();
}
for (auto &adnl : config.adnl_) {
config_add_adnl_addr(ton::PublicKeyHash{adnl->id_}, adnl->category_).ensure();
config_add_adnl_addr(ton::PublicKeyHash{adnl->id_}, td::narrow_cast<td::uint8>(adnl->category_)).ensure();
}
for (auto &dht : config.dht_) {
config_add_dht_node(ton::PublicKeyHash{dht->id_}).ensure();
@ -448,7 +456,7 @@ void DhtServer::load_empty_local_config(td::Promise<td::Unit> promise) {
ig.add_promise(std::move(ret_promise));
for (auto &addr : addrs_) {
config_.config_add_network_addr(addr, addr, nullptr, std::vector<td::int32>{0, 1, 2, 3}, std::vector<td::int32>{})
config_.config_add_network_addr(addr, addr, nullptr, std::vector<td::int8>{0, 1, 2, 3}, std::vector<td::int8>{})
.ensure();
}
@ -501,7 +509,7 @@ void DhtServer::load_local_config(td::Promise<td::Unit> promise) {
ig.add_promise(std::move(ret_promise));
for (auto &addr : addrs_) {
config_.config_add_network_addr(addr, addr, nullptr, std::vector<td::int32>{0, 1, 2, 3}, std::vector<td::int32>{})
config_.config_add_network_addr(addr, addr, nullptr, std::vector<td::int8>{0, 1, 2, 3}, std::vector<td::int8>{})
.ensure();
}
@ -656,12 +664,20 @@ void DhtServer::start_adnl() {
}
void DhtServer::add_addr(const Config::Addr &addr, const Config::AddrCats &cats) {
ton::adnl::AdnlCategoryMask cat_mask;
for (auto cat : cats.cats) {
cat_mask[cat] = true;
}
for (auto cat : cats.priority_cats) {
cat_mask[cat] = true;
}
if (!cats.proxy) {
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr.addr,
cats.cats.size() ? 0 : 1);
std::move(cat_mask), cats.cats.size() ? 0 : 1);
} else {
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_proxy_addr, addr.addr,
cats.proxy, cats.cats.size() ? 0 : 1);
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_proxy_addr, cats.in_addr,
static_cast<td::uint16>(addr.addr.get_port()), cats.proxy, std::move(cat_mask),
cats.cats.size() ? 0 : 1);
}
td::uint32 ts = static_cast<td::uint32>(td::Clocks::system());
@ -687,7 +703,7 @@ void DhtServer::add_addr(const Config::Addr &addr, const Config::AddrCats &cats)
void DhtServer::add_adnl(ton::PublicKeyHash id, AdnlCategory cat) {
CHECK(addr_lists_[cat].size() > 0);
CHECK(keys_.count(id) > 0);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, addr_lists_[cat]);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, addr_lists_[cat], cat);
}
void DhtServer::started_adnl() {
@ -738,7 +754,7 @@ void DhtServer::start_control_interface() {
for (auto &s : config_.controls) {
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[s.second.key]},
ton::adnl::AdnlAddressList{});
ton::adnl::AdnlAddressList{}, static_cast<td::uint8>(255));
td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, ton::adnl::AdnlNodeIdShort{s.second.key},
std::string(""), std::make_unique<Callback>(actor_id(this)));
@ -785,7 +801,7 @@ void DhtServer::add_adnl_node(ton::PublicKey key, AdnlCategory cat, td::Promise<
}
if (!adnl_.empty()) {
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{key}, addr_lists_[cat]);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{key}, addr_lists_[cat], cat);
}
write_config(std::move(promise));
@ -970,23 +986,25 @@ void DhtServer::run_control_query(ton::ton_api::engine_validator_addAdnlId &quer
return;
}
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), cat = query.category_,
promise = std::move(promise)](td::Result<ton::PublicKey> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: ")));
return;
}
auto pub = R.move_as_ok();
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add adnl node: ")));
} else {
promise.set_value(
ton::serialize_tl_object(ton::create_tl_object<ton::ton_api::engine_validator_success>(), true));
}
});
td::actor::send_closure(SelfId, &DhtServer::add_adnl_node, pub, cat, std::move(P));
});
TRY_RESULT_PROMISE(promise, cat, td::narrow_cast_safe<td::uint8>(query.category_));
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), cat, promise = std::move(promise)](td::Result<ton::PublicKey> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: ")));
return;
}
auto pub = R.move_as_ok();
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add adnl node: ")));
} else {
promise.set_value(
ton::serialize_tl_object(ton::create_tl_object<ton::ton_api::engine_validator_success>(), true));
}
});
td::actor::send_closure(SelfId, &DhtServer::add_adnl_node, pub, cat, std::move(P));
});
td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, ton::PublicKeyHash{query.key_hash_},
std::move(P));

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl/adnl.h"
#include "dht/dht.h"
@ -40,7 +40,7 @@
enum DhtServerPermissions : td::uint32 { vep_default = 1, vep_modify = 2, vep_unsafe = 4 };
using AdnlCategory = td::int32;
using AdnlCategory = td::int8;
struct Config {
struct Addr {
@ -177,7 +177,7 @@ class DhtServer : public td::actor::Actor {
void alarm() override;
void run();
void add_adnl_node(ton::PublicKey pub, td::int32 cat, td::Promise<td::Unit> promise);
void add_adnl_node(ton::PublicKey pub, AdnlCategory cat, td::Promise<td::Unit> promise);
void add_dht_node(ton::PublicKeyHash pub, td::Promise<td::Unit> promise);
void add_control_interface(ton::PublicKeyHash id, td::int32 port, td::Promise<td::Unit> promise);
void add_control_process(ton::PublicKeyHash id, td::int32 port, ton::PublicKeyHash pub, td::int32 permissions,
@ -214,4 +214,3 @@ class DhtServer : public td::actor::Actor {
void process_control_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise);
};

View file

@ -1843,7 +1843,7 @@ All these primitives first check whether there is enough space in the Builder, a
\item {\tt E0} --- {\tt IFJMP} ($f$ $c$ -- ), jumps to $c$ (similarly to {\tt JMPX}), but only if $f$ is non-zero.
\item {\tt E1} --- {\tt IFNOTJMP} ($f$ $c$ -- ), jumps to $c$ (similarly to {\tt JMPX}), but only if $f$ is zero.
\item {\tt E2} --- {\tt IFELSE} ($f$ $c$ $c'$ -- ), if integer $f$ is non-zero, executes $c$, otherwise executes $c'$. Equivalent to {\tt CONDSELCHK}; {\tt EXECUTE}.
\item {\tt E300} --- {\tt IFREF} ($f$ -- ), equivalent to {\tt PUSHREFCONT}; {\tt IF}.
\item {\tt E300} --- {\tt IFREF} ($f$ -- ), equivalent to {\tt PUSHREFCONT}; {\tt IF}, with the optimization that the cell reference is not actually loaded into a {\em Slice} and then converted into an ordinary {\em Continuation\/} if $f=0$. Similar remarks apply to the next three primitives.
\item {\tt E301} --- {\tt IFNOTREF} ($f$ -- ), equivalent to {\tt PUSHREFCONT}; {\tt IFNOT}.
\item {\tt E302} --- {\tt IFJMPREF} ($f$ -- ), equivalent to {\tt PUSHREFCONT}; {\tt IFJMP}.
\item {\tt E303} --- {\tt IFNOTJMPREF} ($f$ -- ), equivalent to {\tt PUSHREFCONT}; {\tt IFNOTJMP}.
@ -1851,14 +1851,18 @@ All these primitives first check whether there is enough space in the Builder, a
\item {\tt E305} --- {\tt CONDSELCHK} ($f$ $x$ $y$ -- $x$ or $y$), same as {\tt CONDSEL}, but first checks whether $x$ and $y$ have the same type.
\item {\tt E308} --- {\tt IFRETALT} ($f$ --), performs {\tt RETALT} if integer $f\neq0$.
\item {\tt E309} --- {\tt IFNOTRETALT} ($f$ --), performs {\tt RETALT} if integer $f=0$.
\item {\tt E30D} --- {\tt IFREFELSE} ($f$ $c$ --), equivalent to {\tt PUSHREFCONT}; {\tt SWAP}; {\tt IFELSE}, with the optimization that the cell reference is not actually loaded into a {\em Slice} and then converted into an ordinary {\em Continuation\/} if $f=0$. Similar remarks apply to the next two primitives: {\em Cell\/}s are converted into {\em Continuation\/}s only when necessary.
\item {\tt E30E} --- {\tt IFELSEREF} ($f$ $c$ --), equivalent to {\tt PUSHREFCONT}; {\tt IFELSE}.
\item {\tt E30F} --- {\tt IFREFELSEREF} ($f$ --), equivalent to {\tt PUSHREFCONT}; {\tt PUSHREFCONT}; {\tt IFELSE}.
\item {\tt E310}--{\tt E31F} --- reserved for loops with break operators, cf.~\ptref{sp:prim.loop} below.
\item {\tt E39\_$n$} --- {\tt IFBITJMP $n$} ($x$ $c$ -- $x$), checks whether bit $0\leq n\leq 31$ is set in integer $x$, and if so, performs {\tt JMPX} to continuation~$c$. Value $x$ is left in the stack.
\item {\tt E3B\_$n$} --- {\tt IFNBITJMP $n$} ($x$ $c$ -- $x$), jumps to $c$ if bit $0\leq n\leq 31$ is not set in integer~$x$.
\item {\tt E3D\_$n$} --- {\tt IFBITJMPREF $n$} ($x$ -- $x$), performs a {\tt JMPREF} if bit $0\leq n\leq 31$ is set in integer~$x$.
\item {\tt E3F\_$n$} --- {\tt IFNBITJMPREF $n$} ($x$ -- $x$), performs a {\tt JMPREF} if bit $0\leq n\leq 31$ is not set in integer~$x$.
\end{itemize}
\nxsubpoint\emb{Control flow primitives: loops}
Most of the loop primitives listed below are implemented with the aid of extraordinary continuations, such as {\tt ec\_until} (cf.~\ptref{sp:extraord.cont}), with the loop body and the original current continuation {\tt cc} stored as the arguments to this extraordinary continuation. Typically a suitable extraordinary continuation is constructed, and then saved into the loop body continuation savelist as {\tt c0}; after that, the modified loop body continuation is loaded into {\tt cc} and executed in the usual fashion.
\nxsubpoint\emb{Control flow primitives: loops}\label{sp:prim.loop}
Most of the loop primitives listed below are implemented with the aid of extraordinary continuations, such as {\tt ec\_until} (cf.~\ptref{sp:extraord.cont}), with the loop body and the original current continuation {\tt cc} stored as the arguments to this extraordinary continuation. Typically a suitable extraordinary continuation is constructed, and then saved into the loop body continuation savelist as {\tt c0}; after that, the modified loop body continuation is loaded into {\tt cc} and executed in the usual fashion. All of these loop primitives have {\tt *BRK} versions, adapted for breaking out of a loop; they additionally set {\tt c1} to the original current continuation (or original {\tt c0} for {\tt *ENDBRK} versions), and save the old {\tt c1} into the savelist of the original current continuation (or of the original {\tt c0} for {\tt *ENDBRK} versions).
\begin{itemize}
\item {\tt E4} --- {\tt REPEAT} ($n$ $c$ -- ), executes continuation $c$ $n$ times, if integer $n$ is non-negative. If $n\geq2^{31}$ or $n<-2^{31}$, generates a range check exception. Notice that a {\tt RET} inside the code of $c$ works as a {\tt continue}, not as a {\tt break}. One should use either alternative (experimental) loops or alternative {\tt RETALT} (along with a {\tt SETEXITALT} before the loop) to {\tt break} out of a loop.
\item {\tt E5} --- {\tt REPEATEND} ($n$ -- ), similar to {\tt REPEAT}, but it is applied to the current continuation {\tt cc}.
@ -1868,6 +1872,14 @@ Most of the loop primitives listed below are implemented with the aid of extraor
\item {\tt E9} --- {\tt WHILEEND} ($c'$ -- ), similar to {\tt WHILE}, but uses the current continuation {\tt cc} as the loop body.
\item {\tt EA} --- {\tt AGAIN} ($c$ -- ), similar to {\tt REPEAT}, but executes $c$ infinitely many times. A {\tt RET} only begins a new iteration of the infinite loop, which can be exited only by an exception, or a {\tt RETALT} (or an explicit {\tt JMPX}).
\item {\tt EB} --- {\tt AGAINEND} ( -- ), similar to {\tt AGAIN}, but performed with respect to the current continuation {\tt cc}.
\item {\tt E314} --- {\tt REPEATBRK} ($n$ $c$ -- ), similar to {\tt REPEAT}, but also sets {\tt c1} to the original {\tt cc} after saving the old value of {\tt c1} into the savelist of the original {\tt cc}. In this way {\tt RETALT} could be used to break out of the loop body.
\item {\tt E315} --- {\tt REPEATENDBRK} ($n$ -- ), similar to {\tt REPEATEND}, but also sets {\tt c1} to the original {\tt c0} after saving the old value of {\tt c1} into the savelist of the original {\tt c0}. Equivalent to {\tt SAMEALTSAVE}; {\tt REPEATEND}.
\item {\tt E316} --- {\tt UNTILBRK} ($c$ -- ), similar to {\tt UNTIL}, but also modifies {\tt c1} in the same way as {\tt REPEATBRK}.
\item {\tt E317} --- {\tt UNTILENDBRK} ( -- ), equivalent to {\tt SAMEALTSAVE}; {\tt UNTILEND}.
\item {\tt E318} --- {\tt WHILEBRK} ($c'$ $c$ -- ), similar to {\tt WHILE}, but also modifies {\tt c1} in the same way as {\tt REPEATBRK}.
\item {\tt E319} --- {\tt WHILEENDBRK} ($c$ -- ), equivalent to {\tt SAMEALTSAVE}; {\tt WHILEEND}.
\item {\tt E31A} --- {\tt AGAINBRK} ($c$ -- ), similar to {\tt AGAIN}, but also modifies {\tt c1} in the same way as {\tt REPEATBRK}.
\item {\tt E31B} --- {\tt AGAINENDBRK} ( -- ), equivalent to {\tt SAMEALTSAVE}; {\tt AGAINEND}.
\end{itemize}
\nxsubpoint\label{sp:cont.stk.manip}\emb{Manipulating the stack of continuations}
@ -1914,7 +1926,9 @@ Most of the loop primitives listed below are implemented with the aid of extraor
\item {\tt EDF6} --- {\tt THENRET} ($c$ -- $c'$), computes $c':=c\circ_0{\tt c0}$
\item {\tt EDF7} --- {\tt THENRETALT} ($c$ -- $c'$), computes $c':=c\circ_0{\tt c1}$
\item {\tt EDF8} --- {\tt INVERT} ( -- ), interchanges {\tt c0} and {\tt c1}.
\item {\tt EDF9} --- {\tt BOOLEVAL} ($c$ -- $?$), performs ${\tt cc}\leftarrow \bigl(c\circ_0(({\tt PUSH}\,-1)\circ_0{\tt cc})\bigr)\circ_1(({\tt PUSH}\,0)\circ_0{\tt cc})$. If $c$ represents a boolean circuit, the net effect is to evaluate it and push either $-1$ or $0$ into the stack before continuing.
\item {\tt EDF9} --- {\tt BOOLEVAL} ($c$ -- $?$), performs ${\tt cc}\leftarrow \bigl(c\circ_0(({\tt PUSH}\,-1)\circ_0{\tt cc})\bigr)\circ_1(({\tt PUSH}\,0)\circ_0{\tt cc})$. If $c$ represents a boolean circuit, the net effect is to evaluate it and push either $-1$ or $0$ into the stack before continuing.
\item {\tt EDFA} --- {\tt SAMEALT} ( -- ), sets $c_1:=c_0$. Equivalent to {\tt PUSH c0}; {\tt POP c1}.
\item {\tt EDFB} --- {\tt SAMEALTSAVE} ( -- ), sets $c_1:=c_0$, but first saves the old value of $c_1$ into the savelist of $c_0$. Equivalent to {\tt SAVE c1}; {\tt SAMEALT}.
\item {\tt EE$rn$} --- {\tt BLESSARGS $r,n$} ($x_1$\dots$x_r$ $s$ -- $c$), described in~\ptref{sp:cont.stk.manip}.
\end{itemize}

View file

@ -69,7 +69,7 @@ class TonTest {
val giverInputKey = TonApi.InputKeyRegular(giverKey, "local password".toByteArray())
val giverAddress = client.send(TonApi.GetAccountAddress(TonApi.WalletV3InitialAccountState(giverKey.publicKey, info.configInfo.defaultWalletId), 1)) as TonApi.AccountAddress;
val queryInfo = client.send(TonApi.CreateQuery(giverInputKey, giverAddress, 60, TonApi.ActionMsg(arrayOf(TonApi.MsgMessage(walletAddress, 6660000000, TonApi.MsgDataText("Helo") )), true))) as TonApi.QueryInfo;
val queryInfo = client.send(TonApi.CreateQuery(giverInputKey, giverAddress, 60, TonApi.ActionMsg(arrayOf(TonApi.MsgMessage(walletAddress, inputKey.key.publicKey, 6660000000, TonApi.MsgDataDecryptedText("Helo".toByteArray()) )), true))) as TonApi.QueryInfo;
client.send(TonApi.QuerySend(queryInfo.id)) as TonApi.Ok;
while ((client.send(TonApi.GetAccountState(walletAddress)) as TonApi.FullAccountState).balance <= 0L) {

View file

@ -102,7 +102,7 @@ public class TonTestJava {
TonApi.AccountAddress giverAddress = (TonApi.AccountAddress)client.send(new TonApi.GetAccountAddress(new TonApi.WalletV3InitialAccountState(giverKey.publicKey, info.configInfo.defaultWalletId), 1));
appendLog("sending grams...");
TonApi.QueryInfo queryInfo = (TonApi.QueryInfo)client.send(new TonApi.CreateQuery(giverInputKey, giverAddress, 60, new TonApi.ActionMsg(new TonApi.MsgMessage[]{new TonApi.MsgMessage(walletAddress, 6660000000L, new TonApi.MsgDataText("Helo") )}, true)));
TonApi.QueryInfo queryInfo = (TonApi.QueryInfo)client.send(new TonApi.CreateQuery(giverInputKey, giverAddress, 60, new TonApi.ActionMsg(new TonApi.MsgMessage[]{new TonApi.MsgMessage(walletAddress, "", 6660000000L, new TonApi.MsgDataText("Hello".getBytes()) )}, true)));
result = client.send(new TonApi.QuerySend(queryInfo.id));
if (!(result instanceof TonApi.Ok)) {
appendLog("failed to send grams");

View file

@ -777,12 +777,16 @@ class RldpHttpProxy : public td::actor::Actor {
ton::adnl::AdnlNetworkManager::create(is_client_ ? client_port_ : static_cast<td::uint16>(addr_.get_port()));
adnl_ = ton::adnl::Adnl::create(is_client_ ? std::string("") : (db_root_), keyring_.get());
td::actor::send_closure(adnl_, &ton::adnl::Adnl::register_network_manager, adnl_network_manager_.get());
ton::adnl::AdnlCategoryMask cat_mask;
cat_mask[0] = true;
if (is_client_) {
td::IPAddress addr;
addr.init_host_port("127.0.0.1", client_port_).ensure();
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr, 0);
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr,
std::move(cat_mask), 0);
} else {
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr_, 0);
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr_,
std::move(cat_mask), 0);
}
ton::adnl::AdnlAddressList addr_list;
@ -798,7 +802,8 @@ class RldpHttpProxy : public td::actor::Actor {
auto pub = pk.compute_public_key();
td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), true, [](td::Unit) {});
local_id_ = ton::adnl::AdnlNodeIdShort{pub.compute_short_id()};
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, addr_list);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, addr_list,
static_cast<td::uint8>(0));
if (server_ids_.size() == 0 && !is_client_) {
server_ids_.insert(local_id_);
@ -809,10 +814,12 @@ class RldpHttpProxy : public td::actor::Actor {
auto pub = pk.compute_public_key();
td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), true, [](td::Unit) {});
dht_id_ = ton::adnl::AdnlNodeIdShort{pub.compute_short_id()};
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, addr_list);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, addr_list,
static_cast<td::uint8>(0));
}
for (auto &serv_id : server_ids_) {
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, server_ids_full_[serv_id], addr_list);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, server_ids_full_[serv_id], addr_list,
static_cast<td::uint8>(0));
}
}
{

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/db/RocksDb.h"
@ -162,30 +162,32 @@ Result<size_t> RocksDb::count(Slice prefix) {
}
Status RocksDb::begin_transaction() {
write_batch_ = std::make_unique<rocksdb::WriteBatch>();
//transaction_.reset(db_->BeginTransaction({}, {}));
//write_batch_ = std::make_unique<rocksdb::WriteBatch>();
rocksdb::WriteOptions options;
options.sync = true;
transaction_.reset(db_->BeginTransaction(options, {}));
return Status::OK();
}
Status RocksDb::commit_transaction() {
CHECK(write_batch_);
auto write_batch = std::move(write_batch_);
rocksdb::WriteOptions options;
options.sync = true;
TRY_STATUS(from_rocksdb(db_->Write(options, write_batch.get())));
return Status::OK();
//CHECK(write_batch_);
//auto write_batch = std::move(write_batch_);
//rocksdb::WriteOptions options;
//options.sync = true;
//TRY_STATUS(from_rocksdb(db_->Write(options, write_batch.get())));
//return Status::OK();
//CHECK(transaction_);
//auto res = from_rocksdb(transaction_->Commit());
//transaction_.reset();
//return res;
CHECK(transaction_);
auto res = from_rocksdb(transaction_->Commit());
transaction_.reset();
return res;
}
Status RocksDb::abort_transaction() {
CHECK(write_batch_);
write_batch_.reset();
//CHECK(transaction_);
//transaction_.reset();
//CHECK(write_batch_);
//write_batch_.reset();
CHECK(transaction_);
transaction_.reset();
return Status::OK();
}

View file

@ -32,7 +32,11 @@ TEST(KeyValue, simple) {
td::RocksDb::destroy(db_name).ignore();
std::unique_ptr<td::KeyValue> kv = std::make_unique<td::RocksDb>(td::RocksDb::open(db_name.str()).move_as_ok());
auto set_value = [&](td::Slice key, td::Slice value) { kv->set(key, value); };
auto set_value = [&](td::Slice key, td::Slice value) {
kv->begin_transaction();
kv->set(key, value);
kv->commit_transaction();
};
auto ensure_value = [&](td::Slice key, td::Slice value) {
std::string kv_value;
auto status = kv->get(key, kv_value).move_as_ok();

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/utils/crypto.h"
@ -299,16 +299,16 @@ void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlic
aes_cbc_xcrypt(aes_key, aes_iv, from, to, false);
}
AesCbcState::AesCbcState(Slice key256, Slice iv128) : key_(key256), iv_(iv128) {
CHECK(key_.size() == 32);
CHECK(iv_.size() == 16);
AesCbcState::AesCbcState(Slice key256, Slice iv128) : raw_{td::SecureString(key256), td::SecureString(iv128)} {
CHECK(raw_.key.size() == 32);
CHECK(raw_.iv.size() == 16);
}
void AesCbcState::encrypt(Slice from, MutableSlice to) {
::td::aes_cbc_encrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
::td::aes_cbc_encrypt(raw_.key.as_slice(), raw_.iv.as_mutable_slice(), from, to);
}
void AesCbcState::decrypt(Slice from, MutableSlice to) {
::td::aes_cbc_decrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
::td::aes_cbc_decrypt(raw_.key.as_slice(), raw_.iv.as_mutable_slice(), from, to);
}
class AesCtrState::Impl {

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -65,10 +65,17 @@ class AesCbcState {
void encrypt(Slice from, MutableSlice to);
void decrypt(Slice from, MutableSlice to);
struct Raw {
SecureString key;
SecureString iv;
};
const Raw &raw() const {
return raw_;
}
private:
SecureString key_;
SecureString iv_;
Raw raw_;
};
void sha1(Slice data, unsigned char output[20]);

View file

@ -29,6 +29,8 @@
#include "adnl/adnl.h"
#include "adnl/adnl-test-loopback-implementation.h"
#include "keys/encryptor.h"
#include "td/utils/port/signals.h"
#include "td/utils/port/path.h"
#include "td/utils/Random.h"
@ -81,8 +83,10 @@ int main() {
auto addr = ton::adnl::TestLoopbackNetworkManager::generate_dummy_addr_list();
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr);
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub2}, addr);
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr,
static_cast<td::uint8>(0));
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub2}, addr,
static_cast<td::uint8>(0));
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_peer, src, ton::adnl::AdnlNodeIdFull{pub2}, addr);
@ -91,30 +95,85 @@ int main() {
});
{
auto a = ton::adnl::Adnl::adnl_start_time();
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
CHECK(a == ton::adnl::Adnl::adnl_start_time());
}
{
auto obj = ton::create_tl_object<ton::ton_api::adnl_proxy_fast>(td::BufferSlice{"1234"});
td::Bits256 proxy_id;
td::Random::secure_bytes(proxy_id.as_slice());
auto obj = ton::create_tl_object<ton::ton_api::adnl_proxy_fast>(proxy_id, td::BufferSlice{"1234"});
auto R = ton::adnl::AdnlProxy::create(*obj.get());
R.ensure();
auto P = R.move_as_ok();
td::BufferSlice z{64};
td::Random::secure_bytes(z.as_slice());
auto packet = P->encrypt(ton::adnl::AdnlProxy::Packet{2, 3, z.clone()});
td::Bits256 x;
x.as_slice().copy_from(packet.as_slice().truncate(32));
CHECK(x.is_zero());
auto packet = P->encrypt(ton::adnl::AdnlProxy::Packet{15, 2, 3, 4, 5, 6, z.clone()});
auto packet2R = P->decrypt(std::move(packet));
packet2R.ensure();
auto packet2 = packet2R.move_as_ok();
CHECK(packet2.flags == 15);
CHECK(packet2.ip == 2);
CHECK(packet2.port == 3);
CHECK(packet2.adnl_start_time == 4);
CHECK(packet2.seqno == 5);
CHECK(packet2.date == 6);
CHECK(packet2.data.as_slice() == z.as_slice());
}
{
auto f = td::Clocks::system();
for (int i = 0; i < 10000; i++) {
auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()};
auto pub = pk.compute_public_key();
auto dec = pk.create_decryptor().move_as_ok();
auto enc = pub.create_encryptor().move_as_ok();
td::BufferSlice data{1024};
td::Random::secure_bytes(data.as_slice());
auto enc_data = enc->encrypt(data.as_slice()).move_as_ok();
auto dec_data = dec->decrypt(enc_data.as_slice()).move_as_ok();
CHECK(data.as_slice() == dec_data.as_slice());
}
LOG(ERROR) << "Encrypted 10000 of 1KiB packets. Time=" << (td::Clocks::system() - f);
f = td::Clocks::system();
{
auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()};
auto pub = pk.compute_public_key();
auto dec = pk.create_decryptor().move_as_ok();
auto enc = pub.create_encryptor().move_as_ok();
for (int i = 0; i < 10000; i++) {
td::BufferSlice data{1024};
td::Random::secure_bytes(data.as_slice());
auto enc_data = enc->encrypt(data.as_slice()).move_as_ok();
auto dec_data = dec->decrypt(enc_data.as_slice()).move_as_ok();
CHECK(data.as_slice() == dec_data.as_slice());
}
}
LOG(ERROR) << "Encrypted 10000 of 1KiB packets with one key. Time=" << (td::Clocks::system() - f);
f = td::Clocks::system();
for (int i = 0; i < 10000; i++) {
auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()};
auto pub = pk.compute_public_key();
auto dec = pk.create_decryptor().move_as_ok();
auto enc = pub.create_encryptor().move_as_ok();
td::BufferSlice data{1024};
td::Random::secure_bytes(data.as_slice());
auto enc_data = enc->encrypt(data.as_slice()).move_as_ok();
auto dec_data = dec->decrypt(enc_data.as_slice()).move_as_ok();
CHECK(data.as_slice() == dec_data.as_slice());
}
LOG(ERROR) << "Signed 10000 of 1KiB packets. Time=" << (td::Clocks::system() - f);
f = td::Clocks::system();
{
auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()};
auto pub = pk.compute_public_key();
auto dec = pk.create_decryptor().move_as_ok();
auto enc = pub.create_encryptor().move_as_ok();
for (int i = 0; i < 10000; i++) {
td::BufferSlice data{1024};
td::Random::secure_bytes(data.as_slice());
auto signature = dec->sign(data.as_slice()).move_as_ok();
enc->check_signature(data.as_slice(), signature.as_slice()).ensure();
}
}
LOG(ERROR) << "Signed 10000 of 1KiB packets with one key. Time=" << (td::Clocks::system() - f);
}
auto send_packet = [&](td::uint32 i) {
td::BufferSlice d{i};
d.as_slice()[0] = '1';
@ -159,6 +218,7 @@ int main() {
td::actor::send_closure(adnl, &ton::adnl::Adnl::subscribe, dst, "1", std::make_unique<Callback>(remaining));
});
LOG(ERROR) << "Ed25519 version is " << td::Ed25519::version();
LOG(ERROR) << "testing delivering of all packets";
auto f = td::Clocks::system();
@ -324,6 +384,12 @@ int main() {
}
LOG(ERROR) << "successfully tested ignoring";
if (true) {
auto a = ton::adnl::Adnl::adnl_start_time();
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
CHECK(a == ton::adnl::Adnl::adnl_start_time());
}
td::rmrf(db_root_).ensure();
std::_Exit(0);
return 0;

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl/adnl.h"
#include "adnl/utils.hpp"
@ -250,7 +250,8 @@ int main(int argc, char *argv[]) {
n.adnl_id_full = ton::adnl::AdnlNodeIdFull{pub1};
n.adnl_id = ton::adnl::AdnlNodeIdShort{pub1.compute_short_id()};
td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk1), true, [](td::Unit) {});
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr);
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr,
static_cast<td::uint8>(0));
td::actor::send_closure(network_manager, &ton::adnl::TestLoopbackNetworkManager::add_node_id, n.adnl_id, true,
true);

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl/adnl-network-manager.h"
#include "adnl/adnl-test-loopback-implementation.h"
@ -87,7 +87,8 @@ int main() {
dht_config = dht_configR.move_as_ok();
}
td::actor::send_closure(keyring, &ton::keyring::Keyring::add_key, std::move(pk1), true, [](td::Unit) {});
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr);
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr,
static_cast<td::uint8>(0));
td::actor::send_closure(network_manager, &ton::adnl::TestLoopbackNetworkManager::add_node_id, src, true, true);
dht.push_back(ton::dht::Dht::create(src, db_root_, dht_config, keyring.get(), adnl.get()).move_as_ok());

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl/adnl-network-manager.h"
#include "adnl/adnl-test-loopback-implementation.h"
@ -75,8 +75,10 @@ int main() {
auto addr = ton::adnl::TestLoopbackNetworkManager::generate_dummy_addr_list();
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr);
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub2}, addr);
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub1}, addr,
static_cast<td::uint8>(0));
td::actor::send_closure(adnl, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub2}, addr,
static_cast<td::uint8>(0));
td::actor::send_closure(rldp, &ton::rldp::Rldp::add_id, src);
td::actor::send_closure(rldp, &ton::rldp::Rldp::add_id, dst);

View file

@ -58,8 +58,8 @@ adnl.id.short id:int256 = adnl.id.Short;
adnl.proxyToFastHash ip:int port:int date:int data_hash:int256 shared_secret:int256 = adnl.ProxyTo;
adnl.proxyToFast ip:int port:int date:int signature:int256 = adnl.ProxyToSign;
adnl.proxy.none = adnl.Proxy;
adnl.proxy.fast shared_secret:bytes = adnl.Proxy;
adnl.proxy.none id:int256 = adnl.Proxy;
adnl.proxy.fast id:int256 shared_secret:bytes = adnl.Proxy;
adnl.address.udp ip:int port:int = adnl.Address;
@ -109,6 +109,23 @@ adnl.tunnelPacketContents
= adnl.TunnelPacketContents;
// flag 16 - packet is outbound
// flag 17 - control packet
adnl.proxyPacketHeader
proxy_id:int256
flags:#
ip:flags.0?int
port:flags.0?int
adnl_start_time:flags.1?int
seqno:flags.2?long
date:flags.3?int
signature:int256 = adnl.ProxyPacketHeader;
adnl.proxyControlPacketPing id:int256 = adnl.ProxyControlPacket;
adnl.proxyControlPacketPong id:int256 = adnl.ProxyControlPacket;
adnl.proxyControlPacketRegister ip:int port:int = adnl.ProxyControlPacket;
adnl.message.createChannel key:int256 date:int = adnl.Message;
adnl.message.confirmChannel key:int256 peer_key:int256 date:int = adnl.Message;

Binary file not shown.

View file

@ -51,7 +51,7 @@ ton.blockId workchain:int32 shard:int64 seqno:int32 = internal.BlockId;
ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt;
raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState;
raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message;
raw.message source:accountAddress destination:accountAddress value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message;
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
@ -88,7 +88,11 @@ msg.dataText text:bytes = msg.Data;
msg.dataDecryptedText text:bytes = msg.Data;
msg.dataEncryptedText text:bytes = msg.Data;
msg.dataArray elements:vector<msg.Data> = msg.DataArray;
msg.dataEncrypted source:accountAddress data:msg.Data = msg.DataEncrypted;
msg.dataDecrypted proof:bytes data:msg.Data = msg.DataDecrypted;
msg.dataEncryptedArray elements:vector<msg.dataEncrypted> = msg.DataEncryptedArray;
msg.dataDecryptedArray elements:vector<msg.dataDecrypted> = msg.DataDecryptedArray;
msg.message destination:accountAddress public_key:string amount:int64 data:msg.Data = msg.Message;
@ -216,7 +220,8 @@ guessAccountRevision initial_account_state:InitialAccountState = AccountRevision
getAccountState account_address:accountAddress = FullAccountState;
createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action = query.Info;
msg.decrypt input_key:InputKey data:msg.dataArray = msg.DataArray;
msg.decrypt input_key:InputKey data:msg.dataEncryptedArray = msg.DataDecryptedArray;
msg.decryptWithProof proof:bytes data:msg.dataEncrypted = msg.Data;
query.send id:int53 = Ok;
query.forget id:int53 = Ok;

Binary file not shown.

View file

@ -14,7 +14,7 @@
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
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -56,7 +56,13 @@ constexpr unsigned min_split_merge_interval = 30; // split/merge interval must
constexpr unsigned max_split_merge_delay =
1000; // end of split/merge interval must be at most 1000 seconds in the future
enum GlobalCapabilities { capIhrEnabled = 1, capCreateStatsEnabled = 2 };
enum GlobalCapabilities {
capIhrEnabled = 1,
capCreateStatsEnabled = 2,
capBounceMsgBody = 4,
capReportVersion = 8,
capSplitMergeTransactions = 16
};
inline int shard_pfx_len(ShardId shard) {
return shard ? 63 - td::count_trailing_zeroes_non_zero64(shard) : 0;

View file

@ -155,6 +155,13 @@ TEST(Tonlib, InitClose) {
}
}
td::Slice to_data(const td::SecureString &str) {
return str.as_slice();
}
td::Slice to_data(const tonlib::SimpleEncryptionV2::Decrypted &str) {
return str.data.as_slice();
}
template <class Encryption>
void test_encryption() {
std::string secret = "secret";
@ -164,7 +171,7 @@ void test_encryption() {
auto encrypted_data = Encryption::encrypt_data(data, secret);
LOG(ERROR) << encrypted_data.size();
auto decrypted_data = Encryption::decrypt_data(encrypted_data, secret).move_as_ok();
CHECK(data == decrypted_data);
CHECK(data == to_data(decrypted_data));
Encryption::decrypt_data(encrypted_data, wrong_secret).ensure_error();
Encryption::decrypt_data("", secret).ensure_error();
Encryption::decrypt_data(std::string(32, 'a'), secret).ensure_error();
@ -177,7 +184,7 @@ void test_encryption() {
auto data = td::rand_string('a', 'z', static_cast<int>(i));
auto encrypted_data = Encryption::encrypt_data(data, secret);
auto decrypted_data = Encryption::decrypt_data(encrypted_data, secret).move_as_ok();
CHECK(data == decrypted_data);
CHECK(data == to_data(decrypted_data));
}
}
TEST(Tonlib, SimpleEncryption) {
@ -199,24 +206,33 @@ TEST(Tonlib, SimpleEncryptionAsym) {
auto encrypted_data = SimpleEncryptionV2::encrypt_data(data, public_key, other_private_key).move_as_ok();
LOG(ERROR) << encrypted_data.size();
auto decrypted_data = SimpleEncryptionV2::decrypt_data(encrypted_data, private_key).move_as_ok();
CHECK(data == decrypted_data);
CHECK(data == decrypted_data.data);
auto decrypted_data2 = SimpleEncryptionV2::decrypt_data(encrypted_data, other_private_key).move_as_ok();
CHECK(data == decrypted_data2);
CHECK(data == decrypted_data2.data);
CHECK(decrypted_data.proof == decrypted_data2.proof);
auto decrypted_data3 =
SimpleEncryptionV2::decrypt_data_with_proof(encrypted_data, decrypted_data.proof).move_as_ok();
CHECK(data == decrypted_data3);
SimpleEncryptionV2::decrypt_data(encrypted_data, wrong_private_key).ensure_error();
SimpleEncryptionV2::decrypt_data("", private_key).ensure_error();
SimpleEncryptionV2::decrypt_data(std::string(32, 'a'), private_key).ensure_error();
SimpleEncryptionV2::decrypt_data(std::string(33, 'a'), private_key).ensure_error();
SimpleEncryptionV2::decrypt_data(std::string(64, 'a'), private_key).ensure_error();
SimpleEncryptionV2::decrypt_data(std::string(128, 'a'), private_key).ensure_error();
SimpleEncryptionV2::decrypt_data_with_proof(encrypted_data, decrypted_data.proof, "bad salt").ensure_error();
}
for (size_t i = 0; i < 255; i++) {
auto data = td::rand_string('a', 'z', static_cast<int>(i));
auto encrypted_data = SimpleEncryptionV2::encrypt_data(data, public_key, other_private_key).move_as_ok();
auto decrypted_data = SimpleEncryptionV2::decrypt_data(encrypted_data, private_key).move_as_ok();
CHECK(data == decrypted_data);
CHECK(data == decrypted_data.data);
auto decrypted_data2 = SimpleEncryptionV2::decrypt_data(encrypted_data, other_private_key).move_as_ok();
CHECK(data == decrypted_data2);
CHECK(data == decrypted_data2.data);
}
}
@ -518,19 +534,30 @@ TEST(Tonlib, KeysApi) {
auto other_public_key = td::Ed25519::generate_private_key().move_as_ok().get_public_key().move_as_ok();
std::string text = "hello world";
std::vector<tonlib_api::object_ptr<tonlib_api::msg_Data>> elements;
elements.push_back(make_object<tonlib_api::msg_dataEncryptedText>(
SimpleEncryptionV2::encrypt_data(text, other_public_key, pkey).move_as_ok().as_slice().str()));
std::vector<tonlib_api::object_ptr<tonlib_api::msg_dataEncrypted>> elements;
td::Slice addr = "Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX";
auto encrypted = SimpleEncryptionV2::encrypt_data(text, other_public_key, pkey, addr).move_as_ok().as_slice().str();
elements.push_back(make_object<tonlib_api::msg_dataEncrypted>(
make_object<tonlib_api::accountAddress>(addr.str()), make_object<tonlib_api::msg_dataEncryptedText>(encrypted)));
auto decrypted =
sync_send(client, make_object<tonlib_api::msg_decrypt>(
make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, raw_imported_key->secret_.copy()),
new_local_password.copy()),
make_object<tonlib_api::msg_dataArray>(std::move(elements))))
make_object<tonlib_api::msg_dataEncryptedArray>(std::move(elements))))
.move_as_ok();
downcast_call(*decrypted->elements_[0],
auto proof = decrypted->elements_[0]->proof_;
downcast_call(*decrypted->elements_[0]->data_,
td::overloaded([](auto &) { UNREACHABLE(); },
[&](tonlib_api::msg_dataDecryptedText &decrypted) { CHECK(decrypted.text_ == text); }));
auto decrypted2 = sync_send(client, make_object<tonlib_api::msg_decryptWithProof>(
proof, make_object<tonlib_api::msg_dataEncrypted>(
make_object<tonlib_api::accountAddress>(addr.str()),
make_object<tonlib_api::msg_dataEncryptedText>(encrypted))))
.move_as_ok();
downcast_call(*decrypted2,
td::overloaded([](auto &) { UNREACHABLE(); },
[&](tonlib_api::msg_dataDecryptedText &decrypted) { CHECK(decrypted.text_ == text); }));
}

View file

@ -392,9 +392,9 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
if (transfer_all) {
ASSERT_EQ(amount - first_fee, txn->in_msg_->value_);
} else {
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_->account_address_);
}
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_->account_address_);
if (!encrypt) {
ASSERT_EQ(message, read_text(*txn->in_msg_->msg_data_));
}

View file

@ -1368,6 +1368,7 @@ bool TonlibClient::is_static_request(td::int32 id) {
case tonlib_api::encrypt::ID:
case tonlib_api::decrypt::ID:
case tonlib_api::kdf::ID:
case tonlib_api::msg_decryptWithProof::ID:
return true;
default:
return false;
@ -1841,38 +1842,41 @@ struct ToRawTransactions {
auto body_cell = vm::CellBuilder().append_cellslice(*body).finalize();
auto body_hash = body_cell->get_hash().as_slice().str();
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
if (body->size() >= 32 && static_cast<td::uint32>(body->prefetch_long(32)) <= 1) {
auto type = body.write().fetch_long(32);
td::Status status;
auto get_data = [body = std::move(body), body_cell, this](td::Slice salt) mutable {
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
if (body->size() >= 32 && static_cast<td::uint32>(body->prefetch_long(32)) <= 1) {
auto type = body.write().fetch_long(32);
td::Status status;
auto r_body_message = vm::CellString::load(body.write());
LOG_IF(WARNING, r_body_message.is_error()) << "Failed to parse a message: " << r_body_message.error();
auto r_body_message = vm::CellString::load(body.write());
LOG_IF(WARNING, r_body_message.is_error()) << "Failed to parse a message: " << r_body_message.error();
if (r_body_message.is_ok()) {
if (type == 0) {
data = tonlib_api::make_object<tonlib_api::msg_dataText>(r_body_message.move_as_ok());
} else {
LOG(ERROR) << "TRY DECRYPT";
auto encrypted_message = r_body_message.move_as_ok();
auto r_decrypted_message = [&]() -> td::Result<std::string> {
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
TRY_RESULT(decrypted, SimpleEncryptionV2::decrypt_data(encrypted_message, private_key_.value()));
return decrypted.as_slice().str();
}();
if (r_decrypted_message.is_ok()) {
data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(r_decrypted_message.move_as_ok());
if (r_body_message.is_ok()) {
if (type == 0) {
data = tonlib_api::make_object<tonlib_api::msg_dataText>(r_body_message.move_as_ok());
} else {
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(encrypted_message);
LOG(ERROR) << "TRY DECRYPT";
auto encrypted_message = r_body_message.move_as_ok();
auto r_decrypted_message = [&]() -> td::Result<std::string> {
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
TRY_RESULT(decrypted, SimpleEncryptionV2::decrypt_data(encrypted_message, private_key_.value(), salt));
return decrypted.data.as_slice().str();
}();
if (r_decrypted_message.is_ok()) {
data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(r_decrypted_message.move_as_ok());
} else {
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(encrypted_message);
}
}
}
}
}
if (!data) {
data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(to_bytes(std::move(body_cell)));
}
if (!data) {
data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(to_bytes(std::move(body_cell)));
}
return data;
};
auto tag = block::gen::CommonMsgInfo().get_tag(*message.info);
if (tag < 0) {
@ -1892,9 +1896,10 @@ struct ToRawTransactions {
TRY_RESULT(ihr_fee, to_balance(msg_info.ihr_fee));
auto created_lt = static_cast<td::int64>(msg_info.created_lt);
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance, fwd_fee,
ihr_fee, created_lt, std::move(body_hash),
std::move(data));
return tonlib_api::make_object<tonlib_api::raw_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(src),
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(dest)), balance, fwd_fee, ihr_fee, created_lt,
std::move(body_hash), get_data(src));
}
case block::gen::CommonMsgInfo::ext_in_msg_info: {
block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info;
@ -1902,8 +1907,10 @@ struct ToRawTransactions {
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info");
}
TRY_RESULT(dest, to_std_address(msg_info.dest));
return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0, 0, 0, 0, std::move(body_hash),
std::move(data));
return tonlib_api::make_object<tonlib_api::raw_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(),
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(dest)), 0, 0, 0, 0, std::move(body_hash),
get_data(""));
}
case block::gen::CommonMsgInfo::ext_out_msg_info: {
block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info;
@ -1911,8 +1918,9 @@ struct ToRawTransactions {
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info");
}
TRY_RESULT(src, to_std_address(msg_info.src));
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0, 0, 0, 0, std::move(body_hash),
std::move(data));
return tonlib_api::make_object<tonlib_api::raw_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(src),
tonlib_api::make_object<tonlib_api::accountAddress>(), 0, 0, 0, 0, std::move(body_hash), get_data(src));
}
}
@ -2493,8 +2501,13 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return TonlibError::MessageEncryption("Get public key (in destination)");
}
auto addr = source_->get_address();
addr.bounceable = true;
addr.testnet = false;
TRY_RESULT_PREFIX(encrypted_message,
SimpleEncryptionV2::encrypt_data(action.message, o_public_key.unwrap(), private_key_.value()),
SimpleEncryptionV2::encrypt_data(action.message, o_public_key.unwrap(), private_key_.value(),
addr.rserialize(true)),
TonlibError::Internal());
gift.message = encrypted_message.as_slice().str();
gift.is_encrypted = true;
@ -2569,7 +2582,7 @@ td::Status TonlibClient::do_request(tonlib_api::createQuery& request,
}
td::Status TonlibClient::do_request(tonlib_api::msg_decrypt& request,
td::Promise<object_ptr<tonlib_api::msg_dataArray>>&& promise) {
td::Promise<object_ptr<tonlib_api::msg_dataDecryptedArray>>&& promise) {
if (!request.input_key_) {
return TonlibError::EmptyField("input_key");
}
@ -2577,27 +2590,44 @@ td::Status TonlibClient::do_request(tonlib_api::msg_decrypt& request,
return TonlibError::EmptyField("data");
}
TRY_RESULT(input_key, from_tonlib(*request.input_key_));
using ReturnType = tonlib_api::object_ptr<tonlib_api::msg_dataDecrypted>;
make_request(
int_api::GetPrivateKey{std::move(input_key)},
promise.wrap([elements = std::move(request.data_)](auto key) mutable {
auto private_key = td::Ed25519::PrivateKey(std::move(key.private_key));
elements->elements_ = td::transform(std::move(elements->elements_), [&private_key](auto msg) {
auto new_elements = td::transform(std::move(elements->elements_), [&private_key](auto msg) -> ReturnType {
auto res = tonlib_api::make_object<tonlib_api::msg_dataDecrypted>();
if (!msg) {
return std::move(msg);
return res;
}
if (!msg->data_) {
return res;
}
res->data_ = std::move(msg->data_);
if (!msg->source_) {
return res;
}
auto r_account_address = get_account_address(msg->source_->account_address_);
if (r_account_address.is_error()) {
return res;
}
using ReturnType = tonlib_api::object_ptr<tonlib_api::msg_Data>;
return downcast_call2<ReturnType>(
*msg, td::overloaded([&msg](auto&) { return std::move(msg); },
[&msg, &private_key](tonlib_api::msg_dataEncryptedText& encrypted) -> ReturnType {
auto r_decrypted = SimpleEncryptionV2::decrypt_data(encrypted.text_, private_key);
if (r_decrypted.is_error()) {
return std::move(msg);
}
return tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(
r_decrypted.move_as_ok().as_slice().str());
}));
*res->data_,
td::overloaded(
[&res](auto&) { return std::move(res); },
[&res, &private_key, &msg](tonlib_api::msg_dataEncryptedText& encrypted) -> ReturnType {
auto r_decrypted =
SimpleEncryptionV2::decrypt_data(encrypted.text_, private_key, msg->source_->account_address_);
if (r_decrypted.is_error()) {
return std::move(res);
}
auto decrypted = r_decrypted.move_as_ok();
return tonlib_api::make_object<tonlib_api::msg_dataDecrypted>(
decrypted.proof.as_slice().str(),
tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(decrypted.data.as_slice().str()));
}));
});
return std::move(elements);
return tonlib_api::make_object<tonlib_api::msg_dataDecryptedArray>(std::move(new_elements));
}));
return td::Status::OK();
}
@ -3251,6 +3281,32 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(const
SimpleEncryption::kdf(request.password_, request.salt_, request.iterations_));
}
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
const tonlib_api::msg_decryptWithProof& request) {
if (!request.data_) {
return status_to_tonlib_api(TonlibError::EmptyField("data"));
}
if (!request.data_->data_) {
TonlibError::EmptyField("data.data");
}
if (!request.data_->source_) {
TonlibError::EmptyField("data.source");
}
using ReturnType = tonlib_api::object_ptr<tonlib_api::msg_Data>;
return downcast_call2<ReturnType>(
*request.data_->data_,
td::overloaded([&request](auto&) { return std::move(request.data_->data_); },
[&request](tonlib_api::msg_dataEncryptedText& encrypted) -> ReturnType {
auto r_decrypted = SimpleEncryptionV2::decrypt_data_with_proof(
encrypted.text_, request.proof_, request.data_->source_->account_address_);
if (r_decrypted.is_error()) {
return std::move(request.data_->data_);
}
auto decrypted = r_decrypted.move_as_ok();
return tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(decrypted.as_slice().str());
}));
}
td::Status TonlibClient::do_request(int_api::GetAccountState request,
td::Promise<td::unique_ptr<AccountState>>&& promise) {
auto actor_id = actor_id_++;
@ -3399,4 +3455,9 @@ td::Status TonlibClient::do_request(const tonlib_api::kdf& request, P&&) {
UNREACHABLE();
return TonlibError::Internal();
}
template <class P>
td::Status TonlibClient::do_request(const tonlib_api::msg_decryptWithProof& request, P&&) {
UNREACHABLE();
return TonlibError::Internal();
}
} // namespace tonlib

View file

@ -160,6 +160,8 @@ class TonlibClient : public td::actor::Actor {
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::decrypt& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::kdf& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::msg_decryptWithProof& request);
template <class P>
td::Status do_request(const tonlib_api::runTests& request, P&&);
template <class P>
@ -194,6 +196,8 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::decrypt& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::kdf& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::msg_decryptWithProof& request, P&&);
void make_any_request(tonlib_api::Function& function, QueryContext query_context,
td::Promise<tonlib_api::object_ptr<tonlib_api::Object>>&& promise);
@ -281,7 +285,8 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(tonlib_api::createQuery& request, td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
td::Status do_request(tonlib_api::msg_decrypt& request, td::Promise<object_ptr<tonlib_api::msg_dataArray>>&& promise);
td::Status do_request(tonlib_api::msg_decrypt& request,
td::Promise<object_ptr<tonlib_api::msg_dataDecryptedArray>>&& promise);
td::int64 next_smc_id_{0};
std::map<td::int64, td::unique_ptr<AccountState>> smcs_;

View file

@ -23,7 +23,7 @@
namespace tonlib {
td::AesCbcState SimpleEncryption::calc_aes_cbc_state_hash(td::Slice hash) {
CHECK(hash.size() == 64);
CHECK(hash.size() >= 48);
td::SecureString key(32);
key.as_mutable_slice().copy_from(hash.substr(0, 32));
td::SecureString iv(16);
@ -34,7 +34,7 @@ td::AesCbcState SimpleEncryption::calc_aes_cbc_state_hash(td::Slice hash) {
td::AesCbcState SimpleEncryption::calc_aes_cbc_state_sha512(td::Slice seed) {
td::SecureString hash(64);
sha512(seed, hash.as_mutable_slice());
return calc_aes_cbc_state_hash(hash.as_slice());
return calc_aes_cbc_state_hash(hash.as_slice().substr(0, 48));
}
td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size, td::int64 min_padding) {
@ -106,10 +106,10 @@ td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice encrypted_
return td::SecureString(decrypted_data.as_slice().substr(prefix_size));
}
td::Result<td::SecureString> SimpleEncryptionV2::encrypt_data(td::Slice data,
const td::Ed25519::PublicKey &public_key) {
td::Result<td::SecureString> SimpleEncryptionV2::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
td::Slice salt) {
TRY_RESULT(tmp_private_key, td::Ed25519::generate_private_key());
return encrypt_data(data, public_key, tmp_private_key);
return encrypt_data(data, public_key, tmp_private_key, salt);
}
namespace {
@ -124,9 +124,10 @@ td::SecureString secure_xor(td::Slice a, td::Slice b) {
} // namespace
td::Result<td::SecureString> SimpleEncryptionV2::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
const td::Ed25519::PrivateKey &private_key) {
const td::Ed25519::PrivateKey &private_key,
td::Slice salt) {
TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(public_key, private_key));
auto encrypted = encrypt_data(data, shared_secret.as_slice());
auto encrypted = encrypt_data(data, shared_secret.as_slice(), salt);
TRY_RESULT(tmp_public_key, private_key.get_public_key());
td::SecureString prefixed_encrypted(tmp_public_key.LENGTH + encrypted.size());
prefixed_encrypted.as_mutable_slice().copy_from(tmp_public_key.as_octet_string());
@ -136,8 +137,9 @@ td::Result<td::SecureString> SimpleEncryptionV2::encrypt_data(td::Slice data, co
return std::move(prefixed_encrypted);
}
td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice data,
const td::Ed25519::PrivateKey &private_key) {
td::Result<SimpleEncryptionV2::Decrypted> SimpleEncryptionV2::decrypt_data(td::Slice data,
const td::Ed25519::PrivateKey &private_key,
td::Slice salt) {
if (data.size() < td::Ed25519::PublicKey::LENGTH) {
return td::Status::Error("Failed to decrypte: data is too small");
}
@ -145,17 +147,19 @@ td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice data,
auto tmp_public_key =
td::Ed25519::PublicKey(secure_xor(data.substr(0, td::Ed25519::PublicKey::LENGTH), public_key.as_octet_string()));
TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(tmp_public_key, private_key));
TRY_RESULT(decrypted, decrypt_data(data.substr(td::Ed25519::PublicKey::LENGTH), shared_secret.as_slice()));
TRY_RESULT(decrypted, decrypt_data(data.substr(td::Ed25519::PublicKey::LENGTH), shared_secret.as_slice(), salt));
return std::move(decrypted);
}
td::SecureString SimpleEncryptionV2::encrypt_data(td::Slice data, td::Slice secret) {
td::SecureString SimpleEncryptionV2::encrypt_data(td::Slice data, td::Slice secret, td::Slice salt) {
auto prefix = SimpleEncryption::gen_random_prefix(data.size(), 16);
td::SecureString combined(prefix.size() + data.size());
combined.as_mutable_slice().copy_from(prefix);
combined.as_mutable_slice().substr(prefix.size()).copy_from(data);
return encrypt_data_with_prefix(combined.as_slice(), secret);
return encrypt_data_with_prefix(combined.as_slice(), secret, salt);
}
td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice encrypted_data, td::Slice secret) {
td::Result<std::pair<td::Slice, td::Slice>> unpack_encrypted_data(td::Slice encrypted_data) {
if (encrypted_data.size() < 17) {
return td::Status::Error("Failed to decrypt: data is too small");
}
@ -163,13 +167,18 @@ td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice encrypte
return td::Status::Error("Failed to decrypt: data size is not divisible by 16");
}
auto msg_key = encrypted_data.substr(0, 16);
encrypted_data = encrypted_data.substr(16);
auto data = encrypted_data.substr(16);
return std::make_pair(msg_key, data);
}
auto cbc_state = SimpleEncryption::calc_aes_cbc_state_hash(SimpleEncryption::combine_secrets(secret, msg_key));
td::Result<td::SecureString> SimpleEncryptionV2::do_decrypt(td::Slice cbc_state_secret, td::Slice msg_key,
td::Slice encrypted_data, td::Slice salt) {
auto cbc_state = SimpleEncryption::calc_aes_cbc_state_hash(cbc_state_secret);
td::SecureString decrypted_data(encrypted_data.size(), 0);
auto iv_copy = cbc_state.raw().key.copy();
cbc_state.decrypt(encrypted_data, decrypted_data.as_mutable_slice());
auto data_hash = SimpleEncryption::combine_secrets(decrypted_data, secret);
auto data_hash = SimpleEncryption::combine_secrets(salt, decrypted_data);
auto got_msg_key = data_hash.as_slice().substr(0, 16);
// check hash
if (msg_key != got_msg_key) {
@ -183,9 +192,35 @@ td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice encrypte
return td::SecureString(decrypted_data.as_slice().substr(prefix_size));
}
td::SecureString SimpleEncryptionV2::encrypt_data_with_prefix(td::Slice data, td::Slice secret) {
td::Result<SimpleEncryptionV2::Decrypted> SimpleEncryptionV2::decrypt_data(td::Slice encrypted_data, td::Slice secret,
td::Slice salt) {
TRY_RESULT(p, unpack_encrypted_data(encrypted_data));
auto msg_key = p.first;
auto data = p.second;
auto cbc_state_secret = td::SecureString(SimpleEncryption::combine_secrets(secret, msg_key).as_slice().substr(0, 48));
TRY_RESULT(res, do_decrypt(cbc_state_secret.as_slice(), msg_key, data, salt));
return Decrypted{std::move(cbc_state_secret), std::move(res)};
}
td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data_with_proof(td::Slice encrypted_data, td::Slice proof,
td::Slice salt) {
if (encrypted_data.size() < td::Ed25519::PublicKey::LENGTH) {
return td::Status::Error("Failed to decrypte: data is too small");
}
if (proof.size() != 48) {
return td::Status::Error("Invalid proof size");
}
encrypted_data = encrypted_data.substr(td::Ed25519::PublicKey::LENGTH);
TRY_RESULT(p, unpack_encrypted_data(encrypted_data));
auto msg_key = p.first;
auto data = p.second;
return do_decrypt(proof, msg_key, data, salt);
}
td::SecureString SimpleEncryptionV2::encrypt_data_with_prefix(td::Slice data, td::Slice secret, td::Slice salt) {
CHECK(data.size() % 16 == 0);
auto data_hash = SimpleEncryption::combine_secrets(data, secret);
auto data_hash = SimpleEncryption::combine_secrets(salt, data);
auto msg_key = data_hash.as_slice().substr(0, 16);
td::SecureString res_buf(data.size() + 16, 0);

View file

@ -43,14 +43,27 @@ class SimpleEncryption {
class SimpleEncryptionV2 {
public:
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key);
static td::Result<td::SecureString> decrypt_data(td::Slice data, const td::Ed25519::PrivateKey &private_key);
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
const td::Ed25519::PrivateKey &private_key);
static td::SecureString encrypt_data(td::Slice data, td::Slice secret);
static td::Result<td::SecureString> decrypt_data(td::Slice encrypted_data, td::Slice secret);
td::Slice salt = {});
struct Decrypted {
td::SecureString proof;
td::SecureString data;
};
static td::Result<Decrypted> decrypt_data(td::Slice data, const td::Ed25519::PrivateKey &private_key,
td::Slice sallt = {});
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
const td::Ed25519::PrivateKey &private_key, td::Slice salt = {});
static td::Result<td::SecureString> decrypt_data_with_proof(td::Slice encrypted_data, td::Slice proof,
td::Slice salt = {});
static td::SecureString encrypt_data(td::Slice data, td::Slice secret, td::Slice salt = {});
static td::Result<Decrypted> decrypt_data(td::Slice encrypted_data, td::Slice secret, td::Slice salt = {});
private:
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret);
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret, td::Slice salt = {});
static td::Result<td::SecureString> do_decrypt(td::Slice cbc_state_secret, td::Slice msg_key,
td::Slice encrypted_data, td::Slice salt);
};
} // namespace tonlib

View file

@ -1384,10 +1384,10 @@ class TonlibCli : public td::actor::Actor {
sb << "-" << Grams{td::uint64(-balance)};
}
sb << " Fee: " << Grams{td::uint64(t->fee_)};
if (t->in_msg_->source_.empty()) {
if (!t->in_msg_->source_->account_address_.empty()) {
sb << " External ";
} else {
sb << " From " << t->in_msg_->source_;
sb << " From " << t->in_msg_->source_->account_address_;
}
auto print_msg_data = [](td::StringBuilder& sb,
tonlib_api::object_ptr<tonlib_api::msg_Data>& msg_data) {
@ -1406,10 +1406,10 @@ class TonlibCli : public td::actor::Actor {
print_msg_data(sb, t->in_msg_->msg_data_);
for (auto& ot : t->out_msgs_) {
sb << "\n\t";
if (ot->destination_.empty()) {
if (ot->destination_->account_address_.empty()) {
sb << " External ";
} else {
sb << " To " << ot->destination_;
sb << " To " << ot->destination_->account_address_;
}
sb << " " << Grams{td::uint64(ot->value_)};
print_msg_data(sb, ot->msg_data_);

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "validator-engine-console-query.h"
#include "validator-engine-console.h"
@ -609,6 +609,7 @@ td::Status AddNetworkAddressQuery::receive(td::BufferSlice data) {
td::Status AddNetworkProxyAddressQuery::run() {
TRY_RESULT_ASSIGN(in_addr_, tokenizer_.get_token<td::IPAddress>());
TRY_RESULT_ASSIGN(out_addr_, tokenizer_.get_token<td::IPAddress>());
TRY_RESULT_ASSIGN(id_, tokenizer_.get_token<td::Bits256>());
TRY_RESULT_ASSIGN(shared_secret_, tokenizer_.get_token<td::BufferSlice>());
TRY_RESULT_ASSIGN(cats_, tokenizer_.get_token_vector<td::int32>());
TRY_RESULT_ASSIGN(prio_cats_, tokenizer_.get_token_vector<td::int32>());
@ -619,7 +620,7 @@ td::Status AddNetworkProxyAddressQuery::run() {
td::Status AddNetworkProxyAddressQuery::send() {
auto b = ton::create_serialize_tl_object<ton::ton_api::engine_validator_addProxy>(
static_cast<td::int32>(in_addr_.get_ipv4()), in_addr_.get_port(), static_cast<td::int32>(out_addr_.get_ipv4()),
out_addr_.get_port(), ton::create_tl_object<ton::ton_api::adnl_proxy_fast>(std::move(shared_secret_)),
out_addr_.get_port(), ton::create_tl_object<ton::ton_api::adnl_proxy_fast>(id_, std::move(shared_secret_)),
std::move(cats_), std::move(prio_cats_));
td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise());
return td::Status::OK();

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -104,6 +104,19 @@ inline td::Result<ton::PublicKeyHash> Tokenizer::get_token() {
}
}
template <>
inline td::Result<td::Bits256> Tokenizer::get_token() {
TRY_RESULT(S, get_raw_token());
TRY_RESULT(F, td::hex_decode(S));
if (F.size() == 32) {
td::Bits256 v;
v.as_slice().copy_from(F);
return v;
} else {
return td::Status::Error("cannot parse keyhash: bad length");
}
}
template <>
inline td::Result<td::IPAddress> Tokenizer::get_token() {
TRY_RESULT(S, get_raw_token());
@ -782,7 +795,7 @@ class AddNetworkProxyAddressQuery : public Query {
return "addproxyaddr";
}
static std::string get_help() {
return "addproxyaddr <inip> <outid> <secret> {cats...} {priocats...}\tadds ip address to address list";
return "addproxyaddr <inip> <outip> <id> <secret> {cats...} {priocats...}\tadds ip address to address list";
}
std::string name() const override {
return get_name();
@ -791,6 +804,7 @@ class AddNetworkProxyAddressQuery : public Query {
private:
td::IPAddress in_addr_;
td::IPAddress out_addr_;
td::Bits256 id_;
td::BufferSlice shared_secret_;
std::vector<td::int32> cats_;
std::vector<td::int32> prio_cats_;

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "validator-engine.hpp"
@ -86,8 +86,12 @@ Config::Config(ton::ton_api::engine_validator_config &config) {
[&](const ton::ton_api::engine_addr &obj) {
in_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.ip_), static_cast<td::uint16>(obj.port_)).ensure();
out_ip = in_ip;
categories = obj.categories_;
priority_categories = obj.priority_categories_;
for (auto cat : obj.categories_) {
categories.push_back(td::narrow_cast<td::uint8>(cat));
}
for (auto cat : obj.priority_categories_) {
priority_categories.push_back(td::narrow_cast<td::uint8>(cat));
}
},
[&](const ton::ton_api::engine_addrProxy &obj) {
in_ip.init_ipv4_port(td::IPAddress::ipv4_to_str(obj.in_ip_), static_cast<td::uint16>(obj.in_port_))
@ -98,15 +102,19 @@ Config::Config(ton::ton_api::engine_validator_config &config) {
auto R = ton::adnl::AdnlProxy::create(*obj.proxy_type_.get());
R.ensure();
proxy = R.move_as_ok();
categories = obj.categories_;
priority_categories = obj.priority_categories_;
for (auto cat : obj.categories_) {
categories.push_back(td::narrow_cast<td::uint8>(cat));
}
for (auto cat : obj.priority_categories_) {
priority_categories.push_back(td::narrow_cast<td::uint8>(cat));
}
}
}));
config_add_network_addr(in_ip, out_ip, std::move(proxy), categories, priority_categories).ensure();
}
for (auto &adnl : config.adnl_) {
config_add_adnl_addr(ton::PublicKeyHash{adnl->id_}, adnl->category_).ensure();
config_add_adnl_addr(ton::PublicKeyHash{adnl->id_}, td::narrow_cast<td::uint8>(adnl->category_)).ensure();
}
for (auto &dht : config.dht_) {
config_add_dht_node(ton::PublicKeyHash{dht->id_}).ensure();
@ -1094,7 +1102,9 @@ void ValidatorEngine::load_empty_local_config(td::Promise<td::Unit> promise) {
ig.add_promise(std::move(ret_promise));
for (auto &addr : addrs_) {
config_.config_add_network_addr(addr, addr, nullptr, std::vector<td::int32>{0, 1, 2, 3}, std::vector<td::int32>{})
config_
.config_add_network_addr(addr, addr, nullptr, std::vector<AdnlCategory>{0, 1, 2, 3},
std::vector<AdnlCategory>{})
.ensure();
}
@ -1156,7 +1166,9 @@ void ValidatorEngine::load_local_config(td::Promise<td::Unit> promise) {
ig.add_promise(std::move(ret_promise));
for (auto &addr : addrs_) {
config_.config_add_network_addr(addr, addr, nullptr, std::vector<td::int32>{0, 1, 2, 3}, std::vector<td::int32>{})
config_
.config_add_network_addr(addr, addr, nullptr, std::vector<AdnlCategory>{0, 1, 2, 3},
std::vector<AdnlCategory>{})
.ensure();
}
@ -1378,18 +1390,25 @@ void ValidatorEngine::start_adnl() {
}
void ValidatorEngine::add_addr(const Config::Addr &addr, const Config::AddrCats &cats) {
ton::adnl::AdnlCategoryMask cat_mask;
for (auto cat : cats.cats) {
cat_mask[cat] = true;
}
for (auto cat : cats.priority_cats) {
cat_mask[cat] = true;
}
if (!cats.proxy) {
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_self_addr, addr.addr,
cats.cats.size() ? 0 : 1);
std::move(cat_mask), cats.cats.size() ? 0 : 1);
} else {
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_proxy_addr, addr.addr,
cats.proxy, cats.cats.size() ? 0 : 1);
td::actor::send_closure(adnl_network_manager_, &ton::adnl::AdnlNetworkManager::add_proxy_addr, cats.in_addr,
static_cast<td::uint16>(addr.addr.get_port()), cats.proxy, std::move(cat_mask),
cats.cats.size() ? 0 : 1);
}
td::uint32 ts = static_cast<td::uint32>(td::Clocks::system());
for (auto cat : cats.cats) {
CHECK(cat >= 0);
ton::adnl::AdnlAddress x = ton::adnl::AdnlAddressImpl::create(
ton::create_tl_object<ton::ton_api::adnl_address_udp>(cats.in_addr.get_ipv4(), cats.in_addr.get_port()));
addr_lists_[cat].add_addr(std::move(x));
@ -1397,7 +1416,6 @@ void ValidatorEngine::add_addr(const Config::Addr &addr, const Config::AddrCats
addr_lists_[cat].set_reinit_date(ton::adnl::Adnl::adnl_start_time());
}
for (auto cat : cats.priority_cats) {
CHECK(cat >= 0);
ton::adnl::AdnlAddress x = ton::adnl::AdnlAddressImpl::create(
ton::create_tl_object<ton::ton_api::adnl_address_udp>(cats.in_addr.get_ipv4(), cats.in_addr.get_port()));
prio_addr_lists_[cat].add_addr(std::move(x));
@ -1408,7 +1426,7 @@ void ValidatorEngine::add_addr(const Config::Addr &addr, const Config::AddrCats
void ValidatorEngine::add_adnl(ton::PublicKeyHash id, AdnlCategory cat) {
CHECK(keys_.count(id) > 0);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, addr_lists_[cat]);
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]}, addr_lists_[cat], cat);
}
void ValidatorEngine::started_adnl() {
@ -1526,7 +1544,7 @@ void ValidatorEngine::started_full_node() {
void ValidatorEngine::add_lite_server(ton::PublicKeyHash id, td::uint16 port) {
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]},
ton::adnl::AdnlAddressList{});
ton::adnl::AdnlAddressList{}, static_cast<td::uint8>(255));
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_ext_server_id,
ton::adnl::AdnlNodeIdShort{id});
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_ext_server_port, port);
@ -1565,7 +1583,7 @@ void ValidatorEngine::add_control_interface(ton::PublicKeyHash id, td::uint16 po
};
td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{keys_[id]},
ton::adnl::AdnlAddressList{});
ton::adnl::AdnlAddressList{}, static_cast<td::uint8>(255));
td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, ton::adnl::AdnlNodeIdShort{id}, std::string(""),
std::make_unique<Callback>(actor_id(this), port));
td::actor::send_closure(control_ext_server_, &ton::adnl::AdnlExtServer::add_local_id, ton::adnl::AdnlNodeIdShort{id});
@ -1621,7 +1639,7 @@ void ValidatorEngine::started() {
}
void ValidatorEngine::try_add_adnl_node(ton::PublicKeyHash key, AdnlCategory cat, td::Promise<td::Unit> promise) {
if (cat < 0 || static_cast<td::uint32>(cat) > max_cat()) {
if (cat > max_cat()) {
promise.set_error(td::Status::Error(ton::ErrorCode::protoviolation, "bad category value"));
return;
}
@ -2136,23 +2154,24 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addAdnlId
}
auto id = ton::PublicKeyHash{query.key_hash_};
TRY_RESULT_PROMISE(promise, cat, td::narrow_cast_safe<td::uint8>(query.category_));
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id, cat = query.category_,
promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: ")));
return;
}
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add adnl node: ")));
} else {
promise.set_value(
ton::serialize_tl_object(ton::create_tl_object<ton::ton_api::engine_validator_success>(), true));
}
});
td::actor::send_closure(SelfId, &ValidatorEngine::try_add_adnl_node, id, cat, std::move(P));
});
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), id, cat, promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to get public key: ")));
return;
}
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error_prefix("failed to add adnl node: ")));
} else {
promise.set_value(
ton::serialize_tl_object(ton::create_tl_object<ton::ton_api::engine_validator_success>(), true));
}
});
td::actor::send_closure(SelfId, &ValidatorEngine::try_add_adnl_node, id, cat, std::move(P));
});
check_key(id, std::move(P));
}
@ -2538,7 +2557,17 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addListen
}
});
try_add_listening_port(query.ip_, query.port_, query.categories_, query.priority_categories_, std::move(P));
std::vector<td::uint8> cats;
for (auto cat : query.categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
cats.push_back(c);
}
std::vector<td::uint8> prio_cats;
for (auto cat : query.priority_categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
prio_cats.push_back(c);
}
try_add_listening_port(query.ip_, query.port_, std::move(cats), std::move(prio_cats), std::move(P));
}
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delListeningPort &query, td::BufferSlice data,
@ -2561,7 +2590,17 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delListen
}
});
try_del_listening_port(query.ip_, query.port_, query.categories_, query.priority_categories_, std::move(P));
std::vector<td::uint8> cats;
for (auto cat : query.categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
cats.push_back(c);
}
std::vector<td::uint8> prio_cats;
for (auto cat : query.priority_categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
prio_cats.push_back(c);
}
try_del_listening_port(query.ip_, query.port_, std::move(cats), std::move(prio_cats), std::move(P));
}
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addProxy &query, td::BufferSlice data,
@ -2590,8 +2629,18 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addProxy
}
});
try_add_proxy(query.in_ip_, query.in_port_, query.out_ip_, query.out_port_, R.move_as_ok(), query.categories_,
query.priority_categories_, std::move(P));
std::vector<td::uint8> cats;
for (auto cat : query.categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
cats.push_back(c);
}
std::vector<td::uint8> prio_cats;
for (auto cat : query.priority_categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
prio_cats.push_back(c);
}
try_add_proxy(query.in_ip_, query.in_port_, query.out_ip_, query.out_port_, R.move_as_ok(), std::move(cats),
std::move(prio_cats), std::move(P));
}
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delProxy &query, td::BufferSlice data,
@ -2614,7 +2663,18 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_delProxy
}
});
try_del_proxy(query.out_ip_, query.out_port_, query.categories_, query.priority_categories_, std::move(P));
std::vector<td::uint8> cats;
for (auto cat : query.categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
cats.push_back(c);
}
std::vector<td::uint8> prio_cats;
for (auto cat : query.priority_categories_) {
TRY_RESULT_PROMISE(promise, c, td::narrow_cast_safe<td::uint8>(cat));
prio_cats.push_back(c);
}
try_del_proxy(query.out_ip_, query.out_port_, std::move(cats), std::move(prio_cats), std::move(P));
}
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getConfig &query, td::BufferSlice data,

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -43,7 +43,7 @@
enum ValidatorEnginePermissions : td::uint32 { vep_default = 1, vep_modify = 2, vep_unsafe = 4 };
using AdnlCategory = td::int32;
using AdnlCategory = td::uint8;
struct Config {
struct Addr {
@ -201,8 +201,8 @@ class ValidatorEngine : public td::actor::Actor {
bool started_ = false;
public:
static constexpr td::uint32 max_cat() {
return 256;
static constexpr td::uint8 max_cat() {
return 250;
}
void set_local_config(std::string str);
@ -286,7 +286,7 @@ class ValidatorEngine : public td::actor::Actor {
void alarm() override;
void run();
void try_add_adnl_node(ton::PublicKeyHash pub, td::int32 cat, td::Promise<td::Unit> promise);
void try_add_adnl_node(ton::PublicKeyHash pub, AdnlCategory cat, td::Promise<td::Unit> promise);
void try_add_dht_node(ton::PublicKeyHash pub, td::Promise<td::Unit> promise);
void try_add_validator_permanent_key(ton::PublicKeyHash key_hash, td::uint32 election_date, td::uint32 ttl,
td::Promise<td::Unit> promise);
@ -385,4 +385,3 @@ class ValidatorEngine : public td::actor::Actor {
void process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst,
td::BufferSlice data, td::Promise<td::BufferSlice> promise);
};

View file

@ -79,8 +79,8 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
}
if (handle_->is_applied()) {
auto P = td::PromiseCreator::lambda(
[ SelfId = actor_id(this), seqno = handle_->id().id.seqno ](td::Result<BlockIdExt> R) {
auto P =
td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = handle_->id().id.seqno](td::Result<BlockIdExt> R) {
R.ensure();
auto h = R.move_as_ok();
if (h.id.seqno < seqno) {
@ -119,15 +119,14 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
td::actor::send_closure(manager_, &ValidatorManager::set_block_data, handle_, block_, std::move(P));
} else {
auto P =
td::PromiseCreator::lambda([ SelfId = actor_id(this), handle = handle_ ](td::Result<td::Ref<BlockData>> R) {
CHECK(handle->received());
if (R.is_error()) {
td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &ApplyBlock::written_block_data);
}
});
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result<td::Ref<BlockData>> R) {
CHECK(handle->received());
if (R.is_error()) {
td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &ApplyBlock::written_block_data);
}
});
td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, apply_block_priority(), timeout_,
std::move(P));
@ -136,19 +135,25 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
void ApplyBlock::written_block_data() {
VLOG(VALIDATOR_DEBUG) << "apply block: written block data for " << id_;
if (handle_->id().is_masterchain() && !handle_->inited_proof()) {
abort_query(td::Status::Error(ErrorCode::notready, "proof is absent"));
return;
if (!handle_->id().seqno()) {
CHECK(handle_->inited_split_after());
CHECK(handle_->inited_state_root_hash());
CHECK(handle_->inited_logical_time());
} else {
if (handle_->id().is_masterchain() && !handle_->inited_proof()) {
abort_query(td::Status::Error(ErrorCode::notready, "proof is absent"));
return;
}
if (!handle_->id().is_masterchain() && !handle_->inited_proof_link()) {
abort_query(td::Status::Error(ErrorCode::notready, "proof link is absent"));
return;
}
CHECK(handle_->inited_merge_before());
CHECK(handle_->inited_split_after());
CHECK(handle_->inited_prev());
CHECK(handle_->inited_state_root_hash());
CHECK(handle_->inited_logical_time());
}
if (!handle_->id().is_masterchain() && !handle_->inited_proof_link()) {
abort_query(td::Status::Error(ErrorCode::notready, "proof link is absent"));
return;
}
CHECK(handle_->inited_merge_before());
CHECK(handle_->inited_split_after());
CHECK(handle_->inited_prev());
CHECK(handle_->inited_state_root_hash());
CHECK(handle_->inited_logical_time());
if (handle_->is_applied() && handle_->processed()) {
finish_query();
} else {

View file

@ -39,6 +39,9 @@ namespace validator {
using td::Ref;
class Collator final : public td::actor::Actor {
static constexpr int supported_version = 1;
static constexpr long long supported_capabilities =
ton::capCreateStatsEnabled | ton::capBounceMsgBody | ton::capReportVersion;
using LtCellRef = block::LtCellRef;
using NewOutMsg = block::NewOutMsg;
const ShardIdFull shard;
@ -137,6 +140,7 @@ class Collator final : public td::actor::Actor {
bool shard_conf_adjusted_{false};
bool ihr_enabled_{false};
bool create_stats_enabled_{false};
bool report_version_{false};
td::uint64 overload_history_{0}, underload_history_{0};
td::uint64 block_size_estimate_{};
Ref<block::WorkchainInfo> wc_info_;
@ -286,6 +290,7 @@ class Collator final : public td::actor::Actor {
bool store_master_ref(vm::CellBuilder& cb);
bool store_prev_blk_ref(vm::CellBuilder& cb, bool after_merge);
bool store_zero_state_ref(vm::CellBuilder& cb);
bool store_version(vm::CellBuilder& cb) const;
bool create_block_info(Ref<vm::Cell>& block_info);
bool check_value_flow();
bool create_block_extra(Ref<vm::Cell>& block_extra);

View file

@ -510,6 +510,7 @@ bool Collator::unpack_last_mc_state() {
global_id_ = config_->get_global_blockchain_id();
ihr_enabled_ = config_->ihr_enabled();
create_stats_enabled_ = config_->create_stats_enabled();
report_version_ = config_->has_capability(ton::capReportVersion);
shard_conf_ = std::make_unique<block::ShardConfig>(*config_);
prev_key_block_exists_ = config_->get_last_key_block(prev_key_block_, prev_key_block_lt_);
if (prev_key_block_exists_) {
@ -529,6 +530,16 @@ bool Collator::unpack_last_mc_state() {
<< ", " << block_limits_->bytes.hard() << "]";
LOG(DEBUG) << "block limits: gas [" << block_limits_->gas.underload() << ", " << block_limits_->gas.soft() << ", "
<< block_limits_->gas.hard() << "]";
if (config_->has_capabilities() && (config_->get_capabilities() & ~supported_capabilities)) {
LOG(ERROR) << "block generation capabilities " << config_->get_capabilities()
<< " have been enabled in global configuration, but we support only " << supported_capabilities
<< " (upgrade validator software?)";
}
if (config_->get_global_version() > supported_version) {
LOG(ERROR) << "block version " << config_->get_global_version()
<< " have been enabled in global configuration, but we support only " << supported_version
<< " (upgrade validator software?)";
}
// TODO: extract start_lt and end_lt from prev_mc_block as well
// std::cerr << " block::gen::ShardState::print_ref(mc_state_root) = ";
// block::gen::t_ShardState.print_ref(std::cerr, mc_state_root, 2);
@ -1502,6 +1513,7 @@ bool Collator::fetch_config_params() {
block::MsgPrices{rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor,
(unsigned)rec.first_frac, (unsigned)rec.next_frac};
action_phase_cfg_.workchains = &config_->get_workchain_list();
action_phase_cfg_.bounce_msg_body = (config_->has_capability(ton::capBounceMsgBody) ? 256 : 0);
}
{
// fetch block_grams_created
@ -3571,7 +3583,7 @@ bool Collator::create_block_info(Ref<vm::Cell>& block_info) {
&& cb.store_bool_bool(want_split_) // want_split:Bool
&& cb.store_bool_bool(want_merge_) // want_merge:Bool
&& cb.store_bool_bool(is_key_block_) // key_block:Bool
&& cb.store_long_bool(0, 9) // vert_seqno_incr:(## 1) flags:(## 8)
&& cb.store_long_bool((int)report_version_, 9) // vert_seqno_incr:(## 1) flags:(## 8)
&& cb.store_long_bool(new_block_seqno, 32) // seq_no:#
&& cb.store_long_bool(vert_seqno_, 32) // vert_seq_no:#
&& block::ShardId{shard}.serialize(cb) // shard:ShardIdent
@ -3582,6 +3594,7 @@ bool Collator::create_block_info(Ref<vm::Cell>& block_info) {
&& cb.store_long_bool(cc_seqno, 32) // gen_catchain_seqno:uint32
&& cb.store_long_bool(min_ref_mc_seqno_, 32) // min_ref_mc_seqno:uint32
&& cb.store_long_bool(prev_key_block_seqno_, 32) // prev_key_block_seqno:uint32
&& (!report_version_ || store_version(cb)) // gen_software:flags . 0?GlobalVersion
&& (mc || (store_master_ref(cb2) // master_ref:not_master?
&& cb.store_builder_ref_bool(std::move(cb2)))) // .. ^BlkMasterInfo
&& store_prev_blk_ref(cb2, after_merge_) // prev_ref:..
@ -3589,6 +3602,10 @@ bool Collator::create_block_info(Ref<vm::Cell>& block_info) {
&& cb.finalize_to(block_info);
}
bool Collator::store_version(vm::CellBuilder& cb) const {
return block::gen::t_GlobalVersion.pack_capabilities(cb, supported_version, supported_capabilities);
}
bool Collator::store_zero_state_ref(vm::CellBuilder& cb) {
CHECK(prev_state_root_.not_null());
RootHash root_hash = prev_state_root_->get_hash().bits();

View file

@ -679,6 +679,17 @@ bool ValidateQuery::try_unpack_mc_state() {
config_->set_block_id_ext(mc_blkid_);
ihr_enabled_ = config_->ihr_enabled();
create_stats_enabled_ = config_->create_stats_enabled();
if (config_->has_capabilities() && (config_->get_capabilities() & ~supported_capabilities)) {
LOG(ERROR) << "block generation capabilities " << config_->get_capabilities()
<< " have been enabled in global configuration, but we support only " << supported_capabilities
<< " (upgrade validator software?)";
}
if (config_->get_global_version() > supported_version) {
LOG(ERROR) << "block version " << config_->get_global_version()
<< " have been enabled in global configuration, but we support only " << supported_version
<< " (upgrade validator software?)";
}
old_shard_conf_ = std::make_unique<block::ShardConfig>(*config_);
if (!is_masterchain()) {
new_shard_conf_ = std::make_unique<block::ShardConfig>(*config_);
@ -772,6 +783,7 @@ bool ValidateQuery::fetch_config_params() {
block::MsgPrices{rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor,
(unsigned)rec.first_frac, (unsigned)rec.next_frac};
action_phase_cfg_.workchains = &config_->get_workchain_list();
action_phase_cfg_.bounce_msg_body = (config_->has_capability(ton::capBounceMsgBody) ? 256 : 0);
}
{
// fetch block_grams_created
@ -4832,7 +4844,7 @@ bool ValidateQuery::check_config_update(Ref<vm::CellSlice> old_conf_params, Ref<
}
if (!block::valid_config_data(ocfg_root, old_cfg_addr, true, true, old_mparams_)) {
return reject_query("configuration extracted from (old) configuration smart contract "s + old_cfg_addr.to_hex() +
" failed to pass per-parameted validity checks, or one of mandatory parameters is missing");
" failed to pass per-parameter validity checks, or one of mandatory parameters is missing");
}
if (block::important_config_parameters_changed(new_cfg_root, old_cfg_root)) {
// same as the check in Collator::create_mc_state_extra()

View file

@ -107,6 +107,10 @@ inline ErrorCtxSet ErrorCtx::set_guard(std::vector<std::string> str_list) {
*/
class ValidateQuery : public td::actor::Actor {
static constexpr int supported_version = 1;
static constexpr long long supported_capabilities =
ton::capCreateStatsEnabled | ton::capBounceMsgBody | ton::capReportVersion;
public:
ValidateQuery(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_masterchain_block_id, std::vector<BlockIdExt> prev,
BlockCandidate candidate, td::Ref<ValidatorSet> validator_set,

View file

@ -382,7 +382,6 @@ void ArchiveImporter::finish_query() {
if (promise_) {
promise_.set_value(
std::vector<BlockSeqno>{state_->get_seqno(), std::min<BlockSeqno>(state_->get_seqno(), shard_client_seqno_)});
td::unlink(path_).ensure();
}
stop();
}

View file

@ -1393,6 +1393,39 @@ void ValidatorManagerImpl::start_up() {
td::actor::send_closure(SelfId, &ValidatorManagerImpl::started, R.move_as_ok());
});
auto to_import_dir = db_root_ + "/import";
auto S = td::WalkPath::run(to_import_dir, [&](td::CSlice cfname, td::WalkPath::Type t) -> void {
auto fname = td::Slice(cfname);
if (t == td::WalkPath::Type::NotDir) {
auto d = fname.rfind('/');
if (d != td::Slice::npos) {
fname = fname.substr(d + 1);
}
if (fname.size() <= 13) {
return;
}
if (fname.substr(fname.size() - 5) != ".pack") {
return;
}
fname = fname.substr(0, fname.size() - 5);
if (fname.substr(0, 8) != "archive.") {
return;
}
fname = fname.substr(8);
auto v = td::to_integer_safe<BlockSeqno>(fname);
if (v.is_error()) {
return;
}
auto pos = v.move_as_ok();
LOG(INFO) << "found archive slice '" << cfname << "' for position " << pos;
to_import_[pos] = std::make_pair(cfname.str(), true);
}
});
if (S.is_error()) {
LOG(INFO) << "failed to load blocks from import dir: " << S;
}
validator_manager_init(opts_, actor_id(this), db_.get(), std::move(P));
check_waiters_at_ = td::Timestamp::in(1.0);
@ -1475,23 +1508,35 @@ void ValidatorManagerImpl::download_next_archive() {
finish_prestart_sync();
return;
}
auto seqno = std::min(last_masterchain_seqno_, shard_client_handle_->id().seqno());
auto it = to_import_.upper_bound(seqno);
if (it != to_import_.begin()) {
it--;
if (it->second.second) {
it->second.second = false;
downloaded_archive_slice(it->second.first, false);
return;
}
}
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<std::string> R) {
if (R.is_error()) {
LOG(INFO) << "failed to download archive slice: " << R.error();
delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); },
td::Timestamp::in(2.0));
} else {
td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok());
td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok(), true);
}
});
auto seqno = std::min(last_masterchain_seqno_, shard_client_handle_->id().seqno());
callback_->download_archive(seqno + 1, db_root_ + "/tmp/", td::Timestamp::in(3600.0), std::move(P));
}
void ValidatorManagerImpl::downloaded_archive_slice(std::string name) {
void ValidatorManagerImpl::downloaded_archive_slice(std::string name, bool is_tmp) {
LOG(INFO) << "downloaded archive slice: " << name;
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<std::vector<BlockSeqno>> R) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), name, is_tmp](td::Result<std::vector<BlockSeqno>> R) {
if (is_tmp) {
td::unlink(name).ensure();
}
if (R.is_error()) {
LOG(INFO) << "failed to check downloaded archive slice: " << R.error();
delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); },
@ -1539,6 +1584,8 @@ void ValidatorManagerImpl::checked_archive_slice(std::vector<BlockSeqno> seqno)
}
void ValidatorManagerImpl::finish_prestart_sync() {
to_import_.clear();
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerImpl::completed_prestart_sync);
@ -1665,6 +1712,16 @@ void ValidatorManagerImpl::update_shards() {
if (!validator_id.is_zero()) {
auto val_group_id = get_validator_set_id(shard, val_set, opts_hash);
// DIRTY. But we don't want to create hardfork now
// TODO! DELETE IT LATER
if (last_masterchain_seqno_ >= 2904932 && val_set->get_catchain_seqno() == 44896) {
if (opts_->zero_block_id().file_hash.to_hex() ==
"5E994FCF4D425C0A6CE6A792594B7173205F740A39CD56F537DEFD28B48A0F6E") {
val_group_id[0] = !val_group_id[0];
}
}
VLOG(VALIDATOR_DEBUG) << "validating group " << val_group_id;
auto it = validator_groups_.find(val_group_id);
if (it != validator_groups_.end()) {

View file

@ -264,7 +264,7 @@ class ValidatorManagerImpl : public ValidatorManager {
bool out_of_sync();
void prestart_sync();
void download_next_archive();
void downloaded_archive_slice(std::string name);
void downloaded_archive_slice(std::string name, bool is_tmp);
void checked_archive_slice(std::vector<BlockSeqno> seqno);
void finish_prestart_sync();
void completed_prestart_sync();
@ -556,6 +556,8 @@ class ValidatorManagerImpl : public ValidatorManager {
td::actor::ActorOwn<AsyncStateSerializer> serializer_;
std::map<BlockSeqno, std::pair<std::string, bool>> to_import_;
private:
std::unique_ptr<Callback> callback_;
td::actor::ActorOwn<Db> db_;