mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
493
crypto/block/Binlog.cpp
Normal file
493
crypto/block/Binlog.cpp
Normal file
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "crypto/block/Binlog.h"
|
||||
|
||||
#include "td/utils/as.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/path.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace block {
|
||||
/*
|
||||
*
|
||||
* GENERIC BINLOG (move to separate file)
|
||||
*
|
||||
*/
|
||||
|
||||
BinlogBuffer::BinlogBuffer(std::unique_ptr<BinlogCallback> cb, std::size_t _max_size, td::FileFd fd)
|
||||
: cb(std::move(cb))
|
||||
, need_more_bytes(0)
|
||||
, eptr(nullptr)
|
||||
, log_rpos(0)
|
||||
, log_cpos(0)
|
||||
, log_wpos(0)
|
||||
, fd(std::move(fd))
|
||||
, replica(false)
|
||||
, writing(false)
|
||||
, dirty(false)
|
||||
, created(false)
|
||||
, ok(false) {
|
||||
max_size = _max_size;
|
||||
start = static_cast<unsigned char*>(std::malloc(max_size));
|
||||
DCHECK(start);
|
||||
rptr = wptr = cptr = start;
|
||||
end = start + max_size;
|
||||
}
|
||||
|
||||
unsigned char* BinlogBuffer::alloc_log_event_force(std::size_t size) {
|
||||
unsigned char* res = alloc_log_event(size);
|
||||
if (!res) {
|
||||
throw LevAllocError{size};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned char* BinlogBuffer::try_alloc_log_event(std::size_t size) {
|
||||
if (!eptr) {
|
||||
if (end - wptr >= (long)size) {
|
||||
unsigned char* res = wptr;
|
||||
wptr += size;
|
||||
log_wpos += size;
|
||||
return res;
|
||||
}
|
||||
eptr = wptr;
|
||||
wptr = start;
|
||||
if (rptr == eptr) {
|
||||
rptr = start;
|
||||
}
|
||||
if (cptr == eptr) {
|
||||
cptr = start;
|
||||
}
|
||||
}
|
||||
if (rptr - wptr > (long)size) {
|
||||
unsigned char* res = wptr;
|
||||
wptr += size;
|
||||
log_wpos += size;
|
||||
return res;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BinlogBuffer::flush(int mode) {
|
||||
auto r_res = try_flush(mode);
|
||||
if (r_res.is_ok()) {
|
||||
return r_res.ok();
|
||||
}
|
||||
std::string msg = PSTRING() << "cannot flush binlog file " << binlog_name << " at position " << log_rpos << " "
|
||||
<< r_res.error();
|
||||
LOG(ERROR) << msg;
|
||||
throw BinlogError{msg};
|
||||
}
|
||||
|
||||
td::Result<bool> BinlogBuffer::try_flush(int mode) {
|
||||
LOG(DEBUG) << "in flush: writing=" << writing << " r=" << rptr - start << " c=" << cptr - start
|
||||
<< " w=" << wptr - start << "; rp=" << log_rpos << " cp=" << log_cpos << " wp=" << log_wpos;
|
||||
if (!writing || rptr == cptr) {
|
||||
return false; // nothing to flush
|
||||
}
|
||||
DCHECK(!fd.empty()); // must have an open binlog file
|
||||
while (rptr != cptr) {
|
||||
unsigned char* tptr = (cptr >= rptr ? cptr : eptr);
|
||||
DCHECK(rptr <= tptr);
|
||||
auto sz = tptr - rptr;
|
||||
if (sz) {
|
||||
LOG(INFO) << "writing " << sz << " bytes to binlog " << binlog_name << " at position " << log_rpos;
|
||||
TRY_RESULT(res, fd.pwrite(td::Slice(rptr, sz), log_rpos));
|
||||
if (static_cast<td::int64>(res) != sz) {
|
||||
return td::Status::Error(PSLICE() << "written " << res << " bytes instead of " << sz);
|
||||
}
|
||||
log_rpos += sz;
|
||||
rptr += sz;
|
||||
}
|
||||
if (rptr == eptr) {
|
||||
rptr = start;
|
||||
eptr = nullptr;
|
||||
}
|
||||
}
|
||||
if (mode >= 3) {
|
||||
LOG(INFO) << "syncing binlog " << binlog_name << " (position " << log_rpos << ")";
|
||||
TRY_STATUS(fd.sync());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* BinlogBuffer::alloc_log_event(std::size_t size) {
|
||||
if (!writing) {
|
||||
throw BinlogError{"cannot create new binlog event: binlog not open for writing"};
|
||||
}
|
||||
if (size >= max_size || size > max_event_size) {
|
||||
return nullptr;
|
||||
}
|
||||
size = (size + 3) & -4;
|
||||
unsigned char* res = try_alloc_log_event(size);
|
||||
if (!res) {
|
||||
flush();
|
||||
return try_alloc_log_event(size);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
bool BinlogBuffer::commit_range(unsigned long long pos_start, unsigned long long pos_end) {
|
||||
// TODO: make something more clever, with partially committed/uncommitted segments in [cpos..wpos] range
|
||||
if (pos_start != log_cpos || pos_end < pos_start || pos_end > log_wpos) {
|
||||
return false;
|
||||
}
|
||||
if (!pos_start && pos_end >= pos_start + 4 && td::as<unsigned>(cptr) != 0x0442446b) {
|
||||
throw BinlogError{"incorrect magic"};
|
||||
}
|
||||
long long size = pos_end - pos_start;
|
||||
replay_range(cptr, pos_start, pos_end);
|
||||
log_cpos = pos_end;
|
||||
cptr += size;
|
||||
if (eptr && cptr >= eptr) {
|
||||
cptr -= eptr - start;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinlogBuffer::rollback_range(unsigned long long pos_start, unsigned long long pos_end) {
|
||||
if (pos_start < log_cpos || pos_end < pos_start || pos_end != log_wpos) {
|
||||
return false;
|
||||
}
|
||||
long long size = pos_end - pos_start;
|
||||
log_wpos = pos_end;
|
||||
if (size >= wptr - start) {
|
||||
wptr -= size;
|
||||
} else {
|
||||
DCHECK(eptr);
|
||||
wptr += eptr - start - size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BinlogBuffer::NewBinlogEvent::commit() {
|
||||
//LOG(DEBUG) << "in NewBinlogEvent::commit (status = " << status << ")";
|
||||
if (!(status & 4)) {
|
||||
throw BinlogError{"cannot commit new binlog event: already committed or rolled back"};
|
||||
}
|
||||
if (!bb.commit_range(pos, pos + size)) {
|
||||
throw BinlogError{"cannot commit new binlog event: possibly some earlier log events are not committed yet"};
|
||||
}
|
||||
status = 1;
|
||||
//LOG(DEBUG) << "after NewBinlogEvent::commit (status = " << status << ")";
|
||||
}
|
||||
|
||||
void BinlogBuffer::NewBinlogEvent::rollback() {
|
||||
if (!(status & 4)) {
|
||||
throw BinlogError{"cannot roll back new binlog event: already committed or rolled back"};
|
||||
}
|
||||
if (!bb.rollback_range(pos, pos + size)) {
|
||||
throw BinlogError{"cannot roll back new binlog event: possibly some later log event are already committed"};
|
||||
}
|
||||
status = 2;
|
||||
}
|
||||
|
||||
BinlogBuffer::NewBinlogEvent::~NewBinlogEvent() {
|
||||
if (status & 4) {
|
||||
if (status == 5) {
|
||||
status = 4;
|
||||
commit();
|
||||
} else if (status == 6) {
|
||||
status = 4;
|
||||
rollback();
|
||||
} else {
|
||||
LOG(ERROR) << "newly-allocated binlog event is neither committed nor rolled back (automatically rolling back)";
|
||||
rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BinlogBuffer::replay_range(unsigned char* ptr, unsigned long long pos_start, unsigned long long pos_end) {
|
||||
unsigned char* tptr = (ptr <= wptr ? wptr : eptr);
|
||||
long long avail = tptr - ptr;
|
||||
while (pos_start < pos_end) {
|
||||
if (ptr == eptr) {
|
||||
ptr = start;
|
||||
tptr = wptr;
|
||||
avail = tptr - ptr;
|
||||
if (avail > (long long)(pos_end - pos_start)) {
|
||||
avail = pos_end - pos_start;
|
||||
}
|
||||
}
|
||||
int res = (avail >= 4 ? cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(ptr),
|
||||
td::narrow_cast<size_t>(avail), pos_start)
|
||||
: -0x7ffffffc);
|
||||
if (res <= 0 || res > avail) {
|
||||
std::ostringstream ss;
|
||||
ss << "cannot interpret newly-committed binlog event 0x" << std::hex
|
||||
<< (avail >= 4 ? (unsigned)td::as<unsigned>(ptr) : 0u) << std::dec << ": error " << res;
|
||||
throw BinlogError{ss.str()};
|
||||
}
|
||||
ptr += res;
|
||||
pos_start += res;
|
||||
avail -= res;
|
||||
}
|
||||
}
|
||||
|
||||
int BinlogBuffer::replay_pending(bool allow_partial) {
|
||||
if (rptr == cptr) {
|
||||
return 0;
|
||||
}
|
||||
unsigned char* tptr = (rptr <= cptr ? cptr : eptr);
|
||||
long long avail = tptr - rptr;
|
||||
DCHECK(tptr && avail >= 0);
|
||||
while (rptr != cptr) {
|
||||
int res = (avail >= 4 ? cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(rptr),
|
||||
td::narrow_cast<size_t>(avail), log_rpos)
|
||||
: -0x7ffffffc);
|
||||
if (res > 0) {
|
||||
if (res > avail) {
|
||||
throw BinlogError{"binlog event used more bytes than available"};
|
||||
}
|
||||
avail -= res;
|
||||
log_rpos += res;
|
||||
rptr += res;
|
||||
if (rptr != eptr) {
|
||||
continue;
|
||||
}
|
||||
rptr = start;
|
||||
tptr = cptr;
|
||||
avail = tptr - rptr;
|
||||
continue;
|
||||
}
|
||||
long long prev_need = 0;
|
||||
while (res < -0x40000000) {
|
||||
long long need = res - 0x80000000;
|
||||
need = (need + 3) & -4;
|
||||
if (need > (long long)max_event_size) {
|
||||
throw BinlogError{"binlog event requires too many bytes"};
|
||||
}
|
||||
if (need <= avail) {
|
||||
throw BinlogError{"binlog event requires more bytes, but we already had them"};
|
||||
}
|
||||
if (need <= prev_need) {
|
||||
throw BinlogError{"binlog event requires more bytes, but we already had them"};
|
||||
}
|
||||
prev_need = need;
|
||||
long long total_avail = avail + (rptr > cptr ? cptr - start : 0);
|
||||
if (need > total_avail) {
|
||||
if (allow_partial) {
|
||||
need_more_bytes = td::narrow_cast<size_t>(need - total_avail);
|
||||
return 2;
|
||||
} else {
|
||||
throw BinlogError{"binlog event extends past end of buffer"};
|
||||
}
|
||||
}
|
||||
if (need <= 1024) {
|
||||
unsigned char tmp[1024];
|
||||
std::memcpy(tmp, rptr, td::narrow_cast<size_t>(avail));
|
||||
std::memcpy(tmp + avail, start, td::narrow_cast<size_t>(need - avail));
|
||||
res = cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(tmp), td::narrow_cast<size_t>(need),
|
||||
log_rpos);
|
||||
} else {
|
||||
unsigned char* tmp = static_cast<unsigned char*>(std::malloc(td::narrow_cast<size_t>(need)));
|
||||
std::memcpy(tmp, rptr, td::narrow_cast<size_t>(avail));
|
||||
std::memcpy(tmp + avail, start, td::narrow_cast<size_t>(need - avail));
|
||||
res = cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(tmp), td::narrow_cast<size_t>(need),
|
||||
log_rpos);
|
||||
std::free(tmp);
|
||||
}
|
||||
if (res > need) {
|
||||
throw BinlogError{"binlog event used more bytes than available"};
|
||||
}
|
||||
}
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
if (!res) {
|
||||
throw BinlogError{"unknown error while interpreting binlog event"};
|
||||
}
|
||||
if (res < avail) {
|
||||
avail -= res;
|
||||
log_rpos += res;
|
||||
rptr += res;
|
||||
continue;
|
||||
}
|
||||
DCHECK(eptr);
|
||||
log_rpos += res;
|
||||
rptr += res;
|
||||
rptr = start + (rptr - eptr);
|
||||
eptr = nullptr;
|
||||
DCHECK(start <= rptr && rptr <= cptr && cptr <= wptr && wptr <= end);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
BinlogBuffer::~BinlogBuffer() {
|
||||
if (start) {
|
||||
if (writing) {
|
||||
flush(2);
|
||||
}
|
||||
std::free(start);
|
||||
}
|
||||
}
|
||||
|
||||
td::Status BinlogBuffer::set_binlog(std::string new_binlog_name, int mode) {
|
||||
if (!binlog_name.empty() || !fd.empty()) {
|
||||
return td::Status::Error("binlog buffer already attached to a file");
|
||||
}
|
||||
td::int32 flags = td::FileFd::Read;
|
||||
if ((mode & 1) != 0) {
|
||||
flags |= td::FileFd::Write;
|
||||
}
|
||||
auto r_fd = td::FileFd::open(new_binlog_name, flags, 0640);
|
||||
if (r_fd.is_error()) {
|
||||
if (!(~mode & 3)) {
|
||||
TRY_RESULT(new_fd, td::FileFd::open(new_binlog_name, flags | td::FileFd::CreateNew, 0640));
|
||||
fd = std::move(new_fd);
|
||||
created = true;
|
||||
} else {
|
||||
return r_fd.move_as_error();
|
||||
}
|
||||
} else {
|
||||
fd = r_fd.move_as_ok();
|
||||
}
|
||||
replica = !(mode & 1);
|
||||
if (!replica) {
|
||||
TRY_STATUS(fd.lock(td::FileFd::LockFlags::Write, new_binlog_name, 100));
|
||||
}
|
||||
if (created) {
|
||||
writing = true;
|
||||
td::Status res;
|
||||
try {
|
||||
res = cb->init_new_binlog(*this);
|
||||
} catch (BinlogBuffer::BinlogError& err) {
|
||||
res = td::Status::Error(err.msg);
|
||||
}
|
||||
if (res.is_error()) {
|
||||
fd.close();
|
||||
td::unlink(new_binlog_name).ignore();
|
||||
writing = false;
|
||||
return res;
|
||||
}
|
||||
binlog_name = new_binlog_name;
|
||||
ok = true;
|
||||
return td::Status::OK();
|
||||
}
|
||||
binlog_name = new_binlog_name;
|
||||
auto res = replay_binlog(replica);
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
if (!replica) {
|
||||
if (log_rpos != log_wpos || log_rpos != log_cpos || rptr != wptr || rptr != cptr) {
|
||||
std::string msg = (PSLICE() << "error while interpreting binlog `" << binlog_name << "`: " << log_wpos - log_rpos
|
||||
<< " bytes left uninterpreted at position " << log_rpos << ", truncated binlog?")
|
||||
.c_str();
|
||||
LOG(ERROR) << msg;
|
||||
return td::Status::Error(msg);
|
||||
}
|
||||
//rptr = wptr = cptr = start;
|
||||
//eptr = nullptr;
|
||||
LOG(INFO) << "read and interpreted " << res.move_as_ok() << " bytes from binlog `" << binlog_name
|
||||
<< "`, final position " << log_rpos << ", reopening in write mode";
|
||||
writing = true;
|
||||
if (!log_rpos) {
|
||||
td::Status status;
|
||||
try {
|
||||
status = cb->init_new_binlog(*this);
|
||||
} catch (BinlogBuffer::BinlogError& err) {
|
||||
status = td::Status::Error(err.msg);
|
||||
}
|
||||
if (status.is_error()) {
|
||||
fd.close();
|
||||
td::unlink(new_binlog_name).ignore();
|
||||
writing = false;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok = true;
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Result<long long> BinlogBuffer::replay_binlog(bool allow_partial) {
|
||||
if (writing) {
|
||||
return 0;
|
||||
}
|
||||
long long total = 0;
|
||||
while (true) {
|
||||
auto res = read_file();
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
long long sz = res.move_as_ok();
|
||||
total += sz;
|
||||
try {
|
||||
cptr = wptr;
|
||||
log_cpos = log_wpos;
|
||||
if (!log_rpos && rptr == start && wptr >= rptr + 4 && td::as<unsigned>(rptr) != 0x0442446b) {
|
||||
throw BinlogError{"incorrect magic"};
|
||||
}
|
||||
int r = replay_pending(allow_partial || sz != 0);
|
||||
if (r < 0 && r >= -0x40000000) {
|
||||
throw InterpretError{(PSLICE() << "binlog error " << r).c_str()};
|
||||
}
|
||||
} catch (BinlogError err) {
|
||||
LOG(ERROR) << "error reading binlog " << binlog_name << ": " << err.msg << " at position " << log_rpos;
|
||||
return td::Status::Error(PSLICE() << "error reading binlog " << binlog_name << ": " << err.msg << " at position "
|
||||
<< log_rpos);
|
||||
} catch (InterpretError err) {
|
||||
LOG(ERROR) << "error interpreting binlog " << binlog_name << ": " << err.msg << " at position " << log_rpos;
|
||||
return td::Status::Error(PSLICE() << "error interpreting binlog " << binlog_name << ": " << err.msg
|
||||
<< " at position " << log_rpos);
|
||||
}
|
||||
if (!sz) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
return total;
|
||||
}
|
||||
|
||||
td::Result<int> BinlogBuffer::read_file() {
|
||||
unsigned char* ptr = wptr;
|
||||
std::size_t sz = end - wptr;
|
||||
if (rptr > wptr) {
|
||||
DCHECK(eptr);
|
||||
sz = rptr - wptr;
|
||||
if (sz <= 4) {
|
||||
return 0; // buffer full
|
||||
}
|
||||
sz -= 4;
|
||||
} else if (!sz) {
|
||||
DCHECK(!eptr);
|
||||
if (rptr <= start + 4) {
|
||||
return 0; // buffer full
|
||||
}
|
||||
eptr = end;
|
||||
ptr = wptr = start;
|
||||
sz = rptr - start - 4;
|
||||
}
|
||||
auto r_res = fd.pread(td::MutableSlice(ptr, sz), log_wpos);
|
||||
if (r_res.is_error()) {
|
||||
std::string msg = PSTRING() << "error reading binlog file `" << binlog_name << "` at position " << log_wpos << " : "
|
||||
<< r_res.error();
|
||||
LOG(ERROR) << msg;
|
||||
return td::Status::Error(msg);
|
||||
}
|
||||
auto res = r_res.move_as_ok();
|
||||
DCHECK(std::size_t(res) <= sz);
|
||||
LOG(INFO) << "read " << res << " bytes from binlog `" << binlog_name << "` at position " << log_wpos;
|
||||
log_wpos += res;
|
||||
wptr += res;
|
||||
return (int)res;
|
||||
}
|
||||
} // namespace block
|
157
crypto/block/Binlog.h
Normal file
157
crypto/block/Binlog.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
|
||||
namespace block {
|
||||
/*
|
||||
*
|
||||
* BINLOG (BUFFERS) -- move to separate file
|
||||
*
|
||||
*/
|
||||
|
||||
class BinlogBuffer;
|
||||
|
||||
class BinlogCallback {
|
||||
public:
|
||||
virtual ~BinlogCallback() = default;
|
||||
virtual td::Status init_new_binlog(BinlogBuffer& bb) = 0;
|
||||
virtual int replay_log_event(BinlogBuffer& bb, const unsigned* ptr, std::size_t len, unsigned long long pos) = 0;
|
||||
};
|
||||
|
||||
class BinlogBuffer {
|
||||
static constexpr std::size_t max_event_size = 0xfffc;
|
||||
std::unique_ptr<BinlogCallback> cb;
|
||||
std::size_t max_size;
|
||||
std::size_t need_more_bytes;
|
||||
unsigned char *start, *rptr, *cptr, *wptr, *eptr, *end;
|
||||
unsigned long long log_rpos, log_cpos, log_wpos;
|
||||
std::string binlog_name;
|
||||
td::FileFd fd;
|
||||
bool replica;
|
||||
bool writing;
|
||||
bool dirty;
|
||||
bool created;
|
||||
bool ok;
|
||||
td::Result<int> read_file();
|
||||
td::Result<long long> replay_binlog(bool allow_partial);
|
||||
unsigned char* try_alloc_log_event(std::size_t size);
|
||||
int replay_pending(bool allow_partial = false);
|
||||
void replay_range(unsigned char* ptr, unsigned long long pos_start, unsigned long long pos_end);
|
||||
td::Status set_fd(td::FileFd fd);
|
||||
|
||||
public:
|
||||
struct LevAllocError {
|
||||
std::size_t size;
|
||||
explicit LevAllocError(std::size_t _size) : size(_size) {
|
||||
}
|
||||
};
|
||||
struct InterpretError {
|
||||
std::string msg;
|
||||
explicit InterpretError(std::string _msg) : msg(_msg) {
|
||||
}
|
||||
};
|
||||
struct BinlogError {
|
||||
std::string msg;
|
||||
explicit BinlogError(std::string _msg) : msg(_msg) {
|
||||
}
|
||||
};
|
||||
BinlogBuffer(std::unique_ptr<BinlogCallback> _cb, std::size_t _max_size = (1 << 24), td::FileFd fd = {});
|
||||
BinlogBuffer(const BinlogBuffer&) = delete;
|
||||
BinlogBuffer& operator=(const BinlogBuffer&) = delete;
|
||||
BinlogBuffer(BinlogBuffer&&) = delete;
|
||||
BinlogBuffer& operator=(BinlogBuffer&&) = delete;
|
||||
~BinlogBuffer();
|
||||
td::Status set_binlog(std::string _binlog_name, int mode = 0);
|
||||
unsigned char* alloc_log_event(std::size_t size);
|
||||
unsigned char* alloc_log_event_force(std::size_t size);
|
||||
bool flush(int mode = 0);
|
||||
td::Result<bool> try_flush(int mode);
|
||||
unsigned long long get_rpos() const {
|
||||
return log_rpos;
|
||||
}
|
||||
//
|
||||
class NewBinlogEvent {
|
||||
protected:
|
||||
BinlogBuffer& bb;
|
||||
unsigned long long pos;
|
||||
unsigned size;
|
||||
int status;
|
||||
|
||||
public:
|
||||
NewBinlogEvent(BinlogBuffer& _bb, unsigned long long _pos, unsigned _size)
|
||||
: bb(_bb), pos(_pos), size(_size), status(4) {
|
||||
}
|
||||
~NewBinlogEvent();
|
||||
unsigned long long get_log_pos() const {
|
||||
return pos;
|
||||
}
|
||||
void commit();
|
||||
void commit_later() {
|
||||
if (status & 4) {
|
||||
status = 5;
|
||||
}
|
||||
}
|
||||
void rollback();
|
||||
void rollback_later() {
|
||||
if (status & 4) {
|
||||
status = 6;
|
||||
}
|
||||
};
|
||||
};
|
||||
template <typename T>
|
||||
class NewEvent : public NewBinlogEvent {
|
||||
T* ptr;
|
||||
|
||||
protected:
|
||||
friend class BinlogBuffer;
|
||||
NewEvent(BinlogBuffer& _bb, unsigned long long _pos, unsigned _size, T* _ptr)
|
||||
: NewBinlogEvent(_bb, _pos, _size), ptr(_ptr) {
|
||||
}
|
||||
|
||||
public:
|
||||
T* operator->() const {
|
||||
return ptr;
|
||||
}
|
||||
T& operator*() const {
|
||||
return *ptr;
|
||||
}
|
||||
void commit() {
|
||||
NewBinlogEvent::commit();
|
||||
ptr = nullptr;
|
||||
}
|
||||
void rollback() {
|
||||
NewBinlogEvent::rollback();
|
||||
ptr = nullptr;
|
||||
}
|
||||
};
|
||||
template <typename T, typename... Args>
|
||||
NewEvent<T> alloc(Args... args) {
|
||||
unsigned long long pos = log_wpos;
|
||||
return NewEvent<T>(*this, pos, sizeof(T), new (alloc_log_event_force(sizeof(T))) T(args...));
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class NewBinlogEvent;
|
||||
bool commit_range(unsigned long long pos_start, unsigned long long pos_end);
|
||||
bool rollback_range(unsigned long long pos_start, unsigned long long pos_end);
|
||||
};
|
||||
} // namespace block
|
111
crypto/block/block-binlog.h
Normal file
111
crypto/block/block-binlog.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "td/utils/int_types.h"
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace block {
|
||||
|
||||
namespace log {
|
||||
|
||||
#pragma pack(push, 4)
|
||||
|
||||
struct Start {
|
||||
static constexpr unsigned tag = 0x0442446b;
|
||||
static constexpr unsigned log_type = 0x290100;
|
||||
unsigned tag_field;
|
||||
unsigned type_field;
|
||||
unsigned created_at;
|
||||
unsigned char zerostate_root_hash[32];
|
||||
Start(const RootHash& hash, unsigned _now = 0)
|
||||
: tag_field(tag), type_field(log_type), created_at(_now ? _now : (unsigned)std::time(nullptr)) {
|
||||
td::as<RootHash>(zerostate_root_hash) = hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct SetZeroState {
|
||||
static constexpr unsigned tag = 0x63ab3cd9;
|
||||
unsigned tag_field;
|
||||
unsigned flags;
|
||||
long long file_size;
|
||||
unsigned char file_hash[32];
|
||||
unsigned char root_hash[32];
|
||||
SetZeroState(const RootHash& rhash, const FileHash& fhash, unsigned long long _fsize, unsigned _flags = 0)
|
||||
: tag_field(tag), flags(_flags), file_size(_fsize) {
|
||||
td::as<FileHash>(file_hash) = fhash;
|
||||
td::as<RootHash>(root_hash) = rhash;
|
||||
}
|
||||
};
|
||||
|
||||
struct NewBlock {
|
||||
static constexpr unsigned tag = 0x19f4bc63;
|
||||
unsigned tag_field;
|
||||
unsigned flags; // lower 8 bits = authority
|
||||
int workchain;
|
||||
unsigned seqno;
|
||||
unsigned long long shard;
|
||||
long long file_size;
|
||||
unsigned char file_hash[32];
|
||||
unsigned char root_hash[32];
|
||||
unsigned char last_bytes[8];
|
||||
NewBlock(const ton::BlockId& block, const RootHash& rhash, const FileHash& fhash, unsigned long long _fsize,
|
||||
unsigned _flags)
|
||||
: tag_field(tag)
|
||||
, flags(_flags)
|
||||
, workchain(block.workchain)
|
||||
, seqno(block.seqno)
|
||||
, shard(block.shard)
|
||||
, file_size(_fsize) {
|
||||
td::as<FileHash>(file_hash) = fhash;
|
||||
td::as<RootHash>(root_hash) = rhash;
|
||||
td::as<unsigned long long>(last_bytes) = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct NewState {
|
||||
static constexpr unsigned tag = 0x4190a21f;
|
||||
unsigned tag_field;
|
||||
unsigned flags; // lower 8 bits = authority
|
||||
int workchain;
|
||||
unsigned seqno;
|
||||
unsigned long long shard;
|
||||
long long file_size;
|
||||
unsigned char file_hash[32];
|
||||
unsigned char root_hash[32];
|
||||
unsigned char last_bytes[8];
|
||||
NewState(const ton::BlockId& state, const RootHash& rhash, const FileHash& fhash, unsigned long long _fsize,
|
||||
unsigned _flags)
|
||||
: tag_field(tag)
|
||||
, flags(_flags)
|
||||
, workchain(state.workchain)
|
||||
, seqno(state.seqno)
|
||||
, shard(state.shard)
|
||||
, file_size(_fsize) {
|
||||
td::as<FileHash>(file_hash) = fhash;
|
||||
td::as<RootHash>(root_hash) = rhash;
|
||||
td::as<unsigned long long>(last_bytes) = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace log
|
||||
} // namespace block
|
135
crypto/block/block-db-impl.h
Normal file
135
crypto/block/block-db-impl.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "vm/cells.h"
|
||||
#include "block/Binlog.h"
|
||||
#include "block/block-db.h"
|
||||
#include "block/block-binlog.h"
|
||||
|
||||
namespace block {
|
||||
using td::Ref;
|
||||
|
||||
/*
|
||||
*
|
||||
* BLOCK DATABASE
|
||||
*
|
||||
*/
|
||||
|
||||
class BlockDbImpl;
|
||||
|
||||
class BlockBinlogCallback : public BinlogCallback {
|
||||
BlockDbImpl& db;
|
||||
td::Status init_new_binlog(BinlogBuffer& bb) override;
|
||||
int replay_log_event(BinlogBuffer& bb, const unsigned* ptr, std::size_t len, unsigned long long log_pos) override;
|
||||
int replay(const block::log::Start& lev, unsigned long long log_pos) const;
|
||||
int replay(const block::log::SetZeroState& lev, unsigned long long log_pos) const;
|
||||
int replay(const block::log::NewBlock& lev, unsigned long long log_pos) const;
|
||||
int replay(const block::log::NewState& lev, unsigned long long log_pos) const;
|
||||
template <typename T>
|
||||
inline int try_interpret(const unsigned* ptr, std::size_t len, unsigned long long log_pos);
|
||||
|
||||
public:
|
||||
BlockBinlogCallback(BlockDbImpl& _db) : db(_db) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline int BlockBinlogCallback::try_interpret(const unsigned* ptr, std::size_t len, unsigned long long log_pos) {
|
||||
if (len < sizeof(T)) {
|
||||
return 0x80000000 + sizeof(T);
|
||||
} else {
|
||||
int res = replay(*reinterpret_cast<const T*>(ptr), log_pos);
|
||||
return res >= 0 ? sizeof(T) : res;
|
||||
}
|
||||
}
|
||||
|
||||
class BlockDbImpl final : public BlockDb {
|
||||
int status;
|
||||
bool allow_uninit;
|
||||
bool created;
|
||||
int depth;
|
||||
std::unique_ptr<ZerostateInfo> zstate;
|
||||
std::string base_dir;
|
||||
std::string binlog_name;
|
||||
BinlogBuffer bb;
|
||||
ton::Bits256 zstate_rhash, zstate_fhash;
|
||||
unsigned created_at;
|
||||
std::map<ton::FileHash, td::BufferSlice> file_cache;
|
||||
std::map<ton::BlockId, Ref<FileInfo>> block_info;
|
||||
std::map<ton::BlockId, Ref<FileInfo>> state_info;
|
||||
//
|
||||
td::Result<int> do_init();
|
||||
|
||||
public:
|
||||
enum FMode {
|
||||
chk_none = 0,
|
||||
chk_if_exists = 1,
|
||||
fail_if_exists = 2,
|
||||
overwrite = 4,
|
||||
chk_size_only = 16,
|
||||
chk_file_hash = 32
|
||||
};
|
||||
static constexpr const char* default_binlog_name = "blockdb";
|
||||
static constexpr const char* default_binlog_suffix = ".bin";
|
||||
static constexpr int default_depth = 4;
|
||||
BlockDbImpl(td::Result<int>& _res, std::string _base_dir, std::unique_ptr<ZerostateInfo> _zstate = nullptr,
|
||||
bool _allow_uninit = false, int _depth = 4, std::string _binlog_name = "");
|
||||
~BlockDbImpl();
|
||||
bool ok() const {
|
||||
return status >= 0;
|
||||
}
|
||||
bool initialized() const {
|
||||
return status != 0;
|
||||
}
|
||||
bool init_ok() const {
|
||||
return status > 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class BlockBinlogCallback;
|
||||
td::Ref<FileInfo> zerostate;
|
||||
td::Status init_from_zstate();
|
||||
td::Status update_block_info(Ref<FileInfo> blk_info);
|
||||
td::Status update_state_info(Ref<FileInfo> state);
|
||||
|
||||
private:
|
||||
std::string compute_db_filename(const FileHash& file_hash) const;
|
||||
std::string compute_db_tmp_filename(const FileHash& file_hash, int i, bool makedirs) const;
|
||||
td::Status save_db_file(const FileHash& file_hash, const td::BufferSlice& data, int fmode = 0);
|
||||
td::Status load_data(FileInfo& file_info, bool force = false);
|
||||
// actor BlockDb implementation
|
||||
void get_top_block_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) override;
|
||||
void get_top_block_state_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) override;
|
||||
void get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) override;
|
||||
void get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) override;
|
||||
void get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::Ref<OutputQueueInfoDescr>> promise) override;
|
||||
void get_object_by_file_hash(FileHash file_hash, bool need_data, bool force_file_load,
|
||||
td::Promise<td::Ref<FileInfo>> promise) override;
|
||||
void get_object_by_root_hash(RootHash root_hash, bool need_data, bool force_file_load,
|
||||
td::Promise<td::Ref<FileInfo>> promise) override;
|
||||
void save_new_block(ton::BlockIdExt blk_id, td::BufferSlice data, int authority,
|
||||
td::Promise<td::Unit> promise) override;
|
||||
void save_new_state(ton::BlockIdExt state_id, td::BufferSlice data, int authority,
|
||||
td::Promise<td::Unit> promise) override;
|
||||
bool file_cache_insert(const FileHash& file_hash, const td::BufferSlice& data, int mode = 0);
|
||||
};
|
||||
|
||||
} // namespace block
|
835
crypto/block/block-db.cpp
Normal file
835
crypto/block/block-db.cpp
Normal file
|
@ -0,0 +1,835 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "block-db.h"
|
||||
#include "block-db-impl.h"
|
||||
#include "block-binlog.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "vm/cellslice.h"
|
||||
#include "vm/boc.h"
|
||||
#include "vm/db/StaticBagOfCellsDb.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace block {
|
||||
|
||||
//static constexpr std::string default_binlog_name = "blockdb";
|
||||
//static constexpr std::string default_binlog_suffix = ".bin";
|
||||
|
||||
bool parse_hash_string(std::string arg, RootHash& res) {
|
||||
if (arg.size() != 64) {
|
||||
res.set_zero();
|
||||
return false;
|
||||
}
|
||||
int f = 1;
|
||||
unsigned char* ptr = res.data();
|
||||
for (char c : arg) {
|
||||
f <<= 4;
|
||||
if (c >= '0' && c <= '9') {
|
||||
f += c - '0';
|
||||
} else {
|
||||
c |= 0x20;
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
f += c - ('a' - 10);
|
||||
} else {
|
||||
res.set_zero();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (f >= 0x100) {
|
||||
*ptr++ = (unsigned char)f;
|
||||
f = 1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
td::Result<td::BufferSlice> load_binary_file(std::string filename, td::int64 max_size) {
|
||||
//TODO: use td::read_file
|
||||
auto res = [&]() -> td::Result<td::BufferSlice> {
|
||||
TRY_RESULT(fd, td::FileFd::open(filename, td::FileFd::Read));
|
||||
TRY_RESULT(stat, fd.stat());
|
||||
if (!stat.is_reg_) {
|
||||
return td::Status::Error("file is not regular");
|
||||
}
|
||||
td::int64 size = stat.size_;
|
||||
if (!size) {
|
||||
return td::Status::Error("file is empty");
|
||||
}
|
||||
if ((max_size && size > max_size) || static_cast<td::uint64>(size) > std::numeric_limits<std::size_t>::max()) {
|
||||
return td::Status::Error("file is too long");
|
||||
}
|
||||
td::BufferSlice res(td::narrow_cast<std::size_t>(size));
|
||||
TRY_RESULT(r, fd.read(res.as_slice()));
|
||||
if (r != static_cast<td::uint64>(size)) {
|
||||
return td::Status::Error(PSLICE() << "read " << r << " bytes out of " << size);
|
||||
}
|
||||
return std::move(res);
|
||||
}();
|
||||
LOG_IF(ERROR, res.is_error()) << "error reading file `" << filename << "` : " << res.error();
|
||||
return res;
|
||||
}
|
||||
|
||||
td::Status save_binary_file(std::string filename, const td::BufferSlice& data, unsigned long long max_size) {
|
||||
//TODO: use td::write_file
|
||||
auto status = [&]() {
|
||||
if (max_size && data.size() > max_size) {
|
||||
return td::Status::Error("contents too long");
|
||||
}
|
||||
auto size = data.size();
|
||||
TRY_RESULT(to_file, td::FileFd::open(filename, td::FileFd::CreateNew | td::FileFd::Write));
|
||||
TRY_RESULT(written, to_file.write(data));
|
||||
if (written != static_cast<size_t>(size)) {
|
||||
return td::Status::Error(PSLICE() << "written " << written << " bytes instead of " << size);
|
||||
}
|
||||
to_file.close();
|
||||
return td::Status::OK();
|
||||
}();
|
||||
LOG_IF(ERROR, status.is_error()) << "error writing new file `" << filename << "` : " << status;
|
||||
return status;
|
||||
}
|
||||
|
||||
FileHash compute_file_hash(const td::BufferSlice& data) {
|
||||
ton::Bits256 data_hash;
|
||||
td::sha256(data, td::MutableSlice{data_hash.data(), 32});
|
||||
return data_hash;
|
||||
}
|
||||
|
||||
FileHash compute_file_hash(td::Slice data) {
|
||||
ton::Bits256 data_hash;
|
||||
td::sha256(data, td::MutableSlice{data_hash.data(), 32});
|
||||
return data_hash;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* ZEROSTATE CONFIGURATION
|
||||
*
|
||||
*/
|
||||
|
||||
td::Status ZerostateInfo::base_check() {
|
||||
if (!has_data()) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
auto data_hash = compute_file_hash(data);
|
||||
if (!has_file_hash()) {
|
||||
file_hash = data_hash;
|
||||
} else if (file_hash != data_hash) {
|
||||
return td::Status::Error("zerostate file hash mismatch");
|
||||
}
|
||||
vm::BagOfCells boc;
|
||||
auto res = boc.deserialize(data);
|
||||
if (!res.is_ok() || boc.get_root_count() != 1) {
|
||||
return td::Status::Error("zerostate is not a valid bag of cells"); // not a valid bag-of-Cells
|
||||
}
|
||||
data_hash = boc.get_root_cell()->get_hash().bits();
|
||||
if (!has_root_hash()) {
|
||||
root_hash = data_hash;
|
||||
} else if (root_hash != data_hash) {
|
||||
return td::Status::Error("zerostate root hash mismatch");
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* BLOCK DATABASE
|
||||
*
|
||||
*/
|
||||
|
||||
std::string compute_db_filename(std::string base_dir, const FileHash& file_hash, int depth) {
|
||||
static const char hex_digits[] = "0123456789ABCDEF";
|
||||
assert(depth >= 0 && depth <= 8);
|
||||
std::string res = std::move(base_dir);
|
||||
res.reserve(res.size() + 32 + depth * 3 + 4);
|
||||
for (int i = 0; i < depth; i++) {
|
||||
unsigned u = file_hash.data()[i];
|
||||
res.push_back(hex_digits[u >> 4]);
|
||||
res.push_back(hex_digits[u & 15]);
|
||||
res.push_back('/');
|
||||
}
|
||||
for (int i = 0; i < 32; i++) {
|
||||
unsigned u = file_hash.data()[i];
|
||||
res.push_back(hex_digits[u >> 4]);
|
||||
res.push_back(hex_digits[u & 15]);
|
||||
}
|
||||
res += ".boc";
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string BlockDbImpl::compute_db_filename(const FileHash& file_hash) const {
|
||||
return block::compute_db_filename(base_dir, file_hash, depth);
|
||||
}
|
||||
|
||||
std::string compute_db_tmp_filename(std::string base_dir, const FileHash& file_hash, int i, bool makedirs, int depth) {
|
||||
static const char hex_digits[] = "0123456789ABCDEF";
|
||||
assert(depth >= 0 && depth <= 8);
|
||||
std::string res = std::move(base_dir);
|
||||
res.reserve(res.size() + 32 + depth * 3 + 4);
|
||||
for (int j = 0; j < depth; j++) {
|
||||
unsigned u = file_hash.data()[j];
|
||||
res.push_back(hex_digits[u >> 4]);
|
||||
res.push_back(hex_digits[u & 15]);
|
||||
res.push_back('/');
|
||||
if (makedirs) {
|
||||
td::mkdir(res, 0755).ignore();
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < 32; j++) {
|
||||
unsigned u = file_hash.data()[j];
|
||||
res.push_back(hex_digits[u >> 4]);
|
||||
res.push_back(hex_digits[u & 15]);
|
||||
}
|
||||
res += ".tmp";
|
||||
if (i > 0) {
|
||||
if (i < 10) {
|
||||
res.push_back((char)('0' + i));
|
||||
} else {
|
||||
res.push_back((char)('0' + i / 10));
|
||||
res.push_back((char)('0' + i % 10));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string BlockDbImpl::compute_db_tmp_filename(const FileHash& file_hash, int i, bool makedirs) const {
|
||||
return block::compute_db_tmp_filename(base_dir, file_hash, i, makedirs, depth);
|
||||
}
|
||||
|
||||
bool BlockDbImpl::file_cache_insert(const FileHash& file_hash, const td::BufferSlice& data, int mode) {
|
||||
auto it = file_cache.find(file_hash);
|
||||
if (it != file_cache.end()) {
|
||||
// found
|
||||
return true;
|
||||
}
|
||||
auto res = file_cache.emplace(file_hash, data.clone());
|
||||
return res.second;
|
||||
}
|
||||
|
||||
td::Status BlockDbImpl::save_db_file(const FileHash& file_hash, const td::BufferSlice& data, int fmode) {
|
||||
if (fmode & FMode::chk_file_hash && file_hash != compute_file_hash(data)) {
|
||||
return td::Status::Error("file hash passed for creation of a new file does not match contents");
|
||||
}
|
||||
std::string filename = compute_db_filename(file_hash);
|
||||
bool overwrite = false;
|
||||
auto r_stat = td::stat(filename);
|
||||
if (r_stat.is_ok()) {
|
||||
auto stat = r_stat.move_as_ok();
|
||||
// file exists
|
||||
if (fmode & FMode::fail_if_exists) {
|
||||
return td::Status::Error(PSLICE() << "file " << filename << " cannot be created, it already exists");
|
||||
}
|
||||
if (!(fmode & (FMode::chk_if_exists | FMode::overwrite))) {
|
||||
file_cache_insert(file_hash, data);
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (fmode & FMode::chk_if_exists) {
|
||||
if (stat.size_ != (long long)data.size()) {
|
||||
LOG(ERROR) << "file " << filename << " already exists with wrong content";
|
||||
if (!(fmode & FMode::overwrite)) {
|
||||
return td::Status::Error(PSLICE() << "file " << filename << " already exists with wrong content");
|
||||
}
|
||||
} else if (fmode & FMode::chk_size_only) {
|
||||
file_cache_insert(file_hash, data);
|
||||
return td::Status::OK();
|
||||
} else {
|
||||
auto res = load_binary_file(filename);
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
auto old_contents = res.move_as_ok();
|
||||
if (old_contents.size() != data.size() || old_contents.as_slice() != data.as_slice()) {
|
||||
LOG(ERROR) << "file " << filename << " already exists with wrong content";
|
||||
if (!(fmode & FMode::overwrite)) {
|
||||
return td::Status::Error(PSLICE() << "file " << filename << " already exists with wrong content");
|
||||
}
|
||||
} else {
|
||||
file_cache_insert(file_hash, data);
|
||||
return td::Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
overwrite = true;
|
||||
}
|
||||
std::string tmp_filename;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
tmp_filename = compute_db_tmp_filename(file_hash, i, true);
|
||||
auto res = save_binary_file(tmp_filename, data);
|
||||
if (res.is_ok()) {
|
||||
break;
|
||||
}
|
||||
if (i == 9) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
auto rename_status = td::rename(tmp_filename, filename);
|
||||
if (rename_status.is_error()) {
|
||||
td::unlink(tmp_filename).ignore();
|
||||
LOG(ERROR) << rename_status;
|
||||
return rename_status;
|
||||
}
|
||||
if (overwrite) {
|
||||
LOG(DEBUG) << "database file `" << filename << "` overwritten, " << data.size() << " bytes";
|
||||
} else {
|
||||
LOG(DEBUG) << "new database file `" << filename << "` created, " << data.size() << " bytes";
|
||||
}
|
||||
file_cache_insert(file_hash, data);
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Result<td::actor::ActorOwn<BlockDb>> BlockDb::create_block_db(std::string base_dir,
|
||||
std::unique_ptr<ZerostateInfo> zstate,
|
||||
bool allow_uninit, int depth,
|
||||
std::string binlog_name) {
|
||||
using td::actor::ActorId;
|
||||
using td::actor::ActorOwn;
|
||||
td::Result<int> res;
|
||||
ActorOwn<BlockDbImpl> actor =
|
||||
td::actor::create_actor<BlockDbImpl>(td::actor::ActorOptions().with_name("BlockDB"), res, base_dir,
|
||||
std::move(zstate), allow_uninit, depth, binlog_name);
|
||||
if (res.is_error()) {
|
||||
return std::move(res).move_as_error();
|
||||
} else {
|
||||
return std::move(actor);
|
||||
}
|
||||
}
|
||||
|
||||
BlockDbImpl::BlockDbImpl(td::Result<int>& _res, std::string _base_dir, std::unique_ptr<ZerostateInfo> _zstate,
|
||||
bool _allow_uninit, int _depth, std::string _binlog_name)
|
||||
: status(0)
|
||||
, allow_uninit(_allow_uninit)
|
||||
, created(false)
|
||||
, depth(_depth)
|
||||
, zstate(std::move(_zstate))
|
||||
, base_dir(_base_dir)
|
||||
, binlog_name(_binlog_name)
|
||||
, bb(std::unique_ptr<BinlogCallback>(new BlockBinlogCallback(*this)))
|
||||
, created_at(0) {
|
||||
auto res = do_init();
|
||||
status = (res.is_ok() && res.ok() > 0 ? res.ok() : -1);
|
||||
if (res.is_error()) {
|
||||
_res = std::move(res);
|
||||
} else {
|
||||
_res = res.move_as_ok();
|
||||
}
|
||||
}
|
||||
|
||||
td::Result<int> BlockDbImpl::do_init() {
|
||||
if (base_dir.empty()) {
|
||||
return td::Status::Error("block database cannot have empty base directory");
|
||||
}
|
||||
if (depth < 0 || depth >= 8) {
|
||||
return td::Status::Error("block database directory tree depth must be in range 0..8");
|
||||
}
|
||||
if (base_dir.back() != '/') {
|
||||
base_dir.push_back('/');
|
||||
}
|
||||
if (binlog_name.empty()) {
|
||||
binlog_name = default_binlog_name;
|
||||
}
|
||||
bool f = true;
|
||||
for (char c : binlog_name) {
|
||||
if (c == '.') {
|
||||
f = false;
|
||||
} else if (c == '/') {
|
||||
f = true;
|
||||
}
|
||||
}
|
||||
if (f) {
|
||||
binlog_name += default_binlog_suffix;
|
||||
}
|
||||
if (binlog_name.at(0) != '/') {
|
||||
binlog_name = base_dir + binlog_name;
|
||||
}
|
||||
if (zstate) {
|
||||
if (!zstate->has_data() && zstate->has_filename()) {
|
||||
auto data = load_binary_file(zstate->filename, 1 << 20);
|
||||
if (data.is_error()) {
|
||||
return data.move_as_error();
|
||||
}
|
||||
zstate->data = data.move_as_ok();
|
||||
}
|
||||
auto res = zstate->base_check();
|
||||
if (res.is_error()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
try {
|
||||
auto res = bb.set_binlog(binlog_name, allow_uninit ? 3 : 1);
|
||||
if (res.is_error()) {
|
||||
return res;
|
||||
}
|
||||
} catch (BinlogBuffer::BinlogError& err) {
|
||||
return td::Status::Error(-2, std::string{"error while initializing block database binlog: "} + err.msg);
|
||||
} catch (BinlogBuffer::InterpretError& err) {
|
||||
return td::Status::Error(-3, std::string{"error while interpreting block database binlog: "} + err.msg);
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
BlockDbImpl::~BlockDbImpl() {
|
||||
}
|
||||
|
||||
td::Status BlockDbImpl::init_from_zstate() {
|
||||
if (!zstate) {
|
||||
return td::Status::Error("no zero state provided, cannot initialize from scratch");
|
||||
}
|
||||
if (!zstate->has_data()) {
|
||||
if (zstate->has_filename() || zstate->has_file_hash()) {
|
||||
if (!zstate->has_filename()) {
|
||||
zstate->filename = compute_db_filename(zstate->file_hash);
|
||||
}
|
||||
auto res = load_binary_file(zstate->filename, 1 << 20);
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
zstate->data = res.move_as_ok();
|
||||
} else {
|
||||
return td::Status::Error("cannot load zero state for block DB creation");
|
||||
}
|
||||
}
|
||||
auto res = zstate->base_check();
|
||||
if (res.is_error()) {
|
||||
return res;
|
||||
}
|
||||
assert(zstate->has_file_hash() && zstate->has_root_hash());
|
||||
res = save_db_file(zstate->file_hash, zstate->data, FMode::chk_if_exists | FMode::chk_file_hash);
|
||||
if (res.is_error()) {
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
td::Status BlockBinlogCallback::init_new_binlog(BinlogBuffer& bb) {
|
||||
auto res = db.init_from_zstate();
|
||||
if (res.is_error()) {
|
||||
return res;
|
||||
}
|
||||
auto lev = bb.alloc<log::Start>(db.zstate->root_hash);
|
||||
assert(!lev.get_log_pos());
|
||||
auto lev2 = bb.alloc<log::SetZeroState>(db.zstate->root_hash, db.zstate->file_hash, db.zstate->data.size());
|
||||
lev.commit();
|
||||
lev2.commit(); // TODO: introduce multi-commit bb.commit(lev, lev2)
|
||||
bb.flush(3);
|
||||
db.created = true;
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
#define REPLAY_CASE(__T) \
|
||||
case __T::tag: \
|
||||
return try_interpret<__T>(ptr, len, log_pos);
|
||||
|
||||
int BlockBinlogCallback::replay_log_event(BinlogBuffer& bb, const unsigned* ptr, std::size_t len,
|
||||
unsigned long long log_pos) {
|
||||
assert(len >= 4);
|
||||
LOG(DEBUG) << "replay_log_event(" << len << ", " << log_pos << ", " << *ptr << ")";
|
||||
switch (*ptr) {
|
||||
REPLAY_CASE(log::Start);
|
||||
REPLAY_CASE(log::SetZeroState);
|
||||
REPLAY_CASE(log::NewBlock);
|
||||
REPLAY_CASE(log::NewState);
|
||||
}
|
||||
std::ostringstream ss;
|
||||
ss << "unknown binlog event 0x" << std::hex << *ptr << std::dec;
|
||||
LOG(ERROR) << ss.str() << " at position " << log_pos;
|
||||
throw BinlogBuffer::InterpretError{ss.str()};
|
||||
}
|
||||
|
||||
#undef REPLAY_CASE
|
||||
|
||||
int BlockBinlogCallback::replay(const log::Start& lev, unsigned long long log_pos) const {
|
||||
LOG(DEBUG) << "in replay(Start{" << lev.tag_field << ", " << lev.type_field << ", " << lev.created_at << "})";
|
||||
if (lev.type_field != lev.log_type) {
|
||||
throw BinlogBuffer::InterpretError{(PSLICE() << "unsupported binlog type " << lev.type_field).str()};
|
||||
}
|
||||
if (log_pos) {
|
||||
throw BinlogBuffer::InterpretError{"LEV_START can only be the very first record in a binlog"};
|
||||
}
|
||||
db.zstate_rhash = lev.zerostate_root_hash;
|
||||
db.created_at = lev.created_at;
|
||||
if (db.zstate) {
|
||||
if (!db.zstate->has_root_hash()) {
|
||||
db.zstate->root_hash = db.zstate_rhash;
|
||||
} else if (db.zstate->root_hash != db.zstate_rhash) {
|
||||
throw BinlogBuffer::InterpretError{PSTRING() << "zerostate hash mismatch: in binlog " << db.zstate_rhash.to_hex()
|
||||
<< ", required " << db.zstate->root_hash.to_hex()};
|
||||
}
|
||||
}
|
||||
return 0; // ok
|
||||
}
|
||||
|
||||
int BlockBinlogCallback::replay(const log::SetZeroState& lev, unsigned long long log_pos) const {
|
||||
LOG(DEBUG) << "in replay(SetZeroState)";
|
||||
// LOG(DEBUG) << "db.zstate_rhash = " << db.zstate_rhash.to_hex();
|
||||
if (db.zstate_rhash != td::ConstBitPtr{lev.root_hash}) {
|
||||
throw BinlogBuffer::InterpretError{std::string{"SetZeroState: zerostate root hash mismatch: in binlog "} +
|
||||
ton::Bits256{lev.root_hash}.to_hex() + ", required " + db.zstate_rhash.to_hex()};
|
||||
}
|
||||
db.zerostate = td::Ref<FileInfo>{true,
|
||||
FileType::state,
|
||||
ton::BlockId{ton::masterchainId, 1ULL << 63, 0},
|
||||
0,
|
||||
td::as<FileHash>(lev.file_hash),
|
||||
td::as<RootHash>(lev.root_hash),
|
||||
lev.file_size};
|
||||
return 0; // ok
|
||||
}
|
||||
|
||||
int BlockBinlogCallback::replay(const log::NewBlock& lev, unsigned long long log_pos) const {
|
||||
LOG(DEBUG) << "in replay(NewBlock)";
|
||||
if (!lev.seqno || lev.workchain == ton::workchainInvalid) {
|
||||
return -1;
|
||||
}
|
||||
ton::BlockId blkid{lev.workchain, lev.shard, lev.seqno};
|
||||
auto blk_info = td::Ref<FileInfo>{true,
|
||||
FileType::block,
|
||||
blkid,
|
||||
lev.flags & 0xff,
|
||||
td::as<FileHash>(lev.file_hash),
|
||||
td::as<RootHash>(lev.root_hash),
|
||||
lev.file_size};
|
||||
auto res = db.update_block_info(blk_info);
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot update block information in the local DB: " << res.to_string();
|
||||
return -1;
|
||||
} else {
|
||||
return 0; // ok
|
||||
}
|
||||
}
|
||||
|
||||
int BlockBinlogCallback::replay(const log::NewState& lev, unsigned long long log_pos) const {
|
||||
LOG(DEBUG) << "in replay(NewState)";
|
||||
if (!lev.seqno || lev.workchain == ton::workchainInvalid) {
|
||||
return -1;
|
||||
}
|
||||
ton::BlockId id{lev.workchain, lev.shard, lev.seqno};
|
||||
auto state_info = td::Ref<FileInfo>{true,
|
||||
FileType::state,
|
||||
id,
|
||||
lev.flags & 0xff,
|
||||
td::as<FileHash>(lev.file_hash),
|
||||
td::as<RootHash>(lev.root_hash),
|
||||
lev.file_size};
|
||||
auto res = db.update_state_info(state_info);
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot update shardchain state information in the local DB: " << res.to_string();
|
||||
return -1;
|
||||
} else {
|
||||
return 0; // ok
|
||||
}
|
||||
}
|
||||
|
||||
td::Status BlockDbImpl::update_block_info(Ref<FileInfo> blk_info) {
|
||||
auto it = block_info.find(blk_info->blk.id);
|
||||
if (it != block_info.end()) {
|
||||
// already exists
|
||||
if (it->second->blk.file_hash != blk_info->blk.file_hash || it->second->blk.root_hash != blk_info->blk.root_hash) {
|
||||
return td::Status::Error(-666, std::string{"fatal error in block DB: block "} + blk_info->blk.id.to_str() +
|
||||
" has two records with different file or root hashes");
|
||||
} else {
|
||||
return td::Status::OK();
|
||||
}
|
||||
} else {
|
||||
auto id = blk_info->blk.id;
|
||||
auto res = block_info.emplace(std::move(id), std::move(blk_info));
|
||||
if (res.second) {
|
||||
return td::Status::OK();
|
||||
} else {
|
||||
return td::Status::Error(-666, "cannot insert block information into DB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
td::Status BlockDbImpl::update_state_info(Ref<FileInfo> state) {
|
||||
auto it = state_info.find(state->blk.id);
|
||||
if (it != state_info.end()) {
|
||||
// already exists
|
||||
if (it->second->blk.root_hash != state->blk.root_hash) {
|
||||
return td::Status::Error(-666, std::string{"fatal error in block DB: state for block "} + state->blk.id.to_str() +
|
||||
" has two records with different root hashes");
|
||||
} else {
|
||||
return td::Status::OK();
|
||||
}
|
||||
} else {
|
||||
auto id = state->blk.id;
|
||||
auto res = state_info.emplace(std::move(id), std::move(state));
|
||||
if (res.second) {
|
||||
return td::Status::OK();
|
||||
} else {
|
||||
return td::Status::Error(-666, "cannot insert state information into DB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_top_block_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) {
|
||||
LOG(DEBUG) << "in BlockDb::get_top_block_id()";
|
||||
auto it = block_info.upper_bound(ton::BlockId{shard, std::numeric_limits<td::uint32>::max()});
|
||||
if (it != block_info.begin() && ton::ShardIdFull{(--it)->first} == shard) {
|
||||
promise(it->second->blk);
|
||||
return;
|
||||
}
|
||||
if (shard.is_masterchain()) {
|
||||
promise(ton::BlockIdExt{ton::BlockId{ton::masterchainId, 1ULL << 63, 0}});
|
||||
return;
|
||||
}
|
||||
promise(td::Status::Error(-666));
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_top_block_state_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) {
|
||||
LOG(DEBUG) << "in BlockDb::get_top_block_state_id()";
|
||||
auto it = state_info.upper_bound(ton::BlockId{shard, std::numeric_limits<td::uint32>::max()});
|
||||
if (it != state_info.begin() && ton::ShardIdFull{(--it)->first} == shard) {
|
||||
promise(it->second->blk);
|
||||
return;
|
||||
}
|
||||
if (shard.is_masterchain() && zerostate.not_null()) {
|
||||
promise(zerostate->blk);
|
||||
return;
|
||||
}
|
||||
promise(td::Status::Error(-666, "no state for given workchain found in database"));
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) {
|
||||
LOG(DEBUG) << "in BlockDb::get_block_by_id({" << blk_id.workchain << ", " << blk_id.shard << ", " << blk_id.seqno
|
||||
<< "}, " << need_data << ")";
|
||||
auto it = block_info.find(blk_id);
|
||||
if (it != block_info.end()) {
|
||||
if (need_data && it->second->data.is_null()) {
|
||||
LOG(DEBUG) << "loading data for block " << blk_id.to_str();
|
||||
auto res = load_data(it->second.write());
|
||||
if (res.is_error()) {
|
||||
promise(std::move(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
promise(it->second);
|
||||
}
|
||||
promise(td::Status::Error(-666, "block not found in database"));
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) {
|
||||
LOG(DEBUG) << "in BlockDb::get_state_by_id({" << blk_id.workchain << ", " << blk_id.shard << ", " << blk_id.seqno
|
||||
<< "}, " << need_data << ")";
|
||||
auto it = state_info.find(blk_id);
|
||||
if (it != state_info.end()) {
|
||||
if (need_data && it->second->data.is_null()) {
|
||||
LOG(DEBUG) << "loading data for state " << blk_id.to_str();
|
||||
auto res = load_data(it->second.write());
|
||||
if (res.is_error()) {
|
||||
promise(std::move(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
promise(it->second);
|
||||
}
|
||||
if (zerostate.not_null() && blk_id == zerostate->blk.id) {
|
||||
LOG(DEBUG) << "get_state_by_id(): zerostate requested";
|
||||
if (need_data && zerostate->data.is_null()) {
|
||||
LOG(DEBUG) << "loading data for zerostate";
|
||||
auto res = load_data(zerostate.write());
|
||||
if (res.is_error()) {
|
||||
promise(std::move(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
promise(zerostate);
|
||||
return;
|
||||
}
|
||||
promise(td::Status::Error(-666, "requested state not found in database"));
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::Ref<OutputQueueInfoDescr>> promise) {
|
||||
LOG(DEBUG) << "in BlockDb::get_out_queue_info_by_id({" << blk_id.workchain << ", " << blk_id.shard << ", "
|
||||
<< blk_id.seqno << ")";
|
||||
auto it = state_info.find(blk_id);
|
||||
if (it == state_info.end()) {
|
||||
promise(td::Status::Error(
|
||||
-666, std::string{"cannot obtain output queue info for block "} + blk_id.to_str() + " : cannot load state"));
|
||||
}
|
||||
if (it->second->data.is_null()) {
|
||||
LOG(DEBUG) << "loading data for state " << blk_id.to_str();
|
||||
auto res = load_data(it->second.write());
|
||||
if (res.is_error()) {
|
||||
promise(std::move(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto it2 = block_info.find(blk_id);
|
||||
if (it2 == block_info.end()) {
|
||||
promise(td::Status::Error(-666, std::string{"cannot obtain output queue info for block "} + blk_id.to_str() +
|
||||
" : cannot load block description"));
|
||||
}
|
||||
vm::StaticBagOfCellsDbLazy::Options options;
|
||||
auto res = vm::StaticBagOfCellsDbLazy::create(it->second->data.clone(), options);
|
||||
if (res.is_error()) {
|
||||
td::Status err = res.move_as_error();
|
||||
LOG(ERROR) << "cannot deserialize state for block " << blk_id.to_str() << " : " << err.to_string();
|
||||
promise(std::move(err));
|
||||
return;
|
||||
}
|
||||
auto static_boc = res.move_as_ok();
|
||||
auto rc = static_boc->get_root_count();
|
||||
if (rc.is_error()) {
|
||||
promise(rc.move_as_error());
|
||||
return;
|
||||
}
|
||||
if (rc.move_as_ok() != 1) {
|
||||
promise(td::Status::Error(-668, std::string{"state for block "} + blk_id.to_str() + " is invalid"));
|
||||
return;
|
||||
}
|
||||
auto res3 = static_boc->get_root_cell(0);
|
||||
if (res3.is_error()) {
|
||||
promise(res3.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto state_root = res3.move_as_ok();
|
||||
if (it->second->blk.root_hash != state_root->get_hash().bits()) {
|
||||
promise(td::Status::Error(
|
||||
-668, std::string{"state for block "} + blk_id.to_str() + " is invalid : state root hash mismatch"));
|
||||
}
|
||||
vm::CellSlice cs = vm::load_cell_slice(state_root);
|
||||
if (!cs.have(64, 1) || cs.prefetch_ulong(32) != 0x9023afde) {
|
||||
promise(td::Status::Error(-668, std::string{"state for block "} + blk_id.to_str() + " is invalid"));
|
||||
}
|
||||
auto out_queue_info = cs.prefetch_ref();
|
||||
promise(Ref<OutputQueueInfoDescr>{true, blk_id, it2->second->blk.root_hash.cbits(), state_root->get_hash().bits(),
|
||||
std::move(out_queue_info)});
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_object_by_file_hash(FileHash file_hash, bool need_data, bool force_file_load,
|
||||
td::Promise<td::Ref<FileInfo>> promise) {
|
||||
if (zerostate.not_null() && zerostate->blk.file_hash == file_hash) {
|
||||
if (need_data && zerostate->data.is_null()) {
|
||||
auto res = load_data(zerostate.write());
|
||||
if (res.is_error()) {
|
||||
promise(std::move(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
promise(zerostate);
|
||||
return;
|
||||
}
|
||||
promise(td::Status::Error(-666));
|
||||
}
|
||||
|
||||
void BlockDbImpl::get_object_by_root_hash(RootHash root_hash, bool need_data, bool force_file_load,
|
||||
td::Promise<td::Ref<FileInfo>> promise) {
|
||||
if (zerostate.not_null() && zerostate->blk.root_hash == root_hash) {
|
||||
if (need_data && zerostate->data.is_null()) {
|
||||
auto res = load_data(zerostate.write());
|
||||
if (res.is_error()) {
|
||||
promise(std::move(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
promise(zerostate);
|
||||
return;
|
||||
}
|
||||
promise(td::Status::Error(-666));
|
||||
}
|
||||
|
||||
void BlockDbImpl::save_new_block(ton::BlockIdExt id, td::BufferSlice data, int authority,
|
||||
td::Promise<td::Unit> promise) {
|
||||
// TODO: add verification that data is a BoC with correct root hash, and that it is a Block corresponding to blk_id
|
||||
// ...
|
||||
// TODO: check whether we already have a block with blk_id
|
||||
// ...
|
||||
auto save_res = save_db_file(id.file_hash, data, FMode::chk_if_exists | FMode::overwrite | FMode::chk_file_hash);
|
||||
if (save_res.is_error()) {
|
||||
promise(std::move(save_res));
|
||||
}
|
||||
auto sz = data.size();
|
||||
auto lev = bb.alloc<log::NewBlock>(id.id, id.root_hash, id.file_hash, data.size(), authority & 0xff);
|
||||
if (sz <= 8) {
|
||||
std::memcpy(lev->last_bytes, data.data(), sz);
|
||||
} else {
|
||||
std::memcpy(lev->last_bytes, data.data() + sz - 8, 8);
|
||||
}
|
||||
lev.commit();
|
||||
bb.flush();
|
||||
promise(td::Unit{});
|
||||
}
|
||||
|
||||
void BlockDbImpl::save_new_state(ton::BlockIdExt id, td::BufferSlice data, int authority,
|
||||
td::Promise<td::Unit> promise) {
|
||||
// TODO: add verification that data is a BoC with correct root hash, and that it is a Block corresponding to blk_id
|
||||
// ...
|
||||
// TODO: check whether we already have a block with blk_id
|
||||
// ...
|
||||
auto save_res = save_db_file(id.file_hash, data, FMode::chk_if_exists | FMode::overwrite | FMode::chk_file_hash);
|
||||
if (save_res.is_error()) {
|
||||
promise(std::move(save_res));
|
||||
}
|
||||
auto sz = data.size();
|
||||
auto lev = bb.alloc<log::NewState>(id.id, id.root_hash, id.file_hash, data.size(), authority & 0xff);
|
||||
if (sz <= 8) {
|
||||
std::memcpy(lev->last_bytes, data.data(), sz);
|
||||
} else {
|
||||
std::memcpy(lev->last_bytes, data.data() + sz - 8, 8);
|
||||
}
|
||||
lev.commit();
|
||||
bb.flush();
|
||||
promise(td::Unit{});
|
||||
}
|
||||
|
||||
td::Status BlockDbImpl::load_data(FileInfo& file_info, bool force) {
|
||||
if (!file_info.data.is_null() && !force) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (file_info.blk.file_hash.is_zero()) {
|
||||
return td::Status::Error("cannot load a block file without knowing its file hash");
|
||||
}
|
||||
auto it = file_cache.find(file_info.blk.file_hash);
|
||||
if (it != file_cache.end() && !force) {
|
||||
file_info.data = it->second.clone();
|
||||
return td::Status::OK();
|
||||
}
|
||||
std::string filename = compute_db_filename(file_info.blk.file_hash);
|
||||
auto res = load_binary_file(filename);
|
||||
if (res.is_error()) {
|
||||
return res.move_as_error();
|
||||
}
|
||||
file_info.data = res.move_as_ok();
|
||||
file_cache_insert(file_info.blk.file_hash, file_info.data);
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
FileInfo FileInfo::clone() const {
|
||||
return FileInfo{*this};
|
||||
}
|
||||
|
||||
FileInfo::FileInfo(const FileInfo& other)
|
||||
: td::CntObject()
|
||||
, blk(other.blk)
|
||||
, type(other.type)
|
||||
, status(other.status)
|
||||
, file_size(other.file_size)
|
||||
, data(other.data.clone()) {
|
||||
}
|
||||
|
||||
FileInfo* FileInfo::make_copy() const {
|
||||
return new FileInfo(*this);
|
||||
}
|
||||
|
||||
} // namespace block
|
164
crypto/block/block-db.h
Normal file
164
crypto/block/block-db.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "td/utils/int_types.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/actor/actor.h"
|
||||
#include "ton/ton-types.h"
|
||||
#include "crypto/common/refcnt.hpp"
|
||||
#include "crypto/vm/cells.h"
|
||||
|
||||
namespace block {
|
||||
|
||||
using FileHash = ton::Bits256;
|
||||
using RootHash = ton::Bits256;
|
||||
using td::Ref;
|
||||
|
||||
struct ZerostateInfo {
|
||||
td::BufferSlice data;
|
||||
std::string filename;
|
||||
FileHash file_hash;
|
||||
RootHash root_hash;
|
||||
ZerostateInfo() {
|
||||
file_hash.set_zero();
|
||||
root_hash.set_zero();
|
||||
}
|
||||
ZerostateInfo(RootHash hash, std::string _filename) : filename(_filename), root_hash(std::move(hash)) {
|
||||
file_hash.set_zero();
|
||||
}
|
||||
ZerostateInfo(std::string _filename) : filename(_filename) {
|
||||
file_hash.set_zero();
|
||||
root_hash.set_zero();
|
||||
}
|
||||
ZerostateInfo(RootHash hash, FileHash fhash) : file_hash(std::move(fhash)), root_hash(std::move(hash)) {
|
||||
}
|
||||
bool has_file_hash() const {
|
||||
return !file_hash.is_zero();
|
||||
}
|
||||
bool has_root_hash() const {
|
||||
return !root_hash.is_zero();
|
||||
}
|
||||
bool has_filename() const {
|
||||
return !filename.empty();
|
||||
}
|
||||
bool has_data() const {
|
||||
return !data.empty();
|
||||
}
|
||||
td::Status base_check();
|
||||
};
|
||||
|
||||
enum class FileType {
|
||||
unknown = 0,
|
||||
unknown_boc = 1,
|
||||
block = 2,
|
||||
block_candidate = 3,
|
||||
collated_data = 4,
|
||||
block_signatures = 5,
|
||||
state = 6,
|
||||
out_queue = 7
|
||||
};
|
||||
|
||||
struct FileInfo : public td::CntObject {
|
||||
ton::BlockIdExt blk;
|
||||
FileType type;
|
||||
int status;
|
||||
long long file_size;
|
||||
td::BufferSlice data;
|
||||
FileInfo() : type(FileType::unknown), status(0), file_size(-1) {
|
||||
blk.file_hash.set_zero();
|
||||
blk.root_hash.set_zero();
|
||||
}
|
||||
FileInfo(FileType _type, const ton::BlockId& _id, int _status, const FileHash& _fhash, long long _fsize = -1)
|
||||
: blk(_id, _fhash), type(_type), status(_status), file_size(_fsize) {
|
||||
blk.root_hash.set_zero();
|
||||
}
|
||||
FileInfo(FileType _type, const ton::BlockId& _id, int _status, const FileHash& _fhash, const RootHash& _rhash,
|
||||
long long _fsize = -1)
|
||||
: blk(_id, _rhash, _fhash), type(_type), status(_status), file_size(_fsize) {
|
||||
}
|
||||
FileInfo(FileType _type, const ton::BlockId& _id, int _status, const FileHash& _fhash, const RootHash& _rhash,
|
||||
td::BufferSlice _data)
|
||||
: blk(_id, _rhash, _fhash), type(_type), status(_status), file_size(_data.size()), data(std::move(_data)) {
|
||||
}
|
||||
FileInfo(FileInfo&& other) = default;
|
||||
FileInfo clone() const;
|
||||
FileInfo* make_copy() const override;
|
||||
|
||||
private:
|
||||
FileInfo(const FileInfo& other);
|
||||
};
|
||||
|
||||
struct OutputQueueInfoDescr : public td::CntObject {
|
||||
ton::BlockId id;
|
||||
RootHash block_hash;
|
||||
RootHash state_hash;
|
||||
RootHash output_queue_info_hash;
|
||||
td::Ref<vm::Cell> queue_info;
|
||||
OutputQueueInfoDescr(ton::BlockId _id, const RootHash& _bhash, const RootHash& _shash, Ref<vm::Cell> _qinfo)
|
||||
: id(_id)
|
||||
, block_hash(_bhash)
|
||||
, state_hash(_shash)
|
||||
, output_queue_info_hash(_qinfo->get_hash().bits())
|
||||
, queue_info(std::move(_qinfo)) {
|
||||
}
|
||||
OutputQueueInfoDescr(ton::BlockId _id, td::ConstBitPtr _bhash, td::ConstBitPtr _shash, Ref<vm::Cell> _qinfo)
|
||||
: id(_id)
|
||||
, block_hash(_bhash)
|
||||
, state_hash(_shash)
|
||||
, output_queue_info_hash(_qinfo->get_hash().bits())
|
||||
, queue_info(std::move(_qinfo)) {
|
||||
}
|
||||
};
|
||||
|
||||
class BlockDb : public td::actor::Actor {
|
||||
public:
|
||||
BlockDb() = default;
|
||||
virtual ~BlockDb() = default;
|
||||
static td::Result<td::actor::ActorOwn<BlockDb>> create_block_db(std::string _base_dir,
|
||||
std::unique_ptr<ZerostateInfo> _zstate = nullptr,
|
||||
bool _allow_uninit = false, int _depth = 4,
|
||||
std::string _binlog_name = "");
|
||||
// authority: 0 = standard (inclusion in mc block), 1 = validator (by 2/3 validator signatures)
|
||||
virtual void get_top_block_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) = 0;
|
||||
virtual void get_top_block_state_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) = 0;
|
||||
virtual void get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) = 0;
|
||||
virtual void get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) = 0;
|
||||
virtual void get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::Ref<OutputQueueInfoDescr>> promise) = 0;
|
||||
virtual void get_object_by_file_hash(FileHash file_hash, bool need_data, bool force_file_load,
|
||||
td::Promise<td::Ref<FileInfo>> promise) = 0;
|
||||
virtual void get_object_by_root_hash(RootHash root_hash, bool need_data, bool force_file_load,
|
||||
td::Promise<td::Ref<FileInfo>> promise) = 0;
|
||||
virtual void save_new_block(ton::BlockIdExt blk_id, td::BufferSlice data, int authority,
|
||||
td::Promise<td::Unit> promise) = 0;
|
||||
virtual void save_new_state(ton::BlockIdExt state_id, td::BufferSlice data, int authority,
|
||||
td::Promise<td::Unit> promise) = 0;
|
||||
};
|
||||
|
||||
bool parse_hash_string(std::string arg, RootHash& res);
|
||||
|
||||
FileHash compute_file_hash(const td::BufferSlice& data);
|
||||
FileHash compute_file_hash(td::Slice data);
|
||||
td::Result<td::BufferSlice> load_binary_file(std::string filename, td::int64 max_size = 0);
|
||||
td::Status save_binary_file(std::string filename, const td::BufferSlice& data, unsigned long long max_size = 0);
|
||||
|
||||
std::string compute_db_filename(std::string base_dir, const FileHash& file_hash, int depth = 4);
|
||||
std::string compute_db_tmp_filename(std::string base_dir, const FileHash& file_hash, int i = 0, bool makedirs = true,
|
||||
int depth = 4);
|
||||
|
||||
} // namespace block
|
2135
crypto/block/block-parse.cpp
Normal file
2135
crypto/block/block-parse.cpp
Normal file
File diff suppressed because it is too large
Load diff
1081
crypto/block/block-parse.h
Normal file
1081
crypto/block/block-parse.h
Normal file
File diff suppressed because it is too large
Load diff
1923
crypto/block/block.cpp
Normal file
1923
crypto/block/block.cpp
Normal file
File diff suppressed because it is too large
Load diff
569
crypto/block/block.h
Normal file
569
crypto/block/block.h
Normal file
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "common/refcnt.hpp"
|
||||
#include "vm/cells.h"
|
||||
#include "vm/cellslice.h"
|
||||
#include "vm/dict.h"
|
||||
#include "vm/boc.h"
|
||||
#include "vm/stack.hpp"
|
||||
#include <ostream>
|
||||
#include "tl/tlblib.hpp"
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "ton/ton-types.h"
|
||||
|
||||
namespace block {
|
||||
|
||||
using td::Ref;
|
||||
|
||||
struct StdAddress {
|
||||
ton::WorkchainId workchain{ton::workchainInvalid};
|
||||
bool bounceable{true}; // addresses must be bounceable by default
|
||||
bool testnet{false};
|
||||
ton::StdSmcAddress addr;
|
||||
StdAddress() = default;
|
||||
StdAddress(ton::WorkchainId _wc, const ton::StdSmcAddress& _addr, bool _bounce = true, bool _testnet = false)
|
||||
: workchain(_wc), bounceable(_bounce), testnet(_testnet), addr(_addr) {
|
||||
}
|
||||
StdAddress(ton::WorkchainId _wc, td::ConstBitPtr _addr, bool _bounce = true, bool _testnet = false)
|
||||
: workchain(_wc), bounceable(_bounce), testnet(_testnet), addr(_addr) {
|
||||
}
|
||||
explicit StdAddress(std::string serialized);
|
||||
explicit StdAddress(td::Slice from);
|
||||
bool is_valid() const {
|
||||
return workchain != ton::workchainInvalid;
|
||||
}
|
||||
bool invalidate() {
|
||||
workchain = ton::workchainInvalid;
|
||||
return false;
|
||||
}
|
||||
std::string rserialize(bool base64_url = false) const;
|
||||
bool rserialize_to(td::MutableSlice to, bool base64_url = false) const;
|
||||
bool rserialize_to(char to[48], bool base64_url = false) const;
|
||||
bool rdeserialize(td::Slice from);
|
||||
bool rdeserialize(std::string from);
|
||||
bool rdeserialize(const char from[48]);
|
||||
bool parse_addr(td::Slice acc_string);
|
||||
bool operator==(const StdAddress& other) const;
|
||||
|
||||
static td::Result<StdAddress> parse(td::Slice acc_string);
|
||||
};
|
||||
|
||||
inline td::StringBuilder& operator<<(td::StringBuilder& sb, const StdAddress& addr) {
|
||||
return sb << addr.workchain << " : " << addr.addr.to_hex();
|
||||
}
|
||||
|
||||
bool parse_std_account_addr(td::Slice acc_string, ton::WorkchainId& wc, ton::StdSmcAddress& addr,
|
||||
bool* bounceable = nullptr, bool* testnet_only = nullptr);
|
||||
|
||||
struct ShardId {
|
||||
ton::WorkchainId workchain_id;
|
||||
int shard_pfx_len;
|
||||
unsigned long long shard_pfx;
|
||||
ShardId(ton::WorkchainId wc_id = ton::workchainInvalid)
|
||||
: workchain_id(wc_id), shard_pfx_len(0), shard_pfx(1ULL << 63) {
|
||||
}
|
||||
ShardId(ton::WorkchainId wc_id, unsigned long long sh_pfx);
|
||||
ShardId(ton::ShardIdFull ton_shard);
|
||||
ShardId(ton::BlockId ton_block);
|
||||
ShardId(const ton::BlockIdExt& ton_block);
|
||||
ShardId(ton::WorkchainId wc_id, unsigned long long sh_pfx, int sh_pfx_len);
|
||||
ShardId(vm::CellSlice& cs) {
|
||||
deserialize(cs);
|
||||
}
|
||||
ShardId(Ref<vm::CellSlice> cs_ref) {
|
||||
vm::CellSlice cs{*cs_ref};
|
||||
deserialize(cs);
|
||||
}
|
||||
explicit operator ton::ShardIdFull() const {
|
||||
return ton::ShardIdFull{workchain_id, shard_pfx};
|
||||
}
|
||||
bool operator==(const ShardId& other) const {
|
||||
return workchain_id == other.workchain_id && shard_pfx == other.shard_pfx;
|
||||
}
|
||||
void invalidate() {
|
||||
workchain_id = ton::workchainInvalid;
|
||||
shard_pfx_len = 0;
|
||||
}
|
||||
bool is_valid() const {
|
||||
return workchain_id != ton::workchainInvalid;
|
||||
}
|
||||
void show(std::ostream& os) const;
|
||||
std::string to_str() const;
|
||||
bool serialize(vm::CellBuilder& cb) const;
|
||||
bool deserialize(vm::CellSlice& cs);
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
|
||||
struct EnqueuedMsgDescr {
|
||||
ton::AccountIdPrefixFull src_prefix_, cur_prefix_, next_prefix_, dest_prefix_;
|
||||
ton::LogicalTime lt_;
|
||||
ton::LogicalTime enqueued_lt_;
|
||||
ton::Bits256 hash_;
|
||||
Ref<vm::Cell> msg_;
|
||||
Ref<vm::Cell> msg_env_;
|
||||
EnqueuedMsgDescr() = default;
|
||||
EnqueuedMsgDescr(ton::AccountIdPrefixFull cur_pfx, ton::AccountIdPrefixFull next_pfx, ton::LogicalTime lt,
|
||||
ton::LogicalTime enqueued_lt, td::ConstBitPtr hash)
|
||||
: cur_prefix_(cur_pfx), next_prefix_(next_pfx), lt_(lt), enqueued_lt_(enqueued_lt), hash_(hash) {
|
||||
}
|
||||
bool is_valid() const {
|
||||
return next_prefix_.is_valid();
|
||||
}
|
||||
bool check_key(td::ConstBitPtr key) const;
|
||||
bool invalidate() {
|
||||
next_prefix_.workchain = cur_prefix_.workchain = ton::workchainInvalid;
|
||||
return false;
|
||||
}
|
||||
bool unpack(vm::CellSlice& cs);
|
||||
};
|
||||
|
||||
using compute_shard_end_lt_func_t = std::function<ton::LogicalTime(ton::AccountIdPrefixFull)>;
|
||||
|
||||
struct MsgProcessedUpto {
|
||||
ton::ShardId shard;
|
||||
ton::BlockSeqno mc_seqno;
|
||||
ton::LogicalTime last_inmsg_lt;
|
||||
ton::Bits256 last_inmsg_hash;
|
||||
compute_shard_end_lt_func_t compute_shard_end_lt;
|
||||
MsgProcessedUpto() = default;
|
||||
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
|
||||
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
|
||||
}
|
||||
bool operator<(const MsgProcessedUpto& other) const & {
|
||||
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
|
||||
}
|
||||
bool contains(const MsgProcessedUpto& other) const &;
|
||||
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const &;
|
||||
// NB: this is for checking whether we have already imported an internal message
|
||||
bool already_processed(const EnqueuedMsgDescr& msg) const;
|
||||
};
|
||||
|
||||
struct MsgProcessedUptoCollection {
|
||||
ton::ShardIdFull owner;
|
||||
bool valid{false};
|
||||
std::vector<MsgProcessedUpto> list;
|
||||
MsgProcessedUptoCollection(ton::ShardIdFull _owner) : owner(_owner) {
|
||||
}
|
||||
MsgProcessedUptoCollection(ton::ShardIdFull _owner, Ref<vm::CellSlice> cs_ref);
|
||||
static std::unique_ptr<MsgProcessedUptoCollection> unpack(ton::ShardIdFull _owner, Ref<vm::CellSlice> cs_ref);
|
||||
bool is_valid() const {
|
||||
return valid;
|
||||
}
|
||||
bool insert(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt, td::ConstBitPtr last_proc_hash);
|
||||
bool insert_infty(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt = ~0ULL);
|
||||
bool compactify();
|
||||
bool pack(vm::CellBuilder& cb);
|
||||
bool is_reduced() const;
|
||||
bool contains(const MsgProcessedUpto& other) const;
|
||||
bool contains(const MsgProcessedUptoCollection& other) const;
|
||||
const MsgProcessedUpto* is_simple_update_of(const MsgProcessedUptoCollection& other, bool& ok) const;
|
||||
ton::BlockSeqno min_mc_seqno() const;
|
||||
bool split(ton::ShardIdFull new_owner);
|
||||
bool combine_with(const MsgProcessedUptoCollection& other);
|
||||
// NB: this is for checking whether we have already imported an internal message
|
||||
bool already_processed(const EnqueuedMsgDescr& msg) const;
|
||||
bool for_each_mcseqno(std::function<bool(ton::BlockSeqno)>) const;
|
||||
};
|
||||
|
||||
struct ParamLimits {
|
||||
enum { limits_cnt = 4 };
|
||||
enum { cl_underload = 0, cl_normal = 1, cl_soft = 2, cl_medium = 3, cl_hard = 4 };
|
||||
ParamLimits() = default;
|
||||
ParamLimits(td::uint32 underload, td::uint32 soft_lim, td::uint32 hard_lim)
|
||||
: limits_{underload, soft_lim, (soft_lim + hard_lim) / 2, hard_lim} {
|
||||
}
|
||||
td::uint32 underload() const {
|
||||
return limits_[0];
|
||||
}
|
||||
td::uint32 soft() const {
|
||||
return limits_[1];
|
||||
}
|
||||
td::uint32 hard() const {
|
||||
return limits_[3];
|
||||
}
|
||||
bool compute_medium_limit() {
|
||||
limits_[2] = soft() + ((hard() - soft()) >> 1);
|
||||
return true;
|
||||
}
|
||||
bool deserialize(vm::CellSlice& cs);
|
||||
int classify(td::uint64 value) const;
|
||||
bool fits(unsigned cls, td::uint64 value) const;
|
||||
|
||||
private:
|
||||
std::array<td::uint32, limits_cnt> limits_;
|
||||
};
|
||||
|
||||
struct BlockLimits {
|
||||
ParamLimits bytes, gas, lt_delta;
|
||||
ton::LogicalTime start_lt{0};
|
||||
const vm::CellUsageTree* usage_tree{nullptr};
|
||||
bool deserialize(vm::CellSlice& cs);
|
||||
int classify_size(td::uint64 size) const;
|
||||
int classify_gas(td::uint64 gas) const;
|
||||
int classify_lt(ton::LogicalTime lt) const;
|
||||
int classify(td::uint64 size, td::uint64 gas, ton::LogicalTime lt) const;
|
||||
bool fits(unsigned cls, td::uint64 size, td::uint64 gas, ton::LogicalTime lt) const;
|
||||
};
|
||||
|
||||
struct BlockLimitStatus {
|
||||
const BlockLimits& limits;
|
||||
ton::LogicalTime cur_lt;
|
||||
td::uint64 gas_used{};
|
||||
vm::NewCellStorageStat st_stat;
|
||||
unsigned accounts{}, transactions{};
|
||||
BlockLimitStatus(const BlockLimits& limits_, ton::LogicalTime lt = 0)
|
||||
: limits(limits_), cur_lt(std::max(limits_.start_lt, lt)) {
|
||||
}
|
||||
void reset() {
|
||||
cur_lt = limits.start_lt;
|
||||
st_stat.set_zero();
|
||||
transactions = accounts = 0;
|
||||
gas_used = 0;
|
||||
}
|
||||
td::uint64 estimate_block_size(const vm::NewCellStorageStat::Stat* extra = nullptr) const;
|
||||
int classify() const;
|
||||
bool fits(unsigned cls) const;
|
||||
bool would_fit(unsigned cls, ton::LogicalTime end_lt, td::uint64 more_gas,
|
||||
const vm::NewCellStorageStat::Stat* extra = nullptr) const;
|
||||
bool add_cell(Ref<vm::Cell> cell) {
|
||||
st_stat.add_cell(std::move(cell));
|
||||
return true;
|
||||
}
|
||||
bool add_proof(Ref<vm::Cell> cell) {
|
||||
st_stat.add_proof(std::move(cell), limits.usage_tree);
|
||||
return true;
|
||||
}
|
||||
bool update_lt(ton::LogicalTime lt) {
|
||||
cur_lt = std::max(lt, cur_lt);
|
||||
return true;
|
||||
}
|
||||
bool update_gas(td::uint64 more_gas) {
|
||||
gas_used += more_gas;
|
||||
return true;
|
||||
}
|
||||
bool add_transaction(unsigned cnt = 1) {
|
||||
transactions += cnt;
|
||||
return true;
|
||||
}
|
||||
bool add_account(unsigned cnt = 1) {
|
||||
accounts += cnt;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
namespace tlb {
|
||||
struct CurrencyCollection;
|
||||
} // namespace tlb
|
||||
|
||||
struct CurrencyCollection {
|
||||
using type_class = block::tlb::CurrencyCollection;
|
||||
td::RefInt256 grams;
|
||||
Ref<vm::Cell> extra;
|
||||
CurrencyCollection() = default;
|
||||
explicit CurrencyCollection(td::RefInt256 _grams, Ref<vm::Cell> _extra = {})
|
||||
: grams(std::move(_grams)), extra(std::move(_extra)) {
|
||||
}
|
||||
explicit CurrencyCollection(long long _grams, Ref<vm::Cell> _extra = {})
|
||||
: grams(true, _grams), extra(std::move(_extra)) {
|
||||
}
|
||||
bool set_zero() {
|
||||
grams = td::RefInt256{true, 0};
|
||||
extra.clear();
|
||||
return true;
|
||||
}
|
||||
static CurrencyCollection zero() {
|
||||
return CurrencyCollection(td::RefInt256{true, 0});
|
||||
}
|
||||
bool is_valid() const {
|
||||
return grams.not_null();
|
||||
}
|
||||
bool is_zero() const {
|
||||
return is_valid() && extra.is_null() && !td::sgn(grams);
|
||||
}
|
||||
bool has_extra() const {
|
||||
return extra.not_null();
|
||||
}
|
||||
bool invalidate() {
|
||||
extra.clear();
|
||||
grams.clear();
|
||||
return false;
|
||||
}
|
||||
bool validate() const;
|
||||
bool validate_extra() const;
|
||||
bool operator==(const CurrencyCollection& other) const;
|
||||
bool operator!=(const CurrencyCollection& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
bool operator==(td::RefInt256 other_grams) const {
|
||||
return is_valid() && !has_extra() && !td::cmp(grams, other_grams);
|
||||
}
|
||||
bool operator!=(td::RefInt256 other_grams) const {
|
||||
return !operator==(std::move(other_grams));
|
||||
}
|
||||
bool operator>=(const CurrencyCollection& other) const;
|
||||
bool operator<=(const CurrencyCollection& other) const {
|
||||
return other >= *this;
|
||||
}
|
||||
static bool add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c);
|
||||
static bool add(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c);
|
||||
CurrencyCollection& operator+=(const CurrencyCollection& other);
|
||||
CurrencyCollection& operator+=(CurrencyCollection&& other);
|
||||
CurrencyCollection& operator+=(td::RefInt256 other_grams);
|
||||
CurrencyCollection operator+(const CurrencyCollection& other) const;
|
||||
CurrencyCollection operator+(CurrencyCollection&& other) const;
|
||||
CurrencyCollection operator+(td::RefInt256 other_grams);
|
||||
static bool sub(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c);
|
||||
static bool sub(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c);
|
||||
CurrencyCollection& operator-=(const CurrencyCollection& other);
|
||||
CurrencyCollection& operator-=(CurrencyCollection&& other);
|
||||
CurrencyCollection& operator-=(td::RefInt256 other_grams);
|
||||
CurrencyCollection operator-(const CurrencyCollection& other) const;
|
||||
CurrencyCollection operator-(CurrencyCollection&& other) const;
|
||||
CurrencyCollection operator-(td::RefInt256 other_grams) const;
|
||||
bool store(vm::CellBuilder& cb) const;
|
||||
bool store_or_zero(vm::CellBuilder& cb) const;
|
||||
bool fetch(vm::CellSlice& cs);
|
||||
bool fetch_exact(vm::CellSlice& cs);
|
||||
bool unpack(Ref<vm::CellSlice> csr);
|
||||
bool validate_unpack(Ref<vm::CellSlice> csr);
|
||||
Ref<vm::CellSlice> pack() const;
|
||||
bool pack_to(Ref<vm::CellSlice>& csr) const {
|
||||
return (csr = pack()).not_null();
|
||||
}
|
||||
Ref<vm::Tuple> as_vm_tuple() const {
|
||||
if (is_valid()) {
|
||||
return vm::make_tuple_ref(grams, vm::StackEntry::maybe(extra));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
bool show(std::ostream& os) const;
|
||||
std::string to_str() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const CurrencyCollection& cc);
|
||||
|
||||
struct ShardState {
|
||||
enum { verbosity = 0 };
|
||||
ton::BlockIdExt id_;
|
||||
Ref<vm::Cell> root_;
|
||||
int global_id_;
|
||||
ton::UnixTime utime_;
|
||||
ton::LogicalTime lt_;
|
||||
ton::BlockSeqno mc_blk_seqno_, min_ref_mc_seqno_;
|
||||
ton::BlockIdExt mc_blk_ref_;
|
||||
ton::LogicalTime mc_blk_lt_;
|
||||
bool before_split_{false};
|
||||
std::unique_ptr<vm::AugmentedDictionary> account_dict_;
|
||||
std::unique_ptr<vm::Dictionary> shard_libraries_;
|
||||
Ref<vm::Cell> mc_state_extra_;
|
||||
td::uint64 overload_history_{0}, underload_history_{0};
|
||||
CurrencyCollection total_balance_, total_validator_fees_, global_balance_;
|
||||
std::unique_ptr<vm::AugmentedDictionary> out_msg_queue_;
|
||||
std::unique_ptr<vm::Dictionary> ihr_pending_;
|
||||
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto_;
|
||||
|
||||
bool is_valid() const {
|
||||
return id_.is_valid();
|
||||
}
|
||||
bool is_masterchain() const {
|
||||
return id_.is_masterchain();
|
||||
}
|
||||
bool invalidate() {
|
||||
id_.invalidate();
|
||||
return false;
|
||||
}
|
||||
td::Status unpack_state(ton::BlockIdExt id, Ref<vm::Cell> state_root);
|
||||
td::Status unpack_state_ext(ton::BlockIdExt id, Ref<vm::Cell> state_root, int global_id,
|
||||
ton::BlockSeqno prev_mc_block_seqno, bool after_split, bool clear_history,
|
||||
std::function<bool(ton::BlockSeqno)> for_each_mcseqno);
|
||||
td::Status merge_with(ShardState& sib);
|
||||
td::Result<std::unique_ptr<vm::AugmentedDictionary>> compute_split_out_msg_queue(ton::ShardIdFull subshard);
|
||||
td::Result<std::shared_ptr<block::MsgProcessedUptoCollection>> compute_split_processed_upto(
|
||||
ton::ShardIdFull subshard);
|
||||
td::Status split(ton::ShardIdFull subshard);
|
||||
td::Status unpack_out_msg_queue_info(Ref<vm::Cell> out_msg_queue_info);
|
||||
bool clear_load_history() {
|
||||
overload_history_ = underload_history_ = 0;
|
||||
return true;
|
||||
}
|
||||
bool clear_load_history_if(bool cond) {
|
||||
return !cond || clear_load_history();
|
||||
}
|
||||
td::Status check_before_split(bool before_split) const;
|
||||
td::Status check_global_id(int req_global_id) const;
|
||||
td::Status check_mc_blk_seqno(ton::BlockSeqno last_mc_block_seqno) const;
|
||||
bool update_prev_utime_lt(ton::UnixTime& prev_utime, ton::LogicalTime& prev_lt) const;
|
||||
|
||||
bool for_each_mcseqno(std::function<bool(ton::BlockSeqno)> func) const {
|
||||
return processed_upto_ && processed_upto_->for_each_mcseqno(std::move(func));
|
||||
}
|
||||
};
|
||||
|
||||
struct ValueFlow {
|
||||
struct SetZero {};
|
||||
CurrencyCollection from_prev_blk, to_next_blk, imported, exported, fees_collected, fees_imported, recovered, created,
|
||||
minted;
|
||||
ValueFlow() = default;
|
||||
ValueFlow(SetZero)
|
||||
: from_prev_blk{0}
|
||||
, to_next_blk{0}
|
||||
, imported{0}
|
||||
, exported{0}
|
||||
, fees_collected{0}
|
||||
, fees_imported{0}
|
||||
, recovered{0}
|
||||
, created{0}
|
||||
, minted{0} {
|
||||
}
|
||||
bool is_valid() const {
|
||||
return from_prev_blk.is_valid() && minted.is_valid();
|
||||
}
|
||||
bool validate() const;
|
||||
bool invalidate() {
|
||||
return from_prev_blk.invalidate();
|
||||
}
|
||||
bool set_zero();
|
||||
bool store(vm::CellBuilder& cb) const;
|
||||
bool fetch(vm::CellSlice& cs);
|
||||
bool unpack(Ref<vm::CellSlice> csr);
|
||||
bool show(std::ostream& os) const;
|
||||
std::string to_str() const;
|
||||
|
||||
private:
|
||||
bool show_one(std::ostream& os, const char* str, const CurrencyCollection& cc) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow);
|
||||
|
||||
struct BlkProofLink {
|
||||
ton::BlockIdExt from, to;
|
||||
bool is_key{false}, is_fwd{false};
|
||||
Ref<vm::Cell> dest_proof, shard_proof, proof;
|
||||
ton::CatchainSeqno cc_seqno{0};
|
||||
td::uint32 validator_set_hash{0};
|
||||
std::vector<ton::BlockSignature> signatures;
|
||||
BlkProofLink(ton::BlockIdExt _from, ton::BlockIdExt _to, bool _iskey = false)
|
||||
: from(_from), to(_to), is_key(_iskey), is_fwd(to.seqno() > from.seqno()) {
|
||||
}
|
||||
};
|
||||
|
||||
struct BlkProofChain {
|
||||
ton::BlockIdExt from, to;
|
||||
int mode;
|
||||
std::vector<BlkProofLink> links;
|
||||
std::size_t link_count() const {
|
||||
return links.size();
|
||||
}
|
||||
BlkProofChain(ton::BlockIdExt _from, ton::BlockIdExt _to, int _mode) : from(_from), to(_to), mode(_mode) {
|
||||
}
|
||||
};
|
||||
|
||||
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const ShardId& shard_id);
|
||||
|
||||
bool pack_std_smc_addr_to(char result[48], bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr,
|
||||
bool bounceable, bool testnet);
|
||||
std::string pack_std_smc_addr(bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr, bool bounceable,
|
||||
bool testnet);
|
||||
bool unpack_std_smc_addr(const char packed[48], ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
|
||||
bool& testnet);
|
||||
bool unpack_std_smc_addr(td::Slice packed, ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
|
||||
bool& testnet);
|
||||
bool unpack_std_smc_addr(std::string packed, ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
|
||||
bool& testnet);
|
||||
|
||||
bool store_UInt7(vm::CellBuilder& cb, unsigned long long value);
|
||||
bool store_UInt7(vm::CellBuilder& cb, unsigned long long value1, unsigned long long value2);
|
||||
bool store_Maybe_Grams(vm::CellBuilder& cb, td::RefInt256 value);
|
||||
bool store_Maybe_Grams_nz(vm::CellBuilder& cb, td::RefInt256 value);
|
||||
bool store_CurrencyCollection(vm::CellBuilder& cb, td::RefInt256 value, Ref<vm::Cell> extra);
|
||||
bool fetch_CurrencyCollection(vm::CellSlice& cs, td::RefInt256& value, Ref<vm::Cell>& extra, bool inexact = false);
|
||||
bool unpack_CurrencyCollection(Ref<vm::CellSlice> csr, td::RefInt256& value, Ref<vm::Cell>& extra);
|
||||
|
||||
bool valid_library_collection(Ref<vm::Cell> cell, bool catch_errors = true);
|
||||
|
||||
bool valid_config_data(Ref<vm::Cell> cell, const td::BitArray<256>& addr, bool catch_errors = true,
|
||||
bool relax_par0 = false);
|
||||
|
||||
bool add_extra_currency(Ref<vm::Cell> extra1, Ref<vm::Cell> extra2, Ref<vm::Cell>& res);
|
||||
bool sub_extra_currency(Ref<vm::Cell> extra1, Ref<vm::Cell> extra2, Ref<vm::Cell>& res);
|
||||
|
||||
ton::AccountIdPrefixFull interpolate_addr(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest,
|
||||
int used_dest_bits);
|
||||
bool interpolate_addr_to(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest, int used_dest_bits,
|
||||
ton::AccountIdPrefixFull& res);
|
||||
// result: (transit_addr_dest_bits, nh_addr_dest_bits)
|
||||
std::pair<int, int> perform_hypercube_routing(ton::AccountIdPrefixFull src, ton::AccountIdPrefixFull dest,
|
||||
ton::ShardIdFull cur, int used_dest_bits = 0);
|
||||
|
||||
bool compute_out_msg_queue_key(Ref<vm::Cell> msg_env, td::BitArray<352>& key);
|
||||
|
||||
bool unpack_block_prev_blk(Ref<vm::Cell> block_root, const ton::BlockIdExt& id, std::vector<ton::BlockIdExt>& prev,
|
||||
ton::BlockIdExt& mc_blkid, bool& after_split, ton::BlockIdExt* fetch_blkid = nullptr);
|
||||
td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
|
||||
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
|
||||
ton::BlockIdExt* fetch_blkid = nullptr);
|
||||
td::Status unpack_block_prev_blk_try(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
|
||||
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
|
||||
ton::BlockIdExt* fetch_blkid = nullptr);
|
||||
|
||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root);
|
||||
bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
|
||||
ton::LogicalTime* end_lt = nullptr);
|
||||
bool get_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
|
||||
ton::LogicalTime* end_lt = nullptr);
|
||||
bool unpack_old_mc_block_id(Ref<vm::CellSlice> old_blk_info, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
|
||||
ton::LogicalTime* end_lt = nullptr);
|
||||
bool check_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, const ton::BlockIdExt& blkid);
|
||||
bool check_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, const ton::BlockIdExt& blkid);
|
||||
|
||||
td::Result<Ref<vm::Cell>> get_block_transaction(Ref<vm::Cell> block_root, ton::WorkchainId workchain,
|
||||
const ton::StdSmcAddress& addr, ton::LogicalTime lt);
|
||||
td::Result<Ref<vm::Cell>> get_block_transaction_try(Ref<vm::Cell> block_root, ton::WorkchainId workchain,
|
||||
const ton::StdSmcAddress& addr, ton::LogicalTime lt);
|
||||
|
||||
bool get_transaction_in_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell>& in_msg);
|
||||
bool is_transaction_in_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell> msg);
|
||||
bool is_transaction_out_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell> msg);
|
||||
bool get_transaction_id(Ref<vm::Cell> trans_ref, ton::StdSmcAddress& account_addr, ton::LogicalTime& lt);
|
||||
bool get_transaction_owner(Ref<vm::Cell> trans_ref, ton::StdSmcAddress& addr);
|
||||
|
||||
td::uint32 compute_validator_set_hash(ton::CatchainSeqno cc_seqno, ton::ShardIdFull from,
|
||||
const std::vector<ton::ValidatorDescr>& nodes);
|
||||
|
||||
td::Result<Ref<vm::Cell>> get_config_data_from_smc(Ref<vm::Cell> acc_root);
|
||||
td::Result<Ref<vm::Cell>> get_config_data_from_smc(Ref<vm::CellSlice> acc_csr);
|
||||
bool important_config_parameters_changed(Ref<vm::Cell> old_cfg_root, Ref<vm::Cell> new_cfg_root, bool coarse = false);
|
||||
|
||||
bool is_public_library(td::ConstBitPtr key, Ref<vm::CellSlice> val);
|
||||
|
||||
bool parse_hex_hash(const char* str, const char* end, td::Bits256& hash);
|
||||
bool parse_hex_hash(td::Slice str, td::Bits256& hash);
|
||||
|
||||
bool parse_block_id_ext(const char* str, const char* end, ton::BlockIdExt& blkid);
|
||||
bool parse_block_id_ext(td::Slice str, ton::BlockIdExt& blkid);
|
||||
|
||||
} // namespace block
|
662
crypto/block/block.tlb
Normal file
662
crypto/block/block.tlb
Normal file
|
@ -0,0 +1,662 @@
|
|||
unit$_ = Unit;
|
||||
true$_ = True;
|
||||
// EMPTY False;
|
||||
bool_false$0 = Bool;
|
||||
bool_true$1 = Bool;
|
||||
bool_false$0 = BoolFalse;
|
||||
bool_true$1 = BoolTrue;
|
||||
nothing$0 {X:Type} = Maybe X;
|
||||
just$1 {X:Type} value:X = Maybe X;
|
||||
left$0 {X:Type} {Y:Type} value:X = Either X Y;
|
||||
right$1 {X:Type} {Y:Type} value:Y = Either X Y;
|
||||
pair$_ {X:Type} {Y:Type} first:X second:Y = Both X Y;
|
||||
|
||||
bit$_ (## 1) = Bit;
|
||||
/*
|
||||
*
|
||||
* FROM hashmap.tlb
|
||||
*
|
||||
*/
|
||||
// ordinary Hashmap / HashmapE, with fixed length keys
|
||||
//
|
||||
hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
|
||||
{n = (~m) + l} node:(HashmapNode m X) = Hashmap n X;
|
||||
|
||||
hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X;
|
||||
hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X)
|
||||
right:^(Hashmap n X) = HashmapNode (n + 1) X;
|
||||
|
||||
hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m;
|
||||
hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m;
|
||||
hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m;
|
||||
|
||||
unary_zero$0 = Unary ~0;
|
||||
unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1);
|
||||
|
||||
hme_empty$0 {n:#} {X:Type} = HashmapE n X;
|
||||
hme_root$1 {n:#} {X:Type} root:^(Hashmap n X) = HashmapE n X;
|
||||
|
||||
// true#_ = True;
|
||||
_ {n:#} _:(Hashmap n True) = BitstringSet n;
|
||||
|
||||
// HashmapAug, hashmap with an extra value
|
||||
// (augmentation) of type Y at every node
|
||||
//
|
||||
ahm_edge#_ {n:#} {X:Type} {Y:Type} {l:#} {m:#}
|
||||
label:(HmLabel ~l n) {n = (~m) + l}
|
||||
node:(HashmapAugNode m X Y) = HashmapAug n X Y;
|
||||
ahmn_leaf#_ {X:Type} {Y:Type} extra:Y value:X = HashmapAugNode 0 X Y;
|
||||
ahmn_fork#_ {n:#} {X:Type} {Y:Type} left:^(HashmapAug n X Y)
|
||||
right:^(HashmapAug n X Y) extra:Y = HashmapAugNode (n + 1) X Y;
|
||||
|
||||
ahme_empty$0 {n:#} {X:Type} {Y:Type} extra:Y
|
||||
= HashmapAugE n X Y;
|
||||
ahme_root$1 {n:#} {X:Type} {Y:Type} root:^(HashmapAug n X Y)
|
||||
extra:Y = HashmapAugE n X Y;
|
||||
|
||||
// VarHashmap / VarHashmapE, with variable-length keys
|
||||
//
|
||||
vhm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
|
||||
{n = (~m) + l} node:(VarHashmapNode m X)
|
||||
= VarHashmap n X;
|
||||
vhmn_leaf$00 {n:#} {X:Type} value:X = VarHashmapNode n X;
|
||||
vhmn_fork$01 {n:#} {X:Type} left:^(VarHashmap n X)
|
||||
right:^(VarHashmap n X) value:(Maybe X)
|
||||
= VarHashmapNode (n + 1) X;
|
||||
vhmn_cont$1 {n:#} {X:Type} branch:Bit child:^(VarHashmap n X)
|
||||
value:X = VarHashmapNode (n + 1) X;
|
||||
|
||||
// nothing$0 {X:Type} = Maybe X;
|
||||
// just$1 {X:Type} value:X = Maybe X;
|
||||
|
||||
vhme_empty$0 {n:#} {X:Type} = VarHashmapE n X;
|
||||
vhme_root$1 {n:#} {X:Type} root:^(VarHashmap n X)
|
||||
= VarHashmapE n X;
|
||||
|
||||
//
|
||||
// PfxHashmap / PfxHashmapE, with variable-length keys
|
||||
// constituting a prefix code
|
||||
//
|
||||
|
||||
phm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
|
||||
{n = (~m) + l} node:(PfxHashmapNode m X)
|
||||
= PfxHashmap n X;
|
||||
|
||||
phmn_leaf$0 {n:#} {X:Type} value:X = PfxHashmapNode n X;
|
||||
phmn_fork$1 {n:#} {X:Type} left:^(PfxHashmap n X)
|
||||
right:^(PfxHashmap n X) = PfxHashmapNode (n + 1) X;
|
||||
|
||||
phme_empty$0 {n:#} {X:Type} = PfxHashmapE n X;
|
||||
phme_root$1 {n:#} {X:Type} root:^(PfxHashmap n X)
|
||||
= PfxHashmapE n X;
|
||||
/*
|
||||
*
|
||||
* END hashmap.tlb
|
||||
*
|
||||
*/
|
||||
//
|
||||
// TON BLOCK LAYOUT
|
||||
//
|
||||
addr_none$00 = MsgAddressExt;
|
||||
addr_extern$01 len:(## 9) external_address:(bits len)
|
||||
= MsgAddressExt;
|
||||
anycast_info$_ depth:(#<= 30) { depth >= 1 }
|
||||
rewrite_pfx:(bits depth) = Anycast;
|
||||
addr_std$10 anycast:(Maybe Anycast)
|
||||
workchain_id:int8 address:bits256 = MsgAddressInt;
|
||||
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
|
||||
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
|
||||
_ _:MsgAddressInt = MsgAddress;
|
||||
_ _:MsgAddressExt = MsgAddress;
|
||||
//
|
||||
var_uint$_ {n:#} len:(#< n) value:(uint (len * 8))
|
||||
= VarUInteger n;
|
||||
var_int$_ {n:#} len:(#< n) value:(int (len * 8))
|
||||
= VarInteger n;
|
||||
nanograms$_ amount:(VarUInteger 16) = Grams;
|
||||
//
|
||||
extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32))
|
||||
= ExtraCurrencyCollection;
|
||||
currencies$_ grams:Grams other:ExtraCurrencyCollection
|
||||
= CurrencyCollection;
|
||||
//
|
||||
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
src:MsgAddressInt dest:MsgAddressInt
|
||||
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
|
||||
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
|
||||
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt
|
||||
import_fee:Grams = CommonMsgInfo;
|
||||
ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt
|
||||
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
|
||||
|
||||
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
src:MsgAddress dest:MsgAddressInt
|
||||
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
|
||||
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
|
||||
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
|
||||
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
|
||||
|
||||
tick_tock$_ tick:Bool tock:Bool = TickTock;
|
||||
|
||||
_ split_depth:(Maybe (## 5)) special:(Maybe TickTock)
|
||||
code:(Maybe ^Cell) data:(Maybe ^Cell)
|
||||
library:(HashmapE 256 SimpleLib) = StateInit;
|
||||
|
||||
simple_lib$_ public:Bool root:^Cell = SimpleLib;
|
||||
|
||||
message$_ {X:Type} info:CommonMsgInfo
|
||||
init:(Maybe (Either StateInit ^StateInit))
|
||||
body:(Either X ^X) = Message X;
|
||||
|
||||
message$_ {X:Type} info:CommonMsgInfoRelaxed
|
||||
init:(Maybe (Either StateInit ^StateInit))
|
||||
body:(Either X ^X) = MessageRelaxed X;
|
||||
//
|
||||
interm_addr_regular$0 use_dest_bits:(#<= 96)
|
||||
= IntermediateAddress;
|
||||
interm_addr_simple$10 workchain_id:int8 addr_pfx:uint64
|
||||
= IntermediateAddress;
|
||||
interm_addr_ext$11 workchain_id:int32 addr_pfx:uint64
|
||||
= IntermediateAddress;
|
||||
msg_envelope#4 cur_addr:IntermediateAddress
|
||||
next_addr:IntermediateAddress fwd_fee_remaining:Grams
|
||||
msg:^(Message Any) = MsgEnvelope;
|
||||
//
|
||||
msg_import_ext$000 msg:^(Message Any) transaction:^Transaction
|
||||
= InMsg;
|
||||
msg_import_ihr$010 msg:^(Message Any) transaction:^Transaction
|
||||
ihr_fee:Grams proof_created:^Cell = InMsg;
|
||||
msg_import_imm$011 in_msg:^MsgEnvelope
|
||||
transaction:^Transaction fwd_fee:Grams = InMsg;
|
||||
msg_import_fin$100 in_msg:^MsgEnvelope
|
||||
transaction:^Transaction fwd_fee:Grams = InMsg;
|
||||
msg_import_tr$101 in_msg:^MsgEnvelope out_msg:^MsgEnvelope
|
||||
transit_fee:Grams = InMsg;
|
||||
msg_discard_fin$110 in_msg:^MsgEnvelope transaction_id:uint64
|
||||
fwd_fee:Grams = InMsg;
|
||||
msg_discard_tr$111 in_msg:^MsgEnvelope transaction_id:uint64
|
||||
fwd_fee:Grams proof_delivered:^Cell = InMsg;
|
||||
//
|
||||
import_fees$_ fees_collected:Grams
|
||||
value_imported:CurrencyCollection = ImportFees;
|
||||
|
||||
_ (HashmapAugE 256 InMsg ImportFees) = InMsgDescr;
|
||||
|
||||
msg_export_ext$000 msg:^(Message Any)
|
||||
transaction:^Transaction = OutMsg;
|
||||
msg_export_imm$010 out_msg:^MsgEnvelope
|
||||
transaction:^Transaction reimport:^InMsg = OutMsg;
|
||||
msg_export_new$001 out_msg:^MsgEnvelope
|
||||
transaction:^Transaction = OutMsg;
|
||||
msg_export_tr$011 out_msg:^MsgEnvelope
|
||||
imported:^InMsg = OutMsg;
|
||||
msg_export_deq$110 out_msg:^MsgEnvelope // out_msg_hash:bits256 ?
|
||||
import_block_lt:uint64 = OutMsg;
|
||||
msg_export_tr_req$111 out_msg:^MsgEnvelope
|
||||
imported:^InMsg = OutMsg;
|
||||
msg_export_deq_imm$100 out_msg:^MsgEnvelope
|
||||
reimport:^InMsg = OutMsg;
|
||||
|
||||
_ enqueued_lt:uint64 out_msg:^MsgEnvelope = EnqueuedMsg;
|
||||
|
||||
_ (HashmapAugE 256 OutMsg CurrencyCollection) = OutMsgDescr;
|
||||
|
||||
_ (HashmapAugE 352 EnqueuedMsg uint64) = OutMsgQueue;
|
||||
|
||||
processed_upto$_ last_msg_lt:uint64 last_msg_hash:bits256 = ProcessedUpto;
|
||||
// key is [ shard:uint64 mc_seqno:uint32 ]
|
||||
_ (HashmapE 96 ProcessedUpto) = ProcessedInfo;
|
||||
|
||||
ihr_pending$_ import_lt:uint64 = IhrPendingSince;
|
||||
_ (HashmapE 320 IhrPendingSince) = IhrPendingInfo;
|
||||
|
||||
_ out_queue:OutMsgQueue proc_info:ProcessedInfo
|
||||
ihr_pending:IhrPendingInfo = OutMsgQueueInfo;
|
||||
//
|
||||
storage_used$_ cells:(VarUInteger 7) bits:(VarUInteger 7)
|
||||
public_cells:(VarUInteger 7) = StorageUsed;
|
||||
|
||||
storage_used_short$_ cells:(VarUInteger 7)
|
||||
bits:(VarUInteger 7) = StorageUsedShort;
|
||||
|
||||
storage_info$_ used:StorageUsed last_paid:uint32
|
||||
due_payment:(Maybe Grams) = StorageInfo;
|
||||
|
||||
account_none$0 = Account;
|
||||
account$1 addr:MsgAddressInt storage_stat:StorageInfo
|
||||
storage:AccountStorage = Account;
|
||||
|
||||
account_storage$_ last_trans_lt:uint64
|
||||
balance:CurrencyCollection state:AccountState
|
||||
= AccountStorage;
|
||||
|
||||
account_uninit$00 = AccountState;
|
||||
account_active$1 _:StateInit = AccountState;
|
||||
account_frozen$01 state_hash:bits256 = AccountState;
|
||||
|
||||
acc_state_uninit$00 = AccountStatus;
|
||||
acc_state_frozen$01 = AccountStatus;
|
||||
acc_state_active$10 = AccountStatus;
|
||||
acc_state_nonexist$11 = AccountStatus;
|
||||
|
||||
/* duplicates
|
||||
tick_tock$_ tick:Bool tock:Bool = TickTock;
|
||||
|
||||
_ split_depth:(Maybe (## 5)) special:(Maybe TickTock)
|
||||
code:(Maybe ^Cell) data:(Maybe ^Cell)
|
||||
library:(Maybe ^Cell) = StateInit;
|
||||
*/
|
||||
|
||||
account_descr$_ account:^Account last_trans_hash:bits256
|
||||
last_trans_lt:uint64 = ShardAccount;
|
||||
|
||||
depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo;
|
||||
|
||||
_ (HashmapAugE 256 ShardAccount DepthBalanceInfo) = ShardAccounts;
|
||||
|
||||
transaction$0111 account_addr:bits256 lt:uint64
|
||||
prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32
|
||||
outmsg_cnt:uint15
|
||||
orig_status:AccountStatus end_status:AccountStatus
|
||||
^[ in_msg:(Maybe ^(Message Any)) out_msgs:(HashmapE 15 ^(Message Any)) ]
|
||||
total_fees:CurrencyCollection state_update:^(HASH_UPDATE Account)
|
||||
description:^TransactionDescr = Transaction;
|
||||
|
||||
!merkle_update#02 {X:Type} old_hash:bits256 new_hash:bits256
|
||||
old:^X new:^X = MERKLE_UPDATE X;
|
||||
update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256
|
||||
= HASH_UPDATE X;
|
||||
|
||||
acc_trans#5 account_addr:bits256
|
||||
transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
|
||||
state_update:^(HASH_UPDATE Account)
|
||||
= AccountBlock;
|
||||
|
||||
_ (HashmapAugE 256 AccountBlock CurrencyCollection) = ShardAccountBlocks;
|
||||
//
|
||||
tr_phase_storage$_ storage_fees_collected:Grams
|
||||
storage_fees_due:(Maybe Grams)
|
||||
status_change:AccStatusChange
|
||||
= TrStoragePhase;
|
||||
|
||||
acst_unchanged$0 = AccStatusChange; // x -> x
|
||||
acst_frozen$10 = AccStatusChange; // init -> frozen
|
||||
acst_deleted$11 = AccStatusChange; // frozen -> deleted
|
||||
|
||||
tr_phase_credit$_ due_fees_collected:(Maybe Grams)
|
||||
credit:CurrencyCollection = TrCreditPhase;
|
||||
|
||||
tr_phase_compute_skipped$0 reason:ComputeSkipReason
|
||||
= TrComputePhase;
|
||||
tr_phase_compute_vm$1 success:Bool msg_state_used:Bool
|
||||
account_activated:Bool gas_fees:Grams
|
||||
^[ gas_used:(VarUInteger 7)
|
||||
gas_limit:(VarUInteger 7) gas_credit:(Maybe (VarUInteger 3))
|
||||
mode:int8 exit_code:int32 exit_arg:(Maybe int32)
|
||||
vm_steps:uint32
|
||||
vm_init_state_hash:bits256 vm_final_state_hash:bits256 ]
|
||||
= TrComputePhase;
|
||||
cskip_no_state$00 = ComputeSkipReason;
|
||||
cskip_bad_state$01 = ComputeSkipReason;
|
||||
cskip_no_gas$10 = ComputeSkipReason;
|
||||
|
||||
tr_phase_action$_ success:Bool valid:Bool no_funds:Bool
|
||||
status_change:AccStatusChange
|
||||
total_fwd_fees:(Maybe Grams) total_action_fees:(Maybe Grams)
|
||||
result_code:int32 result_arg:(Maybe int32) tot_actions:uint16
|
||||
spec_actions:uint16 skipped_actions:uint16 msgs_created:uint16
|
||||
action_list_hash:bits256 tot_msg_size:StorageUsedShort
|
||||
= TrActionPhase;
|
||||
|
||||
tr_phase_bounce_negfunds$00 = TrBouncePhase;
|
||||
tr_phase_bounce_nofunds$01 msg_size:StorageUsedShort
|
||||
req_fwd_fees:Grams = TrBouncePhase;
|
||||
tr_phase_bounce_ok$1 msg_size:StorageUsedShort
|
||||
msg_fees:Grams fwd_fees:Grams = TrBouncePhase;
|
||||
//
|
||||
trans_ord$0000 credit_first:Bool
|
||||
storage_ph:(Maybe TrStoragePhase)
|
||||
credit_ph:(Maybe TrCreditPhase)
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool bounce:(Maybe TrBouncePhase)
|
||||
destroyed:Bool
|
||||
= TransactionDescr;
|
||||
|
||||
trans_storage$0001 storage_ph:TrStoragePhase
|
||||
= TransactionDescr;
|
||||
|
||||
trans_tick_tock$001 is_tock:Bool storage:TrStoragePhase
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool destroyed:Bool = TransactionDescr;
|
||||
//
|
||||
split_merge_info$_ cur_shard_pfx_len:(## 6)
|
||||
acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256
|
||||
= SplitMergeInfo;
|
||||
trans_split_prepare$0100 split_info:SplitMergeInfo
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool destroyed:Bool
|
||||
= TransactionDescr;
|
||||
trans_split_install$0101 split_info:SplitMergeInfo
|
||||
prepare_transaction:^Transaction
|
||||
installed:Bool = TransactionDescr;
|
||||
|
||||
trans_merge_prepare$0110 split_info:SplitMergeInfo
|
||||
storage_ph:TrStoragePhase aborted:Bool
|
||||
= TransactionDescr;
|
||||
trans_merge_install$0111 split_info:SplitMergeInfo
|
||||
prepare_transaction:^Transaction
|
||||
credit_ph:(Maybe TrCreditPhase)
|
||||
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
|
||||
aborted:Bool destroyed:Bool
|
||||
= TransactionDescr;
|
||||
|
||||
smc_info#076ef1ea actions:uint16 msgs_sent:uint16
|
||||
unixtime:uint32 block_lt:uint64 trans_lt:uint64
|
||||
rand_seed:bits256 balance_remaining:CurrencyCollection
|
||||
myself:MsgAddressInt = SmartContractInfo;
|
||||
//
|
||||
//
|
||||
out_list_empty$_ = OutList 0;
|
||||
out_list$_ {n:#} prev:^(OutList n) action:OutAction
|
||||
= OutList (n + 1);
|
||||
action_send_msg#0ec3c86d mode:(## 8)
|
||||
out_msg:^(MessageRelaxed Any) = OutAction;
|
||||
action_set_code#ad4de08e new_code:^Cell = OutAction;
|
||||
action_reserve_currency#36e6b809 mode:(## 8)
|
||||
currency:CurrencyCollection = OutAction;
|
||||
|
||||
out_list_node$_ prev:^Cell action:OutAction = OutListNode;
|
||||
//
|
||||
//
|
||||
shard_ident$00 shard_pfx_bits:(#<= 60)
|
||||
workchain_id:int32 shard_prefix:uint64 = ShardIdent;
|
||||
|
||||
ext_blk_ref$_ end_lt:uint64
|
||||
seq_no:uint32 root_hash:bits256 file_hash:bits256
|
||||
= ExtBlkRef;
|
||||
|
||||
block_id_ext$_ shard_id:ShardIdent seq_no:uint32
|
||||
root_hash:bits256 file_hash:bits256 = BlockIdExt;
|
||||
|
||||
master_info$_ master:ExtBlkRef = BlkMasterInfo;
|
||||
|
||||
shard_state#9023afe2 global_id:int32
|
||||
shard_id:ShardIdent
|
||||
seq_no:uint32 vert_seq_no:#
|
||||
gen_utime:uint32 gen_lt:uint64
|
||||
min_ref_mc_seqno:uint32
|
||||
out_msg_queue_info:^OutMsgQueueInfo
|
||||
before_split:(## 1)
|
||||
accounts:^ShardAccounts
|
||||
^[ overload_history:uint64 underload_history:uint64
|
||||
total_balance:CurrencyCollection
|
||||
total_validator_fees:CurrencyCollection
|
||||
libraries:(HashmapE 256 LibDescr)
|
||||
master_ref:(Maybe BlkMasterInfo) ]
|
||||
custom:(Maybe ^McStateExtra)
|
||||
= ShardStateUnsplit;
|
||||
|
||||
_ ShardStateUnsplit = ShardState;
|
||||
split_state#5f327da5 left:^ShardStateUnsplit right:^ShardStateUnsplit = ShardState;
|
||||
|
||||
shared_lib_descr$00 lib:^Cell publishers:(Hashmap 256 True)
|
||||
= LibDescr;
|
||||
|
||||
block_info#9bc7a987 version:uint32
|
||||
not_master:(## 1)
|
||||
after_merge:(## 1) before_split:(## 1)
|
||||
after_split:(## 1)
|
||||
want_split:Bool want_merge:Bool
|
||||
key_block:Bool vert_seqno_incr:(## 1)
|
||||
flags:(## 8)
|
||||
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
|
||||
start_lt:uint64 end_lt:uint64
|
||||
gen_validator_list_hash_short:uint32
|
||||
gen_catchain_seqno:uint32
|
||||
min_ref_mc_seqno:uint32
|
||||
prev_key_block_seqno:uint32
|
||||
master_ref:not_master?^BlkMasterInfo
|
||||
prev_ref:^(BlkPrevInfo after_merge)
|
||||
prev_vert_ref:vert_seq_no?^(BlkPrevInfo 0)
|
||||
= BlockInfo;
|
||||
|
||||
prev_blk_info$_ prev:ExtBlkRef = BlkPrevInfo 0;
|
||||
prev_blks_info$_ prev1:^ExtBlkRef prev2:^ExtBlkRef = BlkPrevInfo 1;
|
||||
|
||||
block#11ef55aa global_id:int32
|
||||
info:^BlockInfo value_flow:^ValueFlow
|
||||
state_update:^(MERKLE_UPDATE ShardState)
|
||||
extra:^BlockExtra = Block;
|
||||
|
||||
block_extra in_msg_descr:^InMsgDescr
|
||||
out_msg_descr:^OutMsgDescr
|
||||
account_blocks:^ShardAccountBlocks
|
||||
rand_seed:bits256
|
||||
created_by:bits256
|
||||
custom:(Maybe ^McBlockExtra) = BlockExtra;
|
||||
//
|
||||
value_flow ^[ from_prev_blk:CurrencyCollection
|
||||
to_next_blk:CurrencyCollection
|
||||
imported:CurrencyCollection
|
||||
exported:CurrencyCollection ]
|
||||
fees_collected:CurrencyCollection
|
||||
^[
|
||||
fees_imported:CurrencyCollection
|
||||
recovered:CurrencyCollection
|
||||
created:CurrencyCollection
|
||||
minted:CurrencyCollection
|
||||
] = ValueFlow;
|
||||
|
||||
//
|
||||
//
|
||||
bt_leaf$0 {X:Type} leaf:X = BinTree X;
|
||||
bt_fork$1 {X:Type} left:^(BinTree X) right:^(BinTree X)
|
||||
= BinTree X;
|
||||
|
||||
fsm_none$0 = FutureSplitMerge;
|
||||
fsm_split$10 split_utime:uint32 interval:uint32 = FutureSplitMerge;
|
||||
fsm_merge$11 merge_utime:uint32 interval:uint32 = FutureSplitMerge;
|
||||
|
||||
shard_descr#b seq_no:uint32 reg_mc_seqno:uint32
|
||||
start_lt:uint64 end_lt:uint64
|
||||
root_hash:bits256 file_hash:bits256
|
||||
before_split:Bool before_merge:Bool
|
||||
want_split:Bool want_merge:Bool
|
||||
nx_cc_updated:Bool flags:(## 3) { flags = 0 }
|
||||
next_catchain_seqno:uint32 next_validator_shard:uint64
|
||||
min_ref_mc_seqno:uint32 gen_utime:uint32
|
||||
split_merge_at:FutureSplitMerge
|
||||
fees_collected:CurrencyCollection
|
||||
funds_created:CurrencyCollection = ShardDescr;
|
||||
|
||||
_ (HashmapE 32 ^(BinTree ShardDescr)) = ShardHashes;
|
||||
|
||||
bta_leaf$0 {X:Type} {Y:Type} extra:Y leaf:X = BinTreeAug X Y;
|
||||
bta_fork$1 {X:Type} {Y:Type} left:^(BinTreeAug X Y)
|
||||
right:^(BinTreeAug X Y) extra:Y = BinTreeAug X Y;
|
||||
|
||||
_ fees:CurrencyCollection create:CurrencyCollection = ShardFeeCreated;
|
||||
_ (HashmapAugE 96 ShardFeeCreated ShardFeeCreated) = ShardFees;
|
||||
|
||||
_ config_addr:bits256 config:^(Hashmap 32 ^Cell)
|
||||
= ConfigParams;
|
||||
|
||||
validator_info$_
|
||||
validator_list_hash_short:uint32
|
||||
catchain_seqno:uint32
|
||||
nx_cc_updated:Bool
|
||||
= ValidatorInfo;
|
||||
|
||||
validator_base_info$_
|
||||
validator_list_hash_short:uint32
|
||||
catchain_seqno:uint32
|
||||
= ValidatorBaseInfo;
|
||||
|
||||
_ key:Bool max_end_lt:uint64 = KeyMaxLt;
|
||||
_ key:Bool blk_ref:ExtBlkRef = KeyExtBlkRef;
|
||||
|
||||
_ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo;
|
||||
|
||||
masterchain_state_extra#cc26
|
||||
shard_hashes:ShardHashes
|
||||
config:ConfigParams
|
||||
^[ flags:(## 16) { flags = 0 }
|
||||
validator_info:ValidatorInfo
|
||||
prev_blocks:OldMcBlocksInfo
|
||||
after_key_block:Bool
|
||||
last_key_block:(Maybe ExtBlkRef) ]
|
||||
global_balance:CurrencyCollection
|
||||
= McStateExtra;
|
||||
|
||||
ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; // 288 bits
|
||||
ed25519_signature#5 R:bits256 s:bits256 = CryptoSignatureSimple; // 516 bits
|
||||
_ CryptoSignatureSimple = CryptoSignature;
|
||||
sig_pair$_ node_id_short:bits256 sign:CryptoSignature = CryptoSignaturePair; // 256+x ~ 772 bits
|
||||
|
||||
certificate#4 temp_key:SigPubKey valid_since:uint32 valid_until:uint32 = Certificate; // 356 bits
|
||||
certificate_env#a419b7d certificate:Certificate = CertificateEnv; // 384 bits
|
||||
signed_certificate$_ certificate:Certificate certificate_signature:CryptoSignature
|
||||
= SignedCertificate; // 356+516 = 872 bits
|
||||
// certificate_signature is the signature of CertificateEnv (with embedded certificate) with persistent key
|
||||
chained_signature#f signed_cert:^SignedCertificate temp_key_signature:CryptoSignatureSimple
|
||||
= CryptoSignature; // 4+(356+516)+516 = 520 bits+ref (1392 bits total)
|
||||
// temp_key_signature is the signature of whatever was originally intended to be signed with temp_key from certificate
|
||||
|
||||
masterchain_block_extra#cca5
|
||||
key_block:(## 1)
|
||||
shard_hashes:ShardHashes
|
||||
shard_fees:ShardFees
|
||||
^[ prev_blk_signatures:(HashmapE 16 CryptoSignaturePair)
|
||||
recover_create_msg:(Maybe ^InMsg)
|
||||
mint_msg:(Maybe ^InMsg) ]
|
||||
config:key_block?ConfigParams
|
||||
= McBlockExtra;
|
||||
|
||||
//
|
||||
// CONFIGURATION PARAMETERS
|
||||
//
|
||||
|
||||
validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr;
|
||||
validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
|
||||
validators#11 utime_since:uint32 utime_until:uint32
|
||||
total:(## 16) main:(## 16) { main <= total } { main >= 1 }
|
||||
list:(Hashmap 16 ValidatorDescr) = ValidatorSet;
|
||||
|
||||
_ config_addr:bits256 = ConfigParam 0;
|
||||
_ elector_addr:bits256 = ConfigParam 1;
|
||||
_ minter_addr:bits256 = ConfigParam 2; // ConfigParam 0 is used if absent
|
||||
_ fee_collector_addr:bits256 = ConfigParam 3; // ConfigParam 1 is used if absent
|
||||
_ dns_root_addr:bits256 = ConfigParam 4; // root TON DNS resolver
|
||||
|
||||
_ mint_new_price:Grams mint_add_price:Grams = ConfigParam 6;
|
||||
_ to_mint:ExtraCurrencyCollection = ConfigParam 7;
|
||||
|
||||
capabilities#c4 version:uint32 capabilities:uint64 = GlobalVersion;
|
||||
_ GlobalVersion = ConfigParam 8; // all zero if absent
|
||||
_ mandatory_params:(Hashmap 32 True) = ConfigParam 9;
|
||||
|
||||
wfmt_basic#1 vm_version:int32 vm_mode:uint64 = WorkchainFormat 1;
|
||||
wfmt_ext#0 min_addr_len:(## 12) max_addr_len:(## 12) addr_len_step:(## 12)
|
||||
{ min_addr_len >= 64 } { min_addr_len <= max_addr_len }
|
||||
{ max_addr_len <= 1023 } { addr_len_step <= 1023 }
|
||||
workchain_type_id:(## 32) { workchain_type_id >= 1 }
|
||||
= WorkchainFormat 0;
|
||||
|
||||
workchain#a6 enabled_since:uint32 actual_min_split:(## 8)
|
||||
min_split:(## 8) max_split:(## 8) { actual_min_split <= min_split }
|
||||
//workchain#a5 enabled_since:uint32 min_split:(## 8) max_split:(## 8)
|
||||
// { min_split <= max_split } { max_split <= 60 }
|
||||
basic:(## 1) active:Bool accept_msgs:Bool flags:(## 13) { flags = 0 }
|
||||
zerostate_root_hash:bits256 zerostate_file_hash:bits256
|
||||
version:uint32 format:(WorkchainFormat basic)
|
||||
= WorkchainDescr;
|
||||
|
||||
_ workchains:(HashmapE 32 WorkchainDescr) = ConfigParam 12;
|
||||
|
||||
block_grams_created#6b masterchain_block_fee:Grams basechain_block_fee:Grams
|
||||
= BlockCreateFees;
|
||||
_ BlockCreateFees = ConfigParam 14;
|
||||
|
||||
_ validators_elected_for:uint32 elections_start_before:uint32
|
||||
elections_end_before:uint32 stake_held_for:uint32
|
||||
= ConfigParam 15;
|
||||
|
||||
_ max_validators:(## 16) max_main_validators:(## 16) min_validators:(## 16)
|
||||
{ max_validators >= max_main_validators }
|
||||
{ max_main_validators >= min_validators }
|
||||
{ min_validators >= 1 }
|
||||
= ConfigParam 16;
|
||||
|
||||
_ min_stake:Grams max_stake:Grams min_total_stake:Grams max_stake_factor:uint32 = ConfigParam 17;
|
||||
|
||||
_#cc utime_since:uint32 bit_price_ps:uint64 cell_price_ps:uint64
|
||||
mc_bit_price_ps:uint64 mc_cell_price_ps:uint64 = StoragePrices;
|
||||
_ (Hashmap 32 StoragePrices) = ConfigParam 18;
|
||||
|
||||
gas_prices#dd gas_price:uint64 gas_limit:uint64 gas_credit:uint64
|
||||
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
|
||||
= GasLimitsPrices;
|
||||
|
||||
config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20;
|
||||
config_gas_prices#_ GasLimitsPrices = ConfigParam 21;
|
||||
|
||||
param_limits#c3 underload:# soft_limit:# { underload <= soft_limit }
|
||||
hard_limit:# { soft_limit <= hard_limit } = ParamLimits;
|
||||
block_limits#5d bytes:ParamLimits gas:ParamLimits lt_delta:ParamLimits
|
||||
= BlockLimits;
|
||||
|
||||
config_mc_block_limits#_ BlockLimits = ConfigParam 22;
|
||||
config_block_limits#_ BlockLimits = ConfigParam 23;
|
||||
|
||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
||||
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
|
||||
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
|
||||
msg_forward_prices#ea lump_price:uint64 bit_price:uint64 cell_price:uint64
|
||||
ihr_price_factor:uint32 first_frac:uint16 next_frac:uint16 = MsgForwardPrices;
|
||||
|
||||
// used for messages to/from masterchain
|
||||
config_mc_fwd_prices#_ MsgForwardPrices = ConfigParam 24;
|
||||
// used for all other messages
|
||||
config_fwd_prices#_ MsgForwardPrices = ConfigParam 25;
|
||||
|
||||
catchain_config#c1 mc_catchain_lifetime:uint32 shard_catchain_lifetime:uint32
|
||||
shard_validators_lifetime:uint32 shard_validators_num:uint32 = CatchainConfig;
|
||||
consensus_config#d6 round_candidates:# { round_candidates >= 1 }
|
||||
next_candidate_delay_ms:uint32 consensus_timeout_ms:uint32
|
||||
fast_attempts:uint32 attempt_duration:uint32 catchain_max_deps:uint32
|
||||
max_block_bytes:uint32 max_collated_bytes:uint32 = ConsensusConfig;
|
||||
|
||||
_ CatchainConfig = ConfigParam 28;
|
||||
_ ConsensusConfig = ConfigParam 29;
|
||||
|
||||
_ fundamental_smc_addr:(HashmapE 256 True) = ConfigParam 31;
|
||||
_ prev_validators:ValidatorSet = ConfigParam 32;
|
||||
_ prev_temp_validators:ValidatorSet = ConfigParam 33;
|
||||
_ cur_validators:ValidatorSet = ConfigParam 34;
|
||||
_ cur_temp_validators:ValidatorSet = ConfigParam 35;
|
||||
_ next_validators:ValidatorSet = ConfigParam 36;
|
||||
_ next_temp_validators:ValidatorSet = ConfigParam 37;
|
||||
|
||||
validator_temp_key#3 adnl_addr:bits256 temp_public_key:SigPubKey seqno:# valid_until:uint32 = ValidatorTempKey;
|
||||
signed_temp_key#4 key:^ValidatorTempKey signature:CryptoSignature = ValidatorSignedTempKey;
|
||||
_ (HashmapE 256 ValidatorSignedTempKey) = ConfigParam 39;
|
||||
|
||||
//
|
||||
// PROOFS
|
||||
//
|
||||
block_signatures_pure#_ sig_count:uint32 sig_weight:uint64
|
||||
signatures:(HashmapE 16 CryptoSignaturePair) = BlockSignaturesPure;
|
||||
block_signatures#11 validator_info:ValidatorBaseInfo pure_signatures:BlockSignaturesPure = BlockSignatures;
|
||||
block_proof#c3 proof_for:BlockIdExt root:^Cell signatures:(Maybe ^BlockSignatures) = BlockProof;
|
||||
|
||||
chain_empty$_ = ProofChain 0;
|
||||
chain_link$_ {n:#} root:^Cell prev:n?^(ProofChain n) = ProofChain (n + 1);
|
||||
top_block_descr#d5 proof_for:BlockIdExt signatures:(Maybe ^BlockSignatures)
|
||||
len:(## 8) { len >= 1 } { len <= 8 } chain:(ProofChain len) = TopBlockDescr;
|
||||
|
||||
//
|
||||
// COLLATED DATA
|
||||
//
|
||||
top_block_descr_set#4ac789f3 collection:(HashmapE 96 ^TopBlockDescr) = TopBlockDescrSet;
|
294
crypto/block/check-proof.cpp
Normal file
294
crypto/block/check-proof.cpp
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "check-proof.h"
|
||||
#include "block/block.h"
|
||||
#include "block/block-parse.h"
|
||||
#include "block/block-auto.h"
|
||||
#include "block/mc-config.h"
|
||||
|
||||
#include "ton/ton-shard.h"
|
||||
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
|
||||
namespace block {
|
||||
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to,
|
||||
bool check_state_hash) {
|
||||
ton::RootHash vhash{root->get_hash().bits()};
|
||||
if (vhash != blkid.root_hash) {
|
||||
return td::Status::Error(PSTRING() << " block header for block " << blkid.to_str() << " has incorrect root hash "
|
||||
<< vhash.to_hex() << " instead of " << blkid.root_hash.to_hex());
|
||||
}
|
||||
std::vector<ton::BlockIdExt> prev;
|
||||
ton::BlockIdExt mc_blkid;
|
||||
bool after_split;
|
||||
TRY_STATUS(block::unpack_block_prev_blk_try(root, blkid, prev, mc_blkid, after_split));
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockInfo::Record info;
|
||||
if (!(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info))) {
|
||||
return td::Status::Error(std::string{"cannot unpack header for block "} + blkid.to_str());
|
||||
}
|
||||
if (store_shard_hash_to) {
|
||||
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
|
||||
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
|
||||
&& upd_cs.size_ext() == 0x20228)) {
|
||||
return td::Status::Error("invalid Merkle update in block header");
|
||||
}
|
||||
auto upd_hash = upd_cs.prefetch_ref(1)->get_hash(0);
|
||||
if (!check_state_hash) {
|
||||
*store_shard_hash_to = upd_hash.bits();
|
||||
} else if (store_shard_hash_to->compare(upd_hash.bits())) {
|
||||
return td::Status::Error(PSTRING() << "state hash mismatch in block header of " << blkid.to_str()
|
||||
<< " : header declares " << upd_hash.bits().to_hex(256) << " expected "
|
||||
<< store_shard_hash_to->to_hex());
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Result<td::Bits256> check_state_proof(ton::BlockIdExt blkid, td::Slice proof) {
|
||||
TRY_RESULT(proof_root, vm::std_boc_deserialize(proof));
|
||||
auto virt_root = vm::MerkleProof::virtualize(std::move(proof_root), 1);
|
||||
if (virt_root.is_null()) {
|
||||
return td::Status::Error("account state proof is invalid");
|
||||
}
|
||||
td::Bits256 state_hash;
|
||||
TRY_STATUS(check_block_header_proof(std::move(virt_root), blkid, &state_hash));
|
||||
return state_hash;
|
||||
}
|
||||
|
||||
td::Result<Ref<vm::Cell>> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data) {
|
||||
try {
|
||||
TRY_RESULT(state_hash, check_state_proof(blkid, proof));
|
||||
TRY_RESULT(state_root, vm::std_boc_deserialize(data));
|
||||
auto state_virt_root = vm::MerkleProof::virtualize(std::move(state_root), 1);
|
||||
if (state_virt_root.is_null()) {
|
||||
return td::Status::Error("account state proof is invalid");
|
||||
}
|
||||
if (state_hash != state_virt_root->get_hash().bits()) {
|
||||
return td::Status::Error("root hash mismatch in the shardchain state proof");
|
||||
}
|
||||
return std::move(state_virt_root);
|
||||
} catch (vm::VmError& err) {
|
||||
return td::Status::Error(PSLICE() << "error scanning shard state proof: " << err.get_msg());
|
||||
} catch (vm::VmVirtError& err) {
|
||||
return td::Status::Error(PSLICE() << "virtualization error scanning shard state proof: " << err.get_msg());
|
||||
}
|
||||
}
|
||||
|
||||
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof) {
|
||||
if (blk == shard_blk) {
|
||||
if (!shard_proof.empty()) {
|
||||
LOG(WARNING) << "Unexpected non-empty shard proof";
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (!blk.is_masterchain() || !blk.is_valid_full()) {
|
||||
return td::Status::Error(PSLICE() << "reference block " << blk.to_str()
|
||||
<< " for a getAccountState query must belong to the masterchain");
|
||||
}
|
||||
TRY_RESULT_PREFIX(P_roots, vm::std_boc_deserialize_multi(std::move(shard_proof)),
|
||||
"cannot deserialize shard configuration proof");
|
||||
if (P_roots.size() != 2) {
|
||||
return td::Status::Error("shard configuration proof must have exactly two roots");
|
||||
}
|
||||
try {
|
||||
auto mc_state_root = vm::MerkleProof::virtualize(std::move(P_roots[1]), 1);
|
||||
if (mc_state_root.is_null()) {
|
||||
return td::Status::Error("shard configuration proof is invalid");
|
||||
}
|
||||
ton::Bits256 mc_state_hash = mc_state_root->get_hash().bits();
|
||||
TRY_STATUS_PREFIX(
|
||||
check_block_header_proof(vm::MerkleProof::virtualize(std::move(P_roots[0]), 1), blk, &mc_state_hash, true),
|
||||
"error in shard configuration block header proof :");
|
||||
block::gen::ShardStateUnsplit::Record sstate;
|
||||
if (!(tlb::unpack_cell(mc_state_root, sstate))) {
|
||||
return td::Status::Error("cannot unpack masterchain state header");
|
||||
}
|
||||
auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(std::move(mc_state_root));
|
||||
if (!shards_dict) {
|
||||
return td::Status::Error("cannot extract shard configuration dictionary from proof");
|
||||
}
|
||||
vm::CellSlice cs;
|
||||
ton::ShardIdFull true_shard;
|
||||
if (!block::ShardConfig::get_shard_hash_raw_from(*shards_dict, cs, shard_blk.shard_full(), true_shard)) {
|
||||
return td::Status::Error(PSLICE() << "masterchain state contains no information for shard "
|
||||
<< shard_blk.shard_full().to_str());
|
||||
}
|
||||
auto shard_info = block::McShardHash::unpack(cs, true_shard);
|
||||
if (shard_info.is_null()) {
|
||||
return td::Status::Error(PSLICE() << "cannot unpack information for shard " << shard_blk.shard_full().to_str()
|
||||
<< " from masterchain state");
|
||||
}
|
||||
if (shard_info->top_block_id() != shard_blk) {
|
||||
return td::Status::Error(PSLICE() << "shard configuration mismatch: expected to find block " << shard_blk.to_str()
|
||||
<< " , found " << shard_info->top_block_id().to_str());
|
||||
}
|
||||
} catch (vm::VmError err) {
|
||||
return td::Status::Error(PSLICE() << "error while traversing shard configuration proof : " << err.get_msg());
|
||||
} catch (vm::VmVirtError err) {
|
||||
return td::Status::Error(PSLICE() << "virtualization error while traversing shard configuration proof : "
|
||||
<< err.get_msg());
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,
|
||||
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash) {
|
||||
TRY_RESULT_PREFIX(Q_roots, vm::std_boc_deserialize_multi(std::move(proof)), "cannot deserialize account proof");
|
||||
if (Q_roots.size() != 2) {
|
||||
return td::Status::Error(PSLICE() << "account state proof must have exactly two roots");
|
||||
}
|
||||
|
||||
if (last_trans_lt) {
|
||||
last_trans_hash->set_zero();
|
||||
}
|
||||
|
||||
try {
|
||||
auto state_root = vm::MerkleProof::virtualize(std::move(Q_roots[1]), 1);
|
||||
if (state_root.is_null()) {
|
||||
return td::Status::Error("account state proof is invalid");
|
||||
}
|
||||
ton::Bits256 state_hash = state_root->get_hash().bits();
|
||||
TRY_STATUS_PREFIX(
|
||||
check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, &state_hash, true),
|
||||
"error in account shard block header proof : ");
|
||||
block::gen::ShardStateUnsplit::Record sstate;
|
||||
if (!(tlb::unpack_cell(std::move(state_root), sstate))) {
|
||||
return td::Status::Error("cannot unpack state header");
|
||||
}
|
||||
vm::AugmentedDictionary accounts_dict{vm::load_cell_slice(sstate.accounts).prefetch_ref(), 256,
|
||||
block::tlb::aug_ShardAccounts};
|
||||
auto acc_csr = accounts_dict.lookup(addr.addr);
|
||||
if (acc_csr.not_null()) {
|
||||
if (root.is_null()) {
|
||||
return td::Status::Error(PSLICE() << "account state proof shows that account state for " << addr
|
||||
<< " must be non-empty, but it actually is empty");
|
||||
}
|
||||
block::gen::ShardAccount::Record acc_info;
|
||||
if (!tlb::csr_unpack(std::move(acc_csr), acc_info)) {
|
||||
return td::Status::Error("cannot unpack ShardAccount from proof");
|
||||
}
|
||||
if (acc_info.account->get_hash().bits().compare(root->get_hash().bits(), 256)) {
|
||||
return td::Status::Error(PSLICE() << "account state hash mismatch: Merkle proof expects "
|
||||
<< acc_info.account->get_hash().bits().to_hex(256)
|
||||
<< " but received data has " << root->get_hash().bits().to_hex(256));
|
||||
}
|
||||
if (last_trans_hash) {
|
||||
*last_trans_hash = acc_info.last_trans_hash;
|
||||
}
|
||||
if (last_trans_lt) {
|
||||
*last_trans_lt = acc_info.last_trans_lt;
|
||||
}
|
||||
} else if (root.not_null()) {
|
||||
return td::Status::Error(PSLICE() << "account state proof shows that account state for " << addr
|
||||
<< " must be empty, but it is not");
|
||||
}
|
||||
} catch (vm::VmError err) {
|
||||
return td::Status::Error(PSLICE() << "error while traversing account proof : " << err.get_msg());
|
||||
} catch (vm::VmVirtError err) {
|
||||
return td::Status::Error(PSLICE() << "virtualization error while traversing account proof : " << err.get_msg());
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Result<AccountState::Info> AccountState::validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const {
|
||||
TRY_RESULT_PREFIX(root, vm::std_boc_deserialize(state.as_slice(), true), "cannot deserialize account state");
|
||||
|
||||
LOG(INFO) << "got account state for " << addr << " with respect to blocks " << blk.to_str()
|
||||
<< (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str());
|
||||
if (blk != ref_blk && ref_blk.id.seqno != ~0U) {
|
||||
return td::Status::Error(PSLICE() << "obtained getAccountState() for a different reference block " << blk.to_str()
|
||||
<< " instead of requested " << ref_blk.to_str());
|
||||
}
|
||||
|
||||
if (!shard_blk.is_valid_full()) {
|
||||
return td::Status::Error(PSLICE() << "shard block id " << shard_blk.to_str() << " in answer is invalid");
|
||||
}
|
||||
|
||||
if (!ton::shard_contains(shard_blk.shard_full(), ton::extract_addr_prefix(addr.workchain, addr.addr))) {
|
||||
return td::Status::Error(PSLICE() << "received data from shard block " << shard_blk.to_str()
|
||||
<< " that cannot contain requested account");
|
||||
}
|
||||
|
||||
TRY_STATUS(block::check_shard_proof(blk, shard_blk, shard_proof.as_slice()));
|
||||
|
||||
Info res;
|
||||
TRY_STATUS(
|
||||
block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, &res.last_trans_hash));
|
||||
res.root = std::move(root);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
td::Result<Transaction::Info> Transaction::validate() {
|
||||
if (root.is_null()) {
|
||||
return td::Status::Error("transactions are expected to be non-empty");
|
||||
}
|
||||
if (hash != root->get_hash().bits()) {
|
||||
return td::Status::Error(PSLICE() << "transaction hash mismatch: expected " << hash.to_hex() << ", found "
|
||||
<< root->get_hash().bits().to_hex(256));
|
||||
}
|
||||
block::gen::Transaction::Record trans;
|
||||
if (!tlb::unpack_cell(root, trans)) {
|
||||
return td::Status::Error("cannot unpack transaction #");
|
||||
}
|
||||
|
||||
if (trans.lt != lt) {
|
||||
return td::Status::Error(PSLICE() << "transaction lt mismatch: expected " << lt << ", found " << trans.lt);
|
||||
}
|
||||
Info res;
|
||||
res.blkid = blkid;
|
||||
res.prev_trans_lt = trans.prev_trans_lt;
|
||||
res.prev_trans_hash = trans.prev_trans_hash;
|
||||
res.transaction = root;
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Result<TransactionList::Info> TransactionList::validate() const {
|
||||
if (blkids.empty()) {
|
||||
return td::Status::Error("transaction list must be non-empty");
|
||||
}
|
||||
auto R = vm::std_boc_deserialize_multi(std::move(transactions_boc));
|
||||
if (R.is_error()) {
|
||||
return td::Status::Error("cannot deserialize transactions BoC");
|
||||
}
|
||||
auto list = R.move_as_ok();
|
||||
if (list.size() != blkids.size()) {
|
||||
return td::Status::Error(PSLICE() << "transaction list size " << list.size()
|
||||
<< " must be equal to the size of block id list " << blkids.size());
|
||||
}
|
||||
size_t c = 0;
|
||||
Info res;
|
||||
auto current_lt = lt;
|
||||
auto current_hash = hash;
|
||||
for (auto& root : list) {
|
||||
const auto& blkid = blkids[c++];
|
||||
Transaction transaction;
|
||||
transaction.blkid = std::move(blkid);
|
||||
transaction.lt = current_lt;
|
||||
transaction.hash = current_hash;
|
||||
transaction.root = root;
|
||||
TRY_RESULT(info, transaction.validate());
|
||||
current_lt = info.prev_trans_lt;
|
||||
current_hash = info.prev_trans_hash;
|
||||
res.transactions.push_back(std::move(info));
|
||||
}
|
||||
return std::move(res);
|
||||
}
|
||||
} // namespace block
|
80
crypto/block/check-proof.h
Normal file
80
crypto/block/check-proof.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "vm/cells.h"
|
||||
#include "block/block.h"
|
||||
|
||||
namespace block {
|
||||
using td::Ref;
|
||||
|
||||
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid,
|
||||
ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false);
|
||||
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof);
|
||||
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,
|
||||
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt = nullptr,
|
||||
ton::Bits256* last_trans_hash = nullptr);
|
||||
td::Result<td::Bits256> check_state_proof(ton::BlockIdExt blkid, td::Slice proof);
|
||||
td::Result<Ref<vm::Cell>> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data);
|
||||
|
||||
struct AccountState {
|
||||
ton::BlockIdExt blk;
|
||||
ton::BlockIdExt shard_blk;
|
||||
td::BufferSlice shard_proof;
|
||||
td::BufferSlice proof;
|
||||
td::BufferSlice state;
|
||||
|
||||
struct Info {
|
||||
td::Ref<vm::Cell> root;
|
||||
ton::LogicalTime last_trans_lt = 0;
|
||||
ton::Bits256 last_trans_hash;
|
||||
};
|
||||
|
||||
td::Result<Info> validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const;
|
||||
};
|
||||
|
||||
struct Transaction {
|
||||
ton::BlockIdExt blkid;
|
||||
ton::LogicalTime lt;
|
||||
ton::Bits256 hash;
|
||||
td::Ref<vm::Cell> root;
|
||||
|
||||
struct Info {
|
||||
ton::BlockIdExt blkid;
|
||||
ton::LogicalTime prev_trans_lt;
|
||||
ton::Bits256 prev_trans_hash;
|
||||
td::Ref<vm::Cell> transaction;
|
||||
};
|
||||
td::Result<Info> validate();
|
||||
};
|
||||
|
||||
struct TransactionList {
|
||||
ton::LogicalTime lt;
|
||||
ton::Bits256 hash;
|
||||
std::vector<ton::BlockIdExt> blkids;
|
||||
td::BufferSlice transactions_boc;
|
||||
|
||||
struct Info {
|
||||
std::vector<Transaction::Info> transactions;
|
||||
};
|
||||
|
||||
td::Result<Info> validate() const;
|
||||
};
|
||||
|
||||
} // namespace block
|
780
crypto/block/create-state.cpp
Normal file
780
crypto/block/create-state.cpp
Normal file
|
@ -0,0 +1,780 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
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
|
||||
*/
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "vm/stack.hpp"
|
||||
#include "vm/boc.h"
|
||||
|
||||
#include "fift/Fift.h"
|
||||
#include "fift/Dictionary.h"
|
||||
#include "fift/SourceLookup.h"
|
||||
#include "fift/words.h"
|
||||
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/Parser.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/port/signals.h"
|
||||
|
||||
#include "block.h"
|
||||
#include "block-parse.h"
|
||||
#include "block-auto.h"
|
||||
#include "mc-config.h"
|
||||
|
||||
#define PDO(__op) \
|
||||
if (!(__op)) { \
|
||||
ok = false; \
|
||||
}
|
||||
#define THRERR(__msg) \
|
||||
if (!ok) { \
|
||||
throw fift::IntError{__msg}; \
|
||||
}
|
||||
#define RETERR \
|
||||
if (!ok) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
using td::Ref;
|
||||
|
||||
int verbosity;
|
||||
|
||||
enum { wc_master = -1, wc_base = 0 };
|
||||
constexpr int wc_undef = std::numeric_limits<int>::min();
|
||||
|
||||
int workchain_id = wc_undef;
|
||||
int global_id = 0;
|
||||
|
||||
typedef td::BitArray<256> hash_t;
|
||||
|
||||
struct SmcDescr {
|
||||
hash_t addr;
|
||||
int split_depth;
|
||||
bool preinit_only;
|
||||
td::RefInt256 gram_balance;
|
||||
Ref<vm::DataCell> state_init; // StateInit
|
||||
Ref<vm::DataCell> account; // Account
|
||||
SmcDescr(const hash_t& _addr) : addr(_addr), split_depth(0), preinit_only(false) {
|
||||
}
|
||||
};
|
||||
|
||||
std::map<hash_t, SmcDescr> smart_contracts;
|
||||
td::RefInt256 total_smc_balance{true, 0}, max_total_smc_balance;
|
||||
|
||||
struct PublicLibDescr {
|
||||
Ref<vm::Cell> root;
|
||||
std::set<hash_t> publishers;
|
||||
PublicLibDescr(Ref<vm::Cell> _root) : root(std::move(_root)) {
|
||||
}
|
||||
};
|
||||
|
||||
std::map<hash_t, PublicLibDescr> public_libraries;
|
||||
|
||||
hash_t config_addr;
|
||||
Ref<vm::Cell> config_param_root;
|
||||
bool config_addr_set;
|
||||
vm::Dictionary config_dict{32};
|
||||
|
||||
ton::UnixTime now;
|
||||
|
||||
bool set_config_smc(const SmcDescr& smc) {
|
||||
if (config_addr_set || smc.preinit_only || workchain_id != wc_master || smc.split_depth) {
|
||||
return false;
|
||||
}
|
||||
vm::CellSlice cs = load_cell_slice(smc.state_init);
|
||||
bool ok = true;
|
||||
PDO(block::gen::t_Maybe_natwidth_5.skip(cs) && block::gen::t_Maybe_TickTock.skip(cs) &&
|
||||
block::gen::t_Maybe_Ref_Cell.skip(cs));
|
||||
RETERR;
|
||||
Ref<vm::Cell> data;
|
||||
PDO(cs.fetch_ulong(1) == 1 && cs.fetch_ref_to(data));
|
||||
THRERR("config smart contract must have non-empty data");
|
||||
vm::CellSlice cs2 = load_cell_slice(data);
|
||||
PDO(cs2.fetch_ref_to(data));
|
||||
THRERR("first reference in config smart contract data must point to initial configuration");
|
||||
PDO(block::valid_config_data(data, smc.addr));
|
||||
THRERR("invalid smart contract configuration data");
|
||||
config_addr = smc.addr;
|
||||
config_param_root = std::move(data);
|
||||
config_addr_set = true;
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "set smart contract " << config_addr << " as the configuration smart contract with configuration:\n";
|
||||
load_cell_slice(config_param_root).print_rec(std::cerr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void interpret_set_workchain(vm::Stack& stack) {
|
||||
workchain_id = stack.pop_smallint_range(0x7fffffff, -0x7fffffff);
|
||||
}
|
||||
|
||||
void interpret_get_workchain(vm::Stack& stack) {
|
||||
stack.push_smallint(workchain_id);
|
||||
}
|
||||
|
||||
void interpret_set_global_id(vm::Stack& stack) {
|
||||
global_id = stack.pop_smallint_range(0x7fffffff, -0x7fffffff);
|
||||
}
|
||||
|
||||
void interpret_get_global_id(vm::Stack& stack) {
|
||||
stack.push_smallint(global_id);
|
||||
}
|
||||
|
||||
void interpret_get_verbosity(vm::Stack& stack) {
|
||||
stack.push_smallint(GET_VERBOSITY_LEVEL());
|
||||
}
|
||||
|
||||
void interpret_set_verbosity(vm::Stack& stack) {
|
||||
int x = stack.pop_smallint_range(15);
|
||||
SET_VERBOSITY_LEVEL(x);
|
||||
}
|
||||
|
||||
void interpret_set_config_smartcontract(vm::Stack& stack) {
|
||||
if (workchain_id != wc_master) {
|
||||
throw fift::IntError{"configuration smart contract may be selected in masterchain only"};
|
||||
}
|
||||
if (config_addr_set) {
|
||||
throw fift::IntError{"configuration smart contract already selected"};
|
||||
}
|
||||
td::RefInt256 int_addr = stack.pop_int_finite();
|
||||
hash_t addr;
|
||||
if (!int_addr->export_bits(addr.bits(), 256, false)) {
|
||||
throw fift::IntError{"not a valid smart-contract address"};
|
||||
}
|
||||
auto it = smart_contracts.find(addr);
|
||||
if (it == smart_contracts.end()) {
|
||||
throw fift::IntError{"unknown smart contract"};
|
||||
}
|
||||
const SmcDescr& smc = it->second;
|
||||
assert(smc.addr == addr);
|
||||
if (smc.preinit_only) {
|
||||
throw fift::IntError{"configuration smart contract must be completely initialized"};
|
||||
}
|
||||
if (!set_config_smc(smc)) {
|
||||
throw fift::IntError{"invalid configuration smart contract"};
|
||||
}
|
||||
}
|
||||
|
||||
bool is_empty_cell(Ref<vm::Cell> cell) {
|
||||
bool is_special;
|
||||
auto cs = load_cell_slice_special(std::move(cell), is_special);
|
||||
return !is_special && cs.empty_ext();
|
||||
}
|
||||
|
||||
bool add_public_library(hash_t lib_addr, hash_t smc_addr, Ref<vm::Cell> lib_root) {
|
||||
if (lib_root.is_null() || lib_root->get_hash().as_array() != lib_addr.as_array()) {
|
||||
return false;
|
||||
}
|
||||
auto ins = public_libraries.emplace(lib_addr, lib_root);
|
||||
PublicLibDescr& lib = ins.first->second;
|
||||
lib.publishers.insert(smc_addr);
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "added " << (ins.second ? "new " : "") << "public library " << lib_addr << " with publisher "
|
||||
<< smc_addr << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
td::RefInt256 create_smartcontract(td::RefInt256 smc_addr, Ref<vm::Cell> code, Ref<vm::Cell> data,
|
||||
Ref<vm::Cell> library, td::RefInt256 balance, int special, int split_depth,
|
||||
int mode) {
|
||||
if (is_empty_cell(code)) {
|
||||
code.clear();
|
||||
}
|
||||
if (is_empty_cell(data)) {
|
||||
data.clear();
|
||||
}
|
||||
if (is_empty_cell(library)) {
|
||||
library.clear();
|
||||
}
|
||||
bool ok = true;
|
||||
if (library.not_null()) {
|
||||
PDO(block::valid_library_collection(library, false));
|
||||
THRERR("not a valid library collection");
|
||||
}
|
||||
vm::CellBuilder cb;
|
||||
if (!split_depth) {
|
||||
PDO(cb.store_long_bool(0, 1));
|
||||
} else {
|
||||
PDO(cb.store_long_bool(1, 1) && cb.store_ulong_rchk_bool(split_depth, 5));
|
||||
}
|
||||
THRERR("invalid split_depth for a smart contract");
|
||||
if (!special) {
|
||||
PDO(cb.store_long_bool(0, 1));
|
||||
} else {
|
||||
PDO(cb.store_long_bool(1, 1) && cb.store_ulong_rchk_bool(special, 2));
|
||||
}
|
||||
THRERR("invalid special TickTock argument for a smart contract");
|
||||
PDO(cb.store_maybe_ref(std::move(code)) && cb.store_maybe_ref(std::move(data)) && cb.store_maybe_ref(library));
|
||||
THRERR("cannot store smart-contract code, data or library");
|
||||
Ref<vm::DataCell> state_init = cb.finalize();
|
||||
hash_t addr;
|
||||
if (smc_addr.is_null()) {
|
||||
addr = state_init->get_hash().as_array();
|
||||
smc_addr = td::RefInt256{true};
|
||||
PDO(smc_addr.write().import_bits(addr.data(), 0, 256, false));
|
||||
} else if (mode == 1) {
|
||||
throw fift::IntError{"cannot create uninitialized smart contracts with specified addresses"};
|
||||
} else {
|
||||
PDO(smc_addr->export_bits(addr.data(), 0, 256, false));
|
||||
}
|
||||
THRERR("cannot initialize smart-contract address");
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "smart-contract address is ";
|
||||
std::cerr << addr << " = " << smc_addr << std::endl;
|
||||
}
|
||||
PDO(mode || !sgn(balance));
|
||||
THRERR("cannot set non-zero balance to smart contract unless it is initialized");
|
||||
PDO(sgn(balance) >= 0);
|
||||
THRERR("balance cannot be negative");
|
||||
if (!mode) {
|
||||
return smc_addr; // compute address only
|
||||
}
|
||||
auto it = smart_contracts.find(addr);
|
||||
if (it != smart_contracts.end()) {
|
||||
std::cerr << "smart contract " << addr << " already defined\n";
|
||||
throw fift::IntError{"smart contract already exists"};
|
||||
}
|
||||
auto ins = smart_contracts.emplace(addr, addr);
|
||||
assert(ins.second);
|
||||
SmcDescr& smc = ins.first->second;
|
||||
smc.split_depth = split_depth;
|
||||
smc.preinit_only = (mode == 1);
|
||||
smc.gram_balance = balance;
|
||||
total_smc_balance += balance;
|
||||
if (mode > 1) {
|
||||
smc.state_init = std::move(state_init);
|
||||
}
|
||||
if (max_total_smc_balance.not_null() && total_smc_balance > max_total_smc_balance) {
|
||||
throw fift::IntError{"total smart-contract balance exceeds limit"};
|
||||
}
|
||||
cb.reset();
|
||||
PDO(cb.store_long_bool(0, 64) // account_storage$_ last_trans_lt:uint64
|
||||
&& block::tlb::t_Grams.store_integer_value(cb, *balance) // balance.grams:Grams
|
||||
&& cb.store_long_bool(0, 1)); // balance.other:ExtraCurrencyCollection
|
||||
if (mode == 1) {
|
||||
PDO(block::gen::t_AccountState.pack_account_uninit(cb));
|
||||
} else {
|
||||
PDO(block::gen::t_AccountState.pack_account_active(cb, vm::load_cell_slice_ref(smc.state_init)));
|
||||
}
|
||||
THRERR("cannot create smart-contract AccountStorage");
|
||||
Ref<vm::DataCell> storage = cb.finalize();
|
||||
vm::CellStorageStat stats;
|
||||
PDO(stats.compute_used_storage(Ref<vm::Cell>(storage)));
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "storage is:\n";
|
||||
vm::load_cell_slice(storage).print_rec(std::cerr);
|
||||
std::cerr << "stats: bits=" << stats.bits << ", cells=" << stats.cells << std::endl;
|
||||
std::cerr << "block::gen::AccountStorage.validate_ref() = " << block::gen::t_AccountStorage.validate_ref(storage)
|
||||
<< std::endl;
|
||||
std::cerr << "block::tlb::AccountStorage.validate_ref() = " << block::tlb::t_AccountStorage.validate_ref(storage)
|
||||
<< std::endl;
|
||||
}
|
||||
PDO(block::gen::t_AccountStorage.validate_ref(storage));
|
||||
THRERR("AccountStorage of created smart-contract is invalid (?)");
|
||||
cb.reset(); // build Account
|
||||
PDO(cb.store_long_bool(1, 1)); // account$1
|
||||
int ctor = 3; // addr_var$11
|
||||
if (workchain_id >= -128 && workchain_id <= 127) {
|
||||
ctor = 2; // addr_std$10
|
||||
}
|
||||
PDO(cb.store_long_bool(ctor, 2)); // addr_std$10 or addr_var$11
|
||||
if (split_depth) {
|
||||
PDO(cb.store_long_bool(1, 1) // just$1
|
||||
&& cb.store_ulong_rchk_bool(split_depth, 5) // depth:(## 5)
|
||||
&& cb.store_bits_bool(addr.cbits(), split_depth)); // rewrite pfx:(depth * Bit)
|
||||
} else {
|
||||
PDO(cb.store_long_bool(0, 1)); // nothing$0
|
||||
}
|
||||
PDO(cb.store_long_rchk_bool(workchain_id, ctor == 2 ? 8 : 32) && cb.store_bits_bool(addr.cbits(), 256));
|
||||
THRERR("Cannot serialize addr:MsgAddressInt of the new smart contract");
|
||||
// storage_stat:StorageInfo -> storage_stat.used:StorageUsed
|
||||
PDO(block::store_UInt7(cb, stats.cells) // cells:(VarUInteger 7)
|
||||
&& block::store_UInt7(cb, stats.bits) // bits:(VarUInteger 7)
|
||||
&& block::store_UInt7(cb, stats.public_cells)); // public_cells:(VarUInteger 7)
|
||||
THRERR("Cannot serialize used:StorageUsed of the new smart contract");
|
||||
PDO(cb.store_long_bool(0, 33)); // last_paid:uint32 due_payment:(Maybe Grams)
|
||||
PDO(cb.append_data_cell_bool(storage)); // storage:AccountStorage
|
||||
THRERR("Cannot create Account of the new smart contract");
|
||||
smc.account = cb.finalize();
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "account is:\n";
|
||||
vm::load_cell_slice(smc.account).print_rec(std::cerr);
|
||||
std::cerr << "block::gen::Account.validate_ref() = " << block::gen::t_Account.validate_ref(smc.account)
|
||||
<< std::endl;
|
||||
std::cerr << "block::tlb::Account.validate_ref() = " << block::tlb::t_Account.validate_ref(smc.account)
|
||||
<< std::endl;
|
||||
}
|
||||
PDO(block::gen::t_Account.validate_ref(smc.account));
|
||||
THRERR("Account of created smart contract is invalid (?)");
|
||||
if (library.not_null()) {
|
||||
vm::Dictionary dict{std::move(library), 256};
|
||||
ok &= dict.check_for_each([addr](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) -> bool {
|
||||
return !cs->prefetch_ulong(1) || add_public_library(key, addr, cs->prefetch_ref());
|
||||
});
|
||||
THRERR("Error processing libraries published by new smart contract");
|
||||
}
|
||||
return smc_addr;
|
||||
}
|
||||
|
||||
// stores accounts:ShardAccounts
|
||||
bool store_accounts(vm::CellBuilder& cb) {
|
||||
vm::AugmentedDictionary dict{256, block::tlb::aug_ShardAccounts};
|
||||
for (const auto& smc_pair : smart_contracts) {
|
||||
const SmcDescr& smc = smc_pair.second;
|
||||
CHECK(smc_pair.first == smc.addr);
|
||||
vm::CellBuilder cb;
|
||||
bool ok = cb.store_ref_bool(smc.account) // account_descr$_ acc:^Account
|
||||
&& cb.store_zeroes_bool(256 + 64) // last_trans_hash:bits256 last_trans_lt:uint64
|
||||
&& dict.set_builder(smc.addr.cbits(), 256, cb, vm::Dictionary::SetMode::Add);
|
||||
CHECK(ok);
|
||||
}
|
||||
return std::move(dict).append_dict_to_bool(cb);
|
||||
}
|
||||
|
||||
// stores libraries:(HashmapE 256 LibDescr)
|
||||
bool store_public_libraries(vm::CellBuilder& cb) {
|
||||
vm::Dictionary dict{256};
|
||||
bool ok = true;
|
||||
vm::CellBuilder empty_cb;
|
||||
for (const auto& lib_pair : public_libraries) {
|
||||
const PublicLibDescr pl = lib_pair.second;
|
||||
PDO(pl.root->get_hash().as_array() == lib_pair.first.as_array());
|
||||
vm::Dictionary publishers{256};
|
||||
for (const auto& publisher : pl.publishers) {
|
||||
PDO(publishers.set_builder(publisher.cbits(), 256, empty_cb, vm::Dictionary::SetMode::Add));
|
||||
}
|
||||
Ref<vm::Cell> root = std::move(publishers).extract_root_cell();
|
||||
PDO(root.not_null());
|
||||
THRERR("public library has an empty or invalid set of publishers");
|
||||
vm::CellBuilder value_cb; // LibDescr
|
||||
PDO(value_cb.store_long_bool(0, 2) && value_cb.store_ref_bool(pl.root) &&
|
||||
value_cb.append_cellslice_bool(vm::load_cell_slice(std::move(root))));
|
||||
THRERR("cannot create LibDescr for a public library");
|
||||
PDO(dict.set_builder(lib_pair.first.cbits(), 256, value_cb, vm::Dictionary::SetMode::Add));
|
||||
THRERR("cannot insert LibDescr of a public library into the public library collection");
|
||||
}
|
||||
PDO(std::move(dict).append_dict_to_bool(cb));
|
||||
return ok;
|
||||
}
|
||||
|
||||
// stores config:ConfigParams
|
||||
bool store_config_params(vm::CellBuilder& cb) {
|
||||
return config_addr_set && config_param_root.not_null() &&
|
||||
cb.store_bits_bool(config_addr.cbits(), 256) // _ config_addr:bits256
|
||||
&& cb.store_ref_bool(config_param_root); // config:^(Hashmap 32 ^Cell)
|
||||
}
|
||||
|
||||
// stores hash of initial masterchain validator set computed from configuration parameter 34
|
||||
bool store_validator_list_hash(vm::CellBuilder& cb) {
|
||||
Ref<vm::Cell> vset_cell = config_dict.lookup_ref(td::BitArray<32>{34});
|
||||
auto res = block::Config::unpack_validator_set(std::move(vset_cell));
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "cannot unpack current validator set: " << res.move_as_error().to_string();
|
||||
return false;
|
||||
}
|
||||
auto vset = res.move_as_ok();
|
||||
LOG_CHECK(vset) << "unpacked validator set is empty";
|
||||
auto ccvc = block::Config::unpack_catchain_validators_config(config_dict.lookup_ref(td::BitArray<32>{28}));
|
||||
ton::ShardIdFull shard{ton::masterchainId};
|
||||
auto nodes = block::Config::do_compute_validator_set(ccvc, shard, *vset, now, 0);
|
||||
LOG_CHECK(!nodes.empty()) << "validator node list in unpacked validator set is empty";
|
||||
auto vset_hash = block::compute_validator_set_hash(0, shard, std::move(nodes));
|
||||
LOG(DEBUG) << "initial validator set hash is " << vset_hash;
|
||||
return cb.store_long_bool(vset_hash, 32);
|
||||
}
|
||||
|
||||
// stores custom:(Maybe ^McStateExtra)
|
||||
bool store_custom(vm::CellBuilder& cb) {
|
||||
if (workchain_id != wc_master) {
|
||||
return cb.store_long_bool(0, 1); // nothing
|
||||
}
|
||||
vm::CellBuilder cb2, cb3;
|
||||
bool ok = true;
|
||||
PDO(cb2.store_long_bool(0xcc26, 16) // masterchain_state_extra#cc26
|
||||
&& cb2.store_long_bool(0, 1) // shard_hashes:ShardHashes = (HashmapE 32 ^(BinTree ShardDescr))
|
||||
&& store_config_params(cb2) // config:ConfigParams
|
||||
&& cb3.store_long_bool(0, 16) // ^[ flags:(## 16) { flags = 0 }
|
||||
&& store_validator_list_hash(cb3) // validator_list_hash_short:uint32
|
||||
&& cb3.store_long_bool(0, 32) // catchain_seqno:uint32
|
||||
&& cb3.store_bool_bool(true) // nx_cc_updated:Bool
|
||||
&& cb3.store_zeroes_bool(1 + 65) // prev_blocks:OldMcBlocksInfo
|
||||
&& cb3.store_long_bool(2, 1 + 1) // after_key_block:Bool last_key_block:(Maybe ...)
|
||||
&& cb2.store_ref_bool(cb3.finalize()) // ]
|
||||
&& block::CurrencyCollection{total_smc_balance}.store(cb2) // global_balance:CurrencyCollection
|
||||
&& cb.store_long_bool(1, 1) // just
|
||||
&& cb.store_ref_bool(cb2.finalize()));
|
||||
return ok;
|
||||
}
|
||||
|
||||
Ref<vm::Cell> create_state() {
|
||||
vm::CellBuilder cb, cb2;
|
||||
now = static_cast<ton::UnixTime>(time(0));
|
||||
bool ok = true;
|
||||
PDO(workchain_id != wc_undef);
|
||||
THRERR("workchain_id is unset, cannot generate state");
|
||||
PDO(workchain_id != wc_master || config_addr_set);
|
||||
THRERR("configuration smart contract must be selected");
|
||||
PDO(cb.store_long_bool(0x9023afe2, 32) // shard_state#9023afe2
|
||||
&& cb.store_long_bool(global_id, 32)); // global_id:int32
|
||||
PDO(cb.store_long_bool(0, 8) && cb.store_long_bool(workchain_id, 32) &&
|
||||
cb.store_long_bool(0, 64) // shard_id:ShardIdent
|
||||
&& cb.store_long_bool(0, 32) // seq_no:#
|
||||
&& cb.store_zeroes_bool(32) // vert_seq_no:#
|
||||
&& cb.store_long_bool(now, 32) // gen_utime:uint32
|
||||
&& cb.store_zeroes_bool(64) // gen_lt:uint64
|
||||
&& cb.store_ones_bool(32) // min_ref_mc_seqno:uint32
|
||||
&& cb2.store_zeroes_bool(1 + 64 + 2) // OutMsgQueueInfo
|
||||
&& cb.store_ref_bool(cb2.finalize()) // out_msg_queue_info:^OutMsgQueueInfo
|
||||
&& cb.store_long_bool(0, 1) // before_split:Bool
|
||||
&& store_accounts(cb2) // accounts:^ShardAccounts
|
||||
&& cb.store_ref_bool(cb2.finalize()) // ...
|
||||
&& cb2.store_zeroes_bool(128) // ^[ overload_history:uint64 underload_history:uint64
|
||||
&& block::CurrencyCollection{total_smc_balance}.store(cb2) // total_balance:CurrencyCollection
|
||||
&& block::tlb::t_CurrencyCollection.null_value(cb2) // total_validator_fees:CurrencyCollection
|
||||
&& store_public_libraries(cb2) // libraries:(Hashmap 256 LibDescr)
|
||||
&& cb2.store_long_bool(0, 1) // master_ref:(Maybe BlkMasterInfo)
|
||||
&& cb.store_ref_bool(cb2.finalize()) // ]
|
||||
&& store_custom(cb)); // custom:(Maybe ^McStateExtra)
|
||||
THRERR("cannot create blockchain state");
|
||||
Ref<vm::Cell> cell = cb.finalize();
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "shard_state is:\n";
|
||||
vm::load_cell_slice(cell).print_rec(std::cerr);
|
||||
std::cerr << "pretty-printed shard_state is:\n";
|
||||
block::gen::t_ShardState.print_ref(std::cerr, cell);
|
||||
std::cerr << "\n";
|
||||
std::cerr << "block::gen::ShardState.validate_ref() = " << block::gen::t_ShardState.validate_ref(cell) << std::endl;
|
||||
std::cerr << "block::tlb::ShardState.validate_ref() = " << block::tlb::t_ShardState.validate_ref(cell) << std::endl;
|
||||
block::gen::ShardStateUnsplit::Record data;
|
||||
bool ok1 = tlb::unpack_cell(cell, data);
|
||||
std::cerr << "block::gen::ShardState.unpack_cell() = " << ok1 << std::endl;
|
||||
if (ok1) {
|
||||
std::cerr << "shard_id = " << data.shard_id
|
||||
<< "; out_msg_queue_info = " << load_cell_slice(data.out_msg_queue_info)
|
||||
<< "; total_balance = " << data.r1.total_balance << std::endl;
|
||||
}
|
||||
}
|
||||
PDO(block::gen::t_ShardState.validate_ref(cell));
|
||||
PDO(block::tlb::t_ShardState.validate_ref(cell));
|
||||
THRERR("created an invalid ShardState record");
|
||||
return cell;
|
||||
}
|
||||
|
||||
// code (cell)
|
||||
// data (cell)
|
||||
// library (cell)
|
||||
// balance (int)
|
||||
// split_depth (int 0..32)
|
||||
// special (int 0..3, +2 = tick, +1 = tock)
|
||||
// [ address (uint256) ]
|
||||
// mode (0 = compute address only, 1 = create uninit, 2 = create complete; +4 = with specified address)
|
||||
// --> 256-bit address
|
||||
void interpret_register_smartcontract(vm::Stack& stack) {
|
||||
if (workchain_id == wc_undef) {
|
||||
throw fift::IntError{"cannot register a smartcontract unless the workchain is specified first"};
|
||||
}
|
||||
td::RefInt256 spec_addr;
|
||||
int mode = stack.pop_smallint_range(2 + 4); // allowed modes: 0 1 2 4 5 6
|
||||
if (mode == 3) {
|
||||
throw fift::IntError{"invalid mode"};
|
||||
}
|
||||
if (mode & 4) {
|
||||
spec_addr = stack.pop_int_finite();
|
||||
mode &= ~4;
|
||||
}
|
||||
int special = stack.pop_smallint_range(3);
|
||||
if (special && workchain_id != wc_master) {
|
||||
throw fift::IntError{"cannot create special smartcontracts outside of the masterchain"};
|
||||
}
|
||||
int split_depth = stack.pop_smallint_range(32);
|
||||
td::RefInt256 balance = stack.pop_int_finite();
|
||||
if (sgn(balance) < 0) {
|
||||
throw fift::IntError{"initial balance of a smartcontract cannot be negative"};
|
||||
}
|
||||
if (sgn(balance) > 0 && !mode) {
|
||||
throw fift::IntError{"cannot set non-zero balance if an account is not created"};
|
||||
}
|
||||
Ref<vm::Cell> library = stack.pop_cell();
|
||||
Ref<vm::Cell> data = stack.pop_cell();
|
||||
Ref<vm::Cell> code = stack.pop_cell();
|
||||
td::RefInt256 addr = create_smartcontract(std::move(spec_addr), std::move(code), std::move(data), std::move(library),
|
||||
std::move(balance), special, split_depth, mode);
|
||||
if (addr.is_null()) {
|
||||
throw fift::IntError{"internal error while creating smartcontract"};
|
||||
}
|
||||
stack.push(std::move(addr));
|
||||
}
|
||||
|
||||
void interpret_create_state(vm::Stack& stack) {
|
||||
if (!global_id) {
|
||||
throw fift::IntError{
|
||||
"(global) blockchain id must be set to a non-zero value: negative for test chains, positive for production"};
|
||||
}
|
||||
Ref<vm::Cell> state = create_state();
|
||||
if (state.is_null()) {
|
||||
throw fift::IntError{"could not create blockchain state"};
|
||||
}
|
||||
stack.push(std::move(state));
|
||||
}
|
||||
|
||||
void interpret_get_config_dict(vm::Stack& stack) {
|
||||
Ref<vm::Cell> value = config_dict.get_root_cell();
|
||||
if (value.is_null()) {
|
||||
stack.push_bool(false);
|
||||
} else {
|
||||
stack.push_cell(std::move(value));
|
||||
stack.push_bool(true);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_get_config_param(vm::Stack& stack) {
|
||||
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
|
||||
Ref<vm::Cell> value = config_dict.lookup_ref(td::BitArray<32>{x});
|
||||
if (value.is_null()) {
|
||||
stack.push_bool(false);
|
||||
} else {
|
||||
stack.push_cell(std::move(value));
|
||||
stack.push_bool(true);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_set_config_param(vm::Stack& stack) {
|
||||
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
|
||||
Ref<vm::Cell> value = stack.pop_cell();
|
||||
if (verbosity > 2 && x >= 0) {
|
||||
std::cerr << "setting configuration parameter #" << x << " to ";
|
||||
// vm::load_cell_slice(value).print_rec(std::cerr);
|
||||
block::gen::ConfigParam{x}.print_ref(std::cerr, value);
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
if (x >= 0 && !block::gen::ConfigParam{x}.validate_ref(value)) {
|
||||
throw fift::IntError{"invalid value for indicated configuration parameter"};
|
||||
}
|
||||
if (!config_dict.set_ref(td::BitArray<32>{x}, std::move(value))) {
|
||||
throw fift::IntError{"cannot set value of configuration parameter (value too long?)"};
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_is_shard_state(vm::Stack& stack) {
|
||||
Ref<vm::Cell> cell = stack.pop_cell();
|
||||
if (verbosity > 4) {
|
||||
std::cerr << "custom shard state is:\n";
|
||||
vm::load_cell_slice(cell).print_rec(std::cerr);
|
||||
std::cerr << "pretty-printed custom shard state is:\n";
|
||||
block::gen::t_ShardState.print_ref(std::cerr, cell);
|
||||
}
|
||||
stack.push_bool(block::gen::t_ShardState.validate_ref(std::move(cell)));
|
||||
}
|
||||
|
||||
void interpret_is_workchain_descr(vm::Stack& stack) {
|
||||
Ref<vm::Cell> cell = stack.pop_cell();
|
||||
if (verbosity > 4) {
|
||||
std::cerr << "WorkchainDescr is:\n";
|
||||
vm::load_cell_slice(cell).print_rec(std::cerr);
|
||||
std::cerr << "pretty-printed WorkchainDescr is:\n";
|
||||
block::gen::t_WorkchainDescr.print_ref(std::cerr, cell);
|
||||
}
|
||||
stack.push_bool(block::gen::t_WorkchainDescr.validate_ref(std::move(cell)));
|
||||
}
|
||||
|
||||
void init_words_custom(fift::Dictionary& d) {
|
||||
d.def_stack_word("verb@ ", interpret_get_verbosity);
|
||||
d.def_stack_word("verb! ", interpret_set_verbosity);
|
||||
d.def_stack_word("wcid@ ", interpret_get_workchain);
|
||||
d.def_stack_word("wcid! ", interpret_set_workchain);
|
||||
d.def_stack_word("globalid@ ", interpret_get_global_id);
|
||||
d.def_stack_word("globalid! ", interpret_set_global_id);
|
||||
d.def_stack_word("config@ ", interpret_get_config_param);
|
||||
d.def_stack_word("config! ", interpret_set_config_param);
|
||||
d.def_stack_word("(configdict) ", interpret_get_config_dict);
|
||||
d.def_stack_word("register_smc ", interpret_register_smartcontract);
|
||||
d.def_stack_word("set_config_smc ", interpret_set_config_smartcontract);
|
||||
d.def_stack_word("create_state ", interpret_create_state);
|
||||
d.def_stack_word("isShardState? ", interpret_is_shard_state);
|
||||
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
|
||||
}
|
||||
|
||||
void usage(const char* progname) {
|
||||
std::cerr
|
||||
<< "Creates initial state for a TON blockchain, using configuration defined by Fift-language source files\n";
|
||||
std::cerr
|
||||
<< "usage: " << progname
|
||||
<< " [-i] [-n] [-I <source-include-path>] {-L <library-fif-file>} <source-file1-fif> <source-file2-fif> ...\n";
|
||||
std::cerr << "\t-n\tDo not preload preamble files `Fift.fif` and `CreateState.fif`\n"
|
||||
"\t-i\tForce interactive mode even if explicit source file names are indicated\n"
|
||||
"\t-I<source-search-path>\tSets colon-separated library source include path. If not indicated, "
|
||||
"$FIFTPATH is used instead.\n"
|
||||
"\t-L<library-fif-file>\tPre-loads a library source file\n"
|
||||
"\t-v<verbosity-level>\tSet verbosity level\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
void parse_include_path_set(std::string include_path_set, std::vector<std::string>& res) {
|
||||
td::Parser parser(include_path_set);
|
||||
while (!parser.empty()) {
|
||||
auto path = parser.read_till_nofail(':');
|
||||
if (!path.empty()) {
|
||||
res.push_back(path.str());
|
||||
}
|
||||
parser.skip_nofail(':');
|
||||
}
|
||||
}
|
||||
|
||||
void preload_preamble(fift::Fift& fift, std::string filename, bool standard = true) {
|
||||
auto status = fift.interpret_file(filename, "");
|
||||
if (status.is_error()) {
|
||||
LOG(ERROR) << "Error interpreting " << (standard ? "standard" : "application-specific") << " preamble file `"
|
||||
<< filename << "`: " << status.error().message()
|
||||
<< "\nCheck that correct include path is set by -I or by FIFTPATH environment variable, or disable "
|
||||
"standard preamble by -n.\n";
|
||||
std::exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
td::set_default_failure_signal_handler().ensure();
|
||||
bool interactive = false;
|
||||
bool fift_preload = true, no_env = false, script_mode = false;
|
||||
std::vector<std::string> library_source_files, source_list;
|
||||
std::vector<std::string> source_include_path;
|
||||
std::string ton_db_path;
|
||||
|
||||
fift::Fift::Config config;
|
||||
|
||||
int i;
|
||||
int new_verbosity_level = VERBOSITY_NAME(INFO);
|
||||
while (!script_mode && (i = getopt(argc, argv, "hinsI:L:v:")) != -1) {
|
||||
switch (i) {
|
||||
case 'i':
|
||||
interactive = true;
|
||||
break;
|
||||
case 'n':
|
||||
fift_preload = false;
|
||||
break;
|
||||
case 'I':
|
||||
LOG(ERROR) << source_include_path;
|
||||
parse_include_path_set(optarg, source_include_path);
|
||||
no_env = true;
|
||||
break;
|
||||
case 's':
|
||||
script_mode = true;
|
||||
break;
|
||||
case 'L':
|
||||
library_source_files.emplace_back(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
SET_VERBOSITY_LEVEL(new_verbosity_level);
|
||||
|
||||
while (optind < argc) {
|
||||
source_list.emplace_back(argv[optind++]);
|
||||
if (script_mode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_env) {
|
||||
const char* path = std::getenv("FIFTPATH");
|
||||
if (path) {
|
||||
parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path);
|
||||
}
|
||||
}
|
||||
std::string current_dir;
|
||||
auto r_current_dir = td::realpath(".");
|
||||
if (r_current_dir.is_ok()) {
|
||||
current_dir = r_current_dir.move_as_ok();
|
||||
source_include_path.push_back(current_dir);
|
||||
}
|
||||
config.source_lookup = fift::SourceLookup(std::make_unique<fift::OsFileLoader>());
|
||||
for (auto& path : source_include_path) {
|
||||
config.source_lookup.add_include_path(path);
|
||||
}
|
||||
|
||||
fift::init_words_common(config.dictionary);
|
||||
fift::init_words_vm(config.dictionary);
|
||||
fift::init_words_ton(config.dictionary);
|
||||
init_words_custom(config.dictionary);
|
||||
|
||||
if (script_mode) {
|
||||
fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,
|
||||
argv + optind);
|
||||
}
|
||||
|
||||
fift::Fift fift(std::move(config));
|
||||
|
||||
if (fift_preload) {
|
||||
preload_preamble(fift, "Fift.fif", true);
|
||||
preload_preamble(fift, "CreateState.fif", false);
|
||||
}
|
||||
|
||||
for (auto source : library_source_files) {
|
||||
auto status = fift.interpret_file(source, "");
|
||||
if (status.is_error()) {
|
||||
std::cerr << "Error interpreting preloaded file `" << source << "`: " << status.error().to_string() << std::endl;
|
||||
std::exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (source_list.empty() && !interactive) {
|
||||
std::cerr << "No Fift source files specified" << std::endl;
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
for (const auto& source : source_list) {
|
||||
auto status = fift.interpret_file(source, current_dir);
|
||||
if (status.is_error()) {
|
||||
std::cerr << "Error interpreting file `" << source << "`: " << status.error().to_string() << std::endl;
|
||||
std::exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (interactive) {
|
||||
fift.interpret_istream(std::cin, current_dir).ensure();
|
||||
}
|
||||
// show_total_cells();
|
||||
}
|
1817
crypto/block/mc-config.cpp
Normal file
1817
crypto/block/mc-config.cpp
Normal file
File diff suppressed because it is too large
Load diff
617
crypto/block/mc-config.h
Normal file
617
crypto/block/mc-config.h
Normal file
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "common/refcnt.hpp"
|
||||
#include "vm/db/StaticBagOfCellsDb.h"
|
||||
#include "vm/dict.h"
|
||||
#include "ton/ton-types.h"
|
||||
#include "ton/ton-shard.h"
|
||||
#include "common/bitstring.h"
|
||||
#include "block.h"
|
||||
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <cstring>
|
||||
|
||||
namespace block {
|
||||
using td::Ref;
|
||||
|
||||
struct ValidatorDescr {
|
||||
ton::Ed25519_PublicKey pubkey;
|
||||
td::Bits256 adnl_addr;
|
||||
td::uint64 weight;
|
||||
td::uint64 cum_weight;
|
||||
ValidatorDescr(const td::Bits256& _pubkey, td::uint64 _weight, td::uint64 _cum_weight)
|
||||
: pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
|
||||
adnl_addr.set_zero();
|
||||
}
|
||||
ValidatorDescr(const td::Bits256& _pubkey, td::uint64 _weight, td::uint64 _cum_weight, const td::Bits256& _adnl_addr)
|
||||
: pubkey(_pubkey), adnl_addr(_adnl_addr), weight(_weight), cum_weight(_cum_weight) {
|
||||
}
|
||||
ValidatorDescr(const ton::Ed25519_PublicKey& _pubkey, td::uint64 _weight, td::uint64 _cum_weight)
|
||||
: pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
|
||||
adnl_addr.set_zero();
|
||||
}
|
||||
bool operator<(td::uint64 wt_pos) const & {
|
||||
return cum_weight < wt_pos;
|
||||
}
|
||||
};
|
||||
|
||||
struct ValidatorSet {
|
||||
unsigned utime_since;
|
||||
unsigned utime_until;
|
||||
int total;
|
||||
int main;
|
||||
td::uint64 total_weight;
|
||||
std::vector<ValidatorDescr> list;
|
||||
ValidatorSet() = default;
|
||||
ValidatorSet(unsigned _since, unsigned _until, int _total, int _main = 0)
|
||||
: utime_since(_since), utime_until(_until), total(_total), main(_main > 0 ? _main : _total), total_weight(0) {
|
||||
}
|
||||
const ValidatorDescr& operator[](unsigned i) const {
|
||||
return list[i];
|
||||
}
|
||||
const ValidatorDescr& at_weight(td::uint64 weight_pos) const;
|
||||
std::vector<ton::ValidatorDescr> export_validator_set() const;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// this structure is hashed with SHA512 to produce pseudo-random bit stream in do_compute_validator_set()
|
||||
// NB: all integers (including 256-bit seed) are actually big-endian
|
||||
struct validator_set_descr {
|
||||
unsigned char seed[32]; // seed for validator set computation, set to zero if none
|
||||
td::uint64 shard;
|
||||
td::int32 workchain;
|
||||
td::uint32 cc_seqno;
|
||||
validator_set_descr() = default;
|
||||
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, bool flag)
|
||||
: shard(td::bswap64(shard_id.shard))
|
||||
, workchain(td::bswap32(shard_id.workchain))
|
||||
, cc_seqno(td::bswap32(cc_seqno_)) {
|
||||
}
|
||||
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_)
|
||||
: validator_set_descr(shard_id, cc_seqno_, false) {
|
||||
std::memset(seed, 0, 32);
|
||||
}
|
||||
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const unsigned char seed_[32])
|
||||
: validator_set_descr(shard_id, cc_seqno_, false) {
|
||||
std::memcpy(seed, seed_, 32);
|
||||
}
|
||||
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, td::ConstBitPtr seed_)
|
||||
: validator_set_descr(shard_id, cc_seqno_, false) {
|
||||
td::BitPtr{seed}.copy_from(seed_, 256);
|
||||
}
|
||||
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const td::Bits256& seed_)
|
||||
: validator_set_descr(shard_id, cc_seqno_, false) {
|
||||
td::BitPtr{seed}.copy_from(seed_.cbits(), 256);
|
||||
}
|
||||
void incr_seed();
|
||||
void hash_to(unsigned char hash_buffer[64]) const;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class ValidatorSetPRNG {
|
||||
validator_set_descr data;
|
||||
union {
|
||||
unsigned char hash[64];
|
||||
td::uint64 hash_longs[8];
|
||||
};
|
||||
int pos{0}, limit{0};
|
||||
|
||||
public:
|
||||
ValidatorSetPRNG() = default;
|
||||
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_) : data(shard_id, cc_seqno_) {
|
||||
}
|
||||
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const unsigned char seed_[32])
|
||||
: data(shard_id, cc_seqno_, seed_) {
|
||||
}
|
||||
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, td::ConstBitPtr seed_)
|
||||
: data(shard_id, cc_seqno_, std::move(seed_)) {
|
||||
}
|
||||
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const td::Bits256& seed_)
|
||||
: data(shard_id, cc_seqno_, seed_) {
|
||||
}
|
||||
td::uint64 next_ulong();
|
||||
td::uint64 next_ranged(td::uint64 range); // integer in 0 .. range-1
|
||||
ValidatorSetPRNG& operator>>(td::uint64& x) {
|
||||
x = next_ulong();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class McShardHashI : public td::CntObject {
|
||||
public:
|
||||
enum class FsmState { fsm_none, fsm_split, fsm_merge };
|
||||
virtual ton::BlockIdExt top_block_id() const = 0;
|
||||
virtual ton::LogicalTime start_lt() const = 0;
|
||||
virtual ton::LogicalTime end_lt() const = 0;
|
||||
virtual ton::UnixTime fsm_utime() const = 0;
|
||||
virtual FsmState fsm_state() const = 0;
|
||||
virtual ton::ShardIdFull shard() const = 0;
|
||||
virtual bool before_split() const = 0;
|
||||
virtual bool before_merge() const = 0;
|
||||
};
|
||||
|
||||
struct McShardHash : public McShardHashI {
|
||||
ton::BlockIdExt blk_;
|
||||
ton::LogicalTime start_lt_, end_lt_;
|
||||
ton::UnixTime gen_utime_{0};
|
||||
ton::UnixTime fsm_utime_{0};
|
||||
ton::UnixTime fsm_interval_{0};
|
||||
ton::BlockSeqno min_ref_mc_seqno_{std::numeric_limits<ton::BlockSeqno>::max()};
|
||||
ton::BlockSeqno reg_mc_seqno_{std::numeric_limits<ton::BlockSeqno>::max()};
|
||||
FsmState fsm_{FsmState::fsm_none};
|
||||
bool disabled_{false};
|
||||
bool before_split_{false}, before_merge_{false}, want_split_{false}, want_merge_{false};
|
||||
ton::CatchainSeqno next_catchain_seqno_{std::numeric_limits<ton::CatchainSeqno>::max()};
|
||||
ton::ShardId next_validator_shard_{ton::shardIdAll};
|
||||
CurrencyCollection fees_collected_, funds_created_;
|
||||
McShardHash(const ton::BlockId& id, ton::LogicalTime start_lt, ton::LogicalTime end_lt, ton::UnixTime gen_utime,
|
||||
const ton::BlockHash& root_hash, const ton::FileHash& file_hash, CurrencyCollection fees_collected = {},
|
||||
CurrencyCollection funds_created = {},
|
||||
ton::BlockSeqno reg_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
|
||||
ton::BlockSeqno min_ref_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
|
||||
ton::CatchainSeqno cc_seqno = std::numeric_limits<ton::CatchainSeqno>::max(), ton::ShardId val_shard = 0,
|
||||
bool nx_cc_updated = false, bool before_split = false, bool before_merge = false, bool want_split = false,
|
||||
bool want_merge = false)
|
||||
: blk_(id, root_hash, file_hash)
|
||||
, start_lt_(start_lt)
|
||||
, end_lt_(end_lt)
|
||||
, gen_utime_(gen_utime)
|
||||
, min_ref_mc_seqno_(min_ref_mc_seqno)
|
||||
, reg_mc_seqno_(reg_mc_seqno)
|
||||
, before_split_(before_split)
|
||||
, before_merge_(before_merge)
|
||||
, want_split_(want_split)
|
||||
, want_merge_(want_merge)
|
||||
, next_catchain_seqno_(cc_seqno)
|
||||
, next_validator_shard_(val_shard ? val_shard : id.shard)
|
||||
, fees_collected_(fees_collected)
|
||||
, funds_created_(funds_created) {
|
||||
}
|
||||
McShardHash(const ton::BlockIdExt& blk, ton::LogicalTime start_lt, ton::LogicalTime end_lt)
|
||||
: blk_(blk), start_lt_(start_lt), end_lt_(end_lt) {
|
||||
}
|
||||
McShardHash(const McShardHash&) = default;
|
||||
bool is_valid() const {
|
||||
return blk_.is_valid();
|
||||
}
|
||||
ton::BlockIdExt top_block_id() const override final {
|
||||
return blk_;
|
||||
}
|
||||
// ZeroStateIdExt zero_state() const override;
|
||||
ton::LogicalTime start_lt() const override final {
|
||||
return start_lt_;
|
||||
}
|
||||
ton::LogicalTime end_lt() const override final {
|
||||
return end_lt_;
|
||||
}
|
||||
ton::UnixTime fsm_utime() const override final {
|
||||
return fsm_utime_;
|
||||
}
|
||||
ton::UnixTime fsm_utime_end() const {
|
||||
return fsm_utime_ + fsm_interval_;
|
||||
}
|
||||
ton::UnixTime created_at() const {
|
||||
return gen_utime_;
|
||||
}
|
||||
FsmState fsm_state() const override final {
|
||||
return fsm_;
|
||||
}
|
||||
bool is_fsm_none() const {
|
||||
return fsm_ == FsmState::fsm_none;
|
||||
}
|
||||
bool is_fsm_split() const {
|
||||
return fsm_ == FsmState::fsm_split;
|
||||
}
|
||||
bool is_fsm_merge() const {
|
||||
return fsm_ == FsmState::fsm_merge;
|
||||
}
|
||||
ton::ShardIdFull shard() const override final {
|
||||
return ton::ShardIdFull(blk_);
|
||||
}
|
||||
ton::WorkchainId workchain() const {
|
||||
return blk_.id.workchain;
|
||||
}
|
||||
bool contains(const ton::AccountIdPrefixFull& pfx) const {
|
||||
return ton::shard_contains(shard(), pfx);
|
||||
}
|
||||
bool before_split() const override final {
|
||||
return before_split_;
|
||||
}
|
||||
bool before_merge() const override final {
|
||||
return before_merge_;
|
||||
}
|
||||
bool is_disabled() const {
|
||||
return disabled_;
|
||||
}
|
||||
void disable() {
|
||||
blk_.invalidate();
|
||||
disabled_ = true;
|
||||
}
|
||||
ton::BlockSeqno seqno() const {
|
||||
return blk_.id.seqno;
|
||||
}
|
||||
bool set_reg_mc_seqno(ton::BlockSeqno reg_mc_seqno) {
|
||||
reg_mc_seqno_ = reg_mc_seqno;
|
||||
return true;
|
||||
}
|
||||
// compares all fields except fsm*, before_merge_, nx_cc_updated_, next_catchain_seqno_, fees_collected_
|
||||
bool basic_info_equal(const McShardHash& other, bool compare_fees = false, bool compare_reg_seqno = true) const;
|
||||
void clear_fsm() {
|
||||
fsm_ = FsmState::fsm_none;
|
||||
}
|
||||
void set_fsm(FsmState fsm, ton::UnixTime fsm_utime, ton::UnixTime fsm_interval);
|
||||
void set_fsm_split(ton::UnixTime fsm_utime, ton::UnixTime fsm_interval) {
|
||||
set_fsm(FsmState::fsm_split, fsm_utime, fsm_interval);
|
||||
}
|
||||
void set_fsm_merge(ton::UnixTime fsm_utime, ton::UnixTime fsm_interval) {
|
||||
set_fsm(FsmState::fsm_merge, fsm_utime, fsm_interval);
|
||||
}
|
||||
bool fsm_equal(const McShardHash& other) const {
|
||||
return fsm_ == other.fsm_ &&
|
||||
(is_fsm_none() || (fsm_utime_ == other.fsm_utime_ && fsm_interval_ == other.fsm_interval_));
|
||||
}
|
||||
bool pack(vm::CellBuilder& cb) const;
|
||||
static Ref<McShardHash> unpack(vm::CellSlice& cs, ton::ShardIdFull id);
|
||||
static Ref<McShardHash> from_block(Ref<vm::Cell> block_root, const ton::FileHash& _fhash, bool init_fees = false);
|
||||
McShardHash* make_copy() const override {
|
||||
return new McShardHash(*this);
|
||||
}
|
||||
};
|
||||
|
||||
struct McShardDescr final : public McShardHash {
|
||||
Ref<vm::Cell> block_root;
|
||||
Ref<vm::Cell> state_root;
|
||||
Ref<vm::Cell> outmsg_root;
|
||||
std::unique_ptr<vm::AugmentedDictionary> out_msg_queue;
|
||||
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto;
|
||||
McShardDescr(const ton::BlockId& id, ton::LogicalTime start_lt, ton::LogicalTime end_lt, ton::UnixTime gen_utime,
|
||||
const ton::BlockHash& root_hash, const ton::FileHash& file_hash, CurrencyCollection fees_collected = {},
|
||||
CurrencyCollection funds_created = {},
|
||||
ton::BlockSeqno reg_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
|
||||
ton::BlockSeqno min_ref_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
|
||||
ton::CatchainSeqno cc_seqno = std::numeric_limits<ton::CatchainSeqno>::max(),
|
||||
ton::ShardId val_shard = ton::shardIdAll, bool nx_cc_updated = false, bool before_split = false,
|
||||
bool before_merge = false, bool want_split = false, bool want_merge = false)
|
||||
: McShardHash(id, start_lt, end_lt, gen_utime, root_hash, file_hash, fees_collected, funds_created, reg_mc_seqno,
|
||||
min_ref_mc_seqno, cc_seqno, val_shard, nx_cc_updated, before_split, before_merge, want_split,
|
||||
want_merge) {
|
||||
}
|
||||
McShardDescr(const ton::BlockIdExt& blk, ton::LogicalTime start_lt, ton::LogicalTime end_lt)
|
||||
: McShardHash(blk, start_lt, end_lt) {
|
||||
}
|
||||
McShardDescr(const McShardHash& shard_hash) : McShardHash(shard_hash) {
|
||||
}
|
||||
McShardDescr(const McShardDescr& other);
|
||||
McShardDescr(McShardDescr&& other) = default;
|
||||
McShardDescr& operator=(const McShardDescr& other);
|
||||
McShardDescr& operator=(McShardDescr&& other) = default;
|
||||
bool set_queue_root(Ref<vm::Cell> queue_root);
|
||||
void disable();
|
||||
static Ref<McShardDescr> from_block(Ref<vm::Cell> block_root, Ref<vm::Cell> state_root, const ton::FileHash& _fhash,
|
||||
bool init_fees = false);
|
||||
static Ref<McShardDescr> from_state(ton::BlockIdExt blkid, Ref<vm::Cell> state_root);
|
||||
};
|
||||
|
||||
struct StoragePrices {
|
||||
ton::UnixTime valid_since{0};
|
||||
td::uint64 bit_price{0};
|
||||
td::uint64 cell_price{0};
|
||||
td::uint64 mc_bit_price{0};
|
||||
td::uint64 mc_cell_price{0};
|
||||
StoragePrices() = default;
|
||||
StoragePrices(ton::UnixTime _valid_since, td::uint64 _bprice, td::uint64 _cprice, td::uint64 _mc_bprice,
|
||||
td::uint64 _mc_cprice)
|
||||
: valid_since(_valid_since)
|
||||
, bit_price(_bprice)
|
||||
, cell_price(_cprice)
|
||||
, mc_bit_price(_mc_bprice)
|
||||
, mc_cell_price(_mc_cprice) {
|
||||
}
|
||||
};
|
||||
|
||||
struct CatchainValidatorsConfig {
|
||||
td::uint32 mc_cc_lifetime, shard_cc_lifetime, shard_val_lifetime, shard_val_num;
|
||||
CatchainValidatorsConfig(td::uint32 mc_cc_lt_, td::uint32 sh_cc_lt_, td::uint32 sh_val_lt_, td::uint32 sh_val_num_)
|
||||
: mc_cc_lifetime(mc_cc_lt_)
|
||||
, shard_cc_lifetime(sh_cc_lt_)
|
||||
, shard_val_lifetime(sh_val_lt_)
|
||||
, shard_val_num(sh_val_num_) {
|
||||
}
|
||||
};
|
||||
|
||||
struct WorkchainInfo : public td::CntObject {
|
||||
ton::WorkchainId workchain{ton::workchainInvalid};
|
||||
ton::UnixTime enabled_since;
|
||||
td::uint32 actual_min_split;
|
||||
td::uint32 min_split, max_split;
|
||||
bool basic;
|
||||
bool active;
|
||||
bool accept_msgs;
|
||||
int flags;
|
||||
td::uint32 version;
|
||||
ton::RootHash zerostate_root_hash;
|
||||
ton::FileHash zerostate_file_hash;
|
||||
int min_addr_len, max_addr_len, addr_len_step;
|
||||
bool is_valid() const {
|
||||
return workchain != ton::workchainInvalid;
|
||||
}
|
||||
bool is_valid_addr_len(int addr_len) const {
|
||||
return addr_len >= min_addr_len && addr_len <= max_addr_len &&
|
||||
(addr_len == min_addr_len || addr_len == max_addr_len ||
|
||||
(addr_len_step > 0 && !((addr_len - min_addr_len) % addr_len_step)));
|
||||
}
|
||||
bool unpack(ton::WorkchainId wc, vm::CellSlice& cs);
|
||||
};
|
||||
|
||||
using WorkchainSet = std::map<td::int32, Ref<WorkchainInfo>>;
|
||||
|
||||
class ShardConfig {
|
||||
Ref<vm::Cell> shard_hashes_;
|
||||
Ref<McShardHash> mc_shard_hash_;
|
||||
std::unique_ptr<vm::Dictionary> shard_hashes_dict_;
|
||||
std::set<ton::ShardIdFull> shards_updated_;
|
||||
bool valid_{false};
|
||||
|
||||
public:
|
||||
ShardConfig() = default;
|
||||
ShardConfig(const ShardConfig& other);
|
||||
ShardConfig(ShardConfig&& other) = default;
|
||||
ShardConfig(Ref<vm::Cell> shard_hashes, Ref<McShardHash> mc_shard_hash = {})
|
||||
: shard_hashes_(std::move(shard_hashes)), mc_shard_hash_(std::move(mc_shard_hash)) {
|
||||
init();
|
||||
}
|
||||
bool is_valid() const {
|
||||
return valid_;
|
||||
}
|
||||
bool unpack(Ref<vm::Cell> shard_hashes, Ref<McShardHash> mc_shard_hash = {});
|
||||
bool unpack(Ref<vm::CellSlice> shard_hashes, Ref<McShardHash> mc_shard_hash = {});
|
||||
Ref<vm::CellSlice> get_root_csr() const;
|
||||
bool has_workchain(ton::WorkchainId workchain) const;
|
||||
std::vector<ton::WorkchainId> get_workchains() const;
|
||||
Ref<McShardHash> get_shard_hash(ton::ShardIdFull id, bool exact = true) const;
|
||||
bool contains(ton::BlockIdExt blkid) const;
|
||||
bool get_shard_hash_raw(vm::CellSlice& cs, ton::ShardIdFull id, ton::ShardIdFull& true_id, bool exact = true) const;
|
||||
ton::LogicalTime get_shard_end_lt(ton::AccountIdPrefixFull acc) const;
|
||||
ton::LogicalTime get_shard_end_lt_ext(ton::AccountIdPrefixFull acc, ton::ShardIdFull& actual_shard) const;
|
||||
static bool get_shard_hash_raw_from(vm::Dictionary& shard_hashes_dict, vm::CellSlice& cs, ton::ShardIdFull id,
|
||||
ton::ShardIdFull& true_id, bool exact = true, Ref<vm::Cell>* leaf = nullptr);
|
||||
std::vector<ton::BlockId> get_shard_hash_ids(bool skip_mc = false) const;
|
||||
std::vector<ton::BlockId> get_shard_hash_ids(const std::function<bool(ton::ShardIdFull, bool)>& filter) const;
|
||||
std::vector<ton::BlockId> get_intersecting_shard_hash_ids(ton::ShardIdFull myself) const;
|
||||
std::vector<ton::BlockId> get_neighbor_shard_hash_ids(ton::ShardIdFull myself) const;
|
||||
std::vector<ton::BlockId> get_proper_neighbor_shard_hash_ids(ton::ShardIdFull myself) const;
|
||||
static std::unique_ptr<vm::Dictionary> extract_shard_hashes_dict(Ref<vm::Cell> mc_state_root);
|
||||
bool process_shard_hashes(std::function<int(McShardHash&)> func);
|
||||
bool process_sibling_shard_hashes(std::function<int(McShardHash&, const McShardHash*)> func);
|
||||
// may become non-static const in the future
|
||||
static bool is_neighbor(ton::ShardIdFull x, ton::ShardIdFull y);
|
||||
Ref<McShardHash> get_mc_hash() const {
|
||||
return mc_shard_hash_;
|
||||
}
|
||||
void set_mc_hash(Ref<McShardHash> mc_shard_hash) {
|
||||
mc_shard_hash_ = std::move(mc_shard_hash);
|
||||
}
|
||||
ton::CatchainSeqno get_shard_cc_seqno(ton::ShardIdFull shard) const;
|
||||
block::compute_shard_end_lt_func_t get_compute_shard_end_lt_func() const {
|
||||
return std::bind(&ShardConfig::get_shard_end_lt, *this, std::placeholders::_1);
|
||||
}
|
||||
bool new_workchain(ton::WorkchainId workchain, ton::BlockSeqno reg_mc_seqno, const ton::RootHash& zerostate_root_hash,
|
||||
const ton::FileHash& zerostate_file_hash);
|
||||
td::Result<bool> update_shard_block_info(Ref<McShardHash> new_info, const std::vector<ton::BlockIdExt>& old_blkids);
|
||||
td::Result<bool> update_shard_block_info2(Ref<McShardHash> new_info1, Ref<McShardHash> new_info2,
|
||||
const std::vector<ton::BlockIdExt>& old_blkids);
|
||||
td::Result<bool> may_update_shard_block_info(Ref<McShardHash> new_info,
|
||||
const std::vector<ton::BlockIdExt>& old_blkids,
|
||||
ton::LogicalTime lt_limit = std::numeric_limits<ton::LogicalTime>::max(),
|
||||
Ref<McShardHash>* ancestor = nullptr) const;
|
||||
|
||||
private:
|
||||
bool init();
|
||||
bool do_update_shard_info(Ref<McShardHash> new_info);
|
||||
bool do_update_shard_info2(Ref<McShardHash> new_info1, Ref<McShardHash> new_info2);
|
||||
bool set_shard_info(ton::ShardIdFull shard, Ref<vm::Cell> value);
|
||||
};
|
||||
|
||||
class Config {
|
||||
enum {
|
||||
default_mc_catchain_lifetime = 200,
|
||||
default_shard_catchain_lifetime = 200,
|
||||
default_shard_validators_lifetime = 3000,
|
||||
default_shard_validators_num = 7
|
||||
};
|
||||
|
||||
public:
|
||||
enum { needValidatorSet = 16, needSpecialSmc = 32, needWorkchainInfo = 256 };
|
||||
int mode{0};
|
||||
ton::BlockIdExt block_id;
|
||||
|
||||
private:
|
||||
td::BitArray<256> config_addr;
|
||||
Ref<vm::Cell> config_root;
|
||||
std::unique_ptr<vm::Dictionary> config_dict;
|
||||
std::unique_ptr<ValidatorSet> cur_validators_;
|
||||
std::unique_ptr<vm::Dictionary> workchains_dict_;
|
||||
WorkchainSet workchains_;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<vm::Dictionary> special_smc_dict;
|
||||
|
||||
public:
|
||||
static constexpr ton::LogicalTime get_lt_align() {
|
||||
return 1000000;
|
||||
}
|
||||
static constexpr ton::LogicalTime get_max_lt_growth() {
|
||||
return 10 * get_lt_align() - 1;
|
||||
}
|
||||
Ref<vm::Cell> get_config_param(int idx) const;
|
||||
Ref<vm::Cell> get_config_param(int idx, int idx2) const;
|
||||
Ref<vm::Cell> operator[](int idx) const {
|
||||
return get_config_param(idx);
|
||||
}
|
||||
Ref<vm::Cell> get_root_cell() const {
|
||||
return config_root;
|
||||
}
|
||||
bool is_masterchain() const {
|
||||
return block_id.is_masterchain();
|
||||
}
|
||||
bool set_block_id_ext(const ton::BlockIdExt& block_id_ext);
|
||||
td::Result<std::vector<ton::StdSmcAddress>> get_special_smartcontracts(bool without_config = false) const;
|
||||
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
|
||||
static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root);
|
||||
td::Result<std::vector<StoragePrices>> get_storage_prices() const;
|
||||
static CatchainValidatorsConfig unpack_catchain_validators_config(Ref<vm::Cell> cell);
|
||||
CatchainValidatorsConfig get_catchain_validators_config() const;
|
||||
td::Status visit_validator_params() const;
|
||||
td::Result<std::unique_ptr<BlockLimits>> get_block_limits(bool is_masterchain = false) const;
|
||||
auto get_mc_block_limits() const {
|
||||
return get_block_limits(true);
|
||||
}
|
||||
static td::Result<std::pair<WorkchainSet, std::unique_ptr<vm::Dictionary>>> unpack_workchain_list_ext(
|
||||
Ref<vm::Cell> cell);
|
||||
static td::Result<WorkchainSet> unpack_workchain_list(Ref<vm::Cell> cell);
|
||||
const WorkchainSet& get_workchain_list() const {
|
||||
return workchains_;
|
||||
}
|
||||
const ValidatorSet* get_cur_validator_set() const {
|
||||
return cur_validators_.get();
|
||||
}
|
||||
ton::ValidatorSessionConfig get_consensus_config() const;
|
||||
bool foreach_config_param(std::function<bool(int, Ref<vm::Cell>)> scan_func) const;
|
||||
Ref<WorkchainInfo> get_workchain_info(ton::WorkchainId workchain_id) const;
|
||||
std::vector<ton::ValidatorDescr> compute_validator_set(ton::ShardIdFull shard, const block::ValidatorSet& vset,
|
||||
ton::UnixTime time, ton::CatchainSeqno cc_seqno) const;
|
||||
std::vector<ton::ValidatorDescr> compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time,
|
||||
ton::CatchainSeqno cc_seqno) const;
|
||||
std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) const;
|
||||
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
|
||||
ton::ShardIdFull shard,
|
||||
const block::ValidatorSet& vset, ton::UnixTime time,
|
||||
ton::CatchainSeqno cc_seqno);
|
||||
|
||||
static td::Result<std::unique_ptr<Config>> unpack_config(Ref<vm::Cell> config_root,
|
||||
const td::Bits256& config_addr = td::Bits256::zero(),
|
||||
int mode = 0);
|
||||
static td::Result<std::unique_ptr<Config>> unpack_config(Ref<vm::CellSlice> config_csr, int mode = 0);
|
||||
static td::Result<std::unique_ptr<Config>> extract_from_state(Ref<vm::Cell> mc_state_root, int mode = 0);
|
||||
static td::Result<std::unique_ptr<Config>> extract_from_key_block(Ref<vm::Cell> key_block_root, int mode = 0);
|
||||
|
||||
protected:
|
||||
Config(int _mode) : mode(_mode) {
|
||||
config_addr.set_zero();
|
||||
}
|
||||
Config(Ref<vm::Cell> config_root, const td::Bits256& config_addr = td::Bits256::zero(), int _mode = 0);
|
||||
td::Status unpack_wrapped(Ref<vm::CellSlice> config_csr);
|
||||
td::Status unpack(Ref<vm::CellSlice> config_csr);
|
||||
td::Status unpack_wrapped();
|
||||
td::Status unpack();
|
||||
};
|
||||
|
||||
class ConfigInfo : public Config, public ShardConfig {
|
||||
public:
|
||||
enum {
|
||||
needStateRoot = 1,
|
||||
needLibraries = 2,
|
||||
needStateExtraRoot = 4,
|
||||
needShardHashes = 8,
|
||||
needAccountsRoot = 64,
|
||||
needPrevBlocks = 128
|
||||
};
|
||||
int vert_seqno{-1};
|
||||
int global_id_{0};
|
||||
ton::UnixTime utime{0};
|
||||
ton::LogicalTime lt{0};
|
||||
ton::BlockSeqno min_ref_mc_seqno_{std::numeric_limits<ton::BlockSeqno>::max()};
|
||||
ton::CatchainSeqno cc_seqno_{std::numeric_limits<ton::CatchainSeqno>::max()};
|
||||
int shard_cc_updated{-1};
|
||||
bool nx_cc_updated;
|
||||
bool is_key_state_{false};
|
||||
|
||||
private:
|
||||
Ref<vm::Cell> state_root;
|
||||
Ref<vm::Cell> lib_root_;
|
||||
Ref<vm::Cell> state_extra_root_;
|
||||
Ref<vm::CellSlice> accounts_root;
|
||||
ton::ZeroStateIdExt zerostate_id_;
|
||||
ton::BlockIdExt last_key_block_;
|
||||
ton::LogicalTime last_key_block_lt_;
|
||||
Ref<vm::Cell> shard_hashes;
|
||||
std::unique_ptr<vm::Dictionary> shard_hashes_dict;
|
||||
std::unique_ptr<vm::AugmentedDictionary> accounts_dict;
|
||||
std::unique_ptr<vm::AugmentedDictionary> prev_blocks_dict_;
|
||||
std::unique_ptr<vm::Dictionary> libraries_dict_;
|
||||
|
||||
public:
|
||||
bool set_block_id_ext(const ton::BlockIdExt& block_id_ext);
|
||||
bool rotated_all_shards() const {
|
||||
return nx_cc_updated;
|
||||
}
|
||||
int get_global_blockchain_id() const {
|
||||
return global_id_;
|
||||
}
|
||||
ton::ZeroStateIdExt get_zerostate_id() const {
|
||||
return zerostate_id_;
|
||||
}
|
||||
Ref<vm::Cell> lookup_library(const ton::Bits256& root_hash) const {
|
||||
return lookup_library(root_hash.bits());
|
||||
}
|
||||
Ref<vm::Cell> lookup_library(td::ConstBitPtr root_hash) const;
|
||||
Ref<vm::Cell> get_libraries_root() const {
|
||||
return lib_root_;
|
||||
}
|
||||
bool is_key_state() const {
|
||||
return is_key_state_;
|
||||
}
|
||||
Ref<vm::Cell> get_state_extra_root() const {
|
||||
return state_extra_root_;
|
||||
}
|
||||
ton::CatchainSeqno get_shard_cc_seqno(ton::ShardIdFull shard) const;
|
||||
bool get_last_key_block(ton::BlockIdExt& blkid, ton::LogicalTime& blklt, bool strict = false) const;
|
||||
bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
|
||||
bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const;
|
||||
// returns block with min seqno and req_lt <= block.end_lt
|
||||
bool get_mc_block_by_lt(ton::LogicalTime lt, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
|
||||
bool get_prev_key_block(ton::BlockSeqno req_seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
|
||||
bool get_next_key_block(ton::BlockSeqno req_seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
|
||||
td::Result<std::vector<std::pair<ton::StdSmcAddress, int>>> get_special_ticktock_smartcontracts(
|
||||
int tick_tock = 3) const;
|
||||
int get_smc_tick_tock(td::ConstBitPtr smc_addr) const;
|
||||
std::unique_ptr<vm::AugmentedDictionary> create_accounts_dict() const;
|
||||
const vm::AugmentedDictionary& get_accounts_dict() const;
|
||||
std::vector<ton::ValidatorDescr> compute_validator_set_cc(ton::ShardIdFull shard, const block::ValidatorSet& vset,
|
||||
ton::UnixTime time,
|
||||
ton::CatchainSeqno* cc_seqno_delta = nullptr) const;
|
||||
std::vector<ton::ValidatorDescr> compute_validator_set_cc(ton::ShardIdFull shard, ton::UnixTime time,
|
||||
ton::CatchainSeqno* cc_seqno_delta = nullptr) const;
|
||||
static td::Result<std::unique_ptr<ConfigInfo>> extract_config(std::shared_ptr<vm::StaticBagOfCellsDb> static_boc,
|
||||
int mode = 0);
|
||||
static td::Result<std::unique_ptr<ConfigInfo>> extract_config(Ref<vm::Cell> mc_state_root, int mode = 0);
|
||||
|
||||
private:
|
||||
ConfigInfo(Ref<vm::Cell> mc_state_root, int _mode = 0);
|
||||
td::Status unpack_wrapped();
|
||||
td::Status unpack();
|
||||
void reset_mc_hash();
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
} // namespace block
|
221
crypto/block/output-queue-merger.cpp
Normal file
221
crypto/block/output-queue-merger.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "output-queue-merger.h"
|
||||
|
||||
namespace block {
|
||||
|
||||
/*
|
||||
*
|
||||
* OUTPUT QUEUE MERGER
|
||||
*
|
||||
*/
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::operator<(const MsgKeyValue& other) const {
|
||||
return lt < other.lt ||
|
||||
(lt == other.lt && td::bitstring::bits_memcmp(key.cbits() + 96, other.key.cbits() + 96, 256) < 0);
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::less(const std::unique_ptr<MsgKeyValue>& he1,
|
||||
const std::unique_ptr<MsgKeyValue>& he2) {
|
||||
return *he1 < *he2;
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::greater(const std::unique_ptr<MsgKeyValue>& he1,
|
||||
const std::unique_ptr<MsgKeyValue>& he2) {
|
||||
return *he2 < *he1;
|
||||
}
|
||||
|
||||
OutputQueueMerger::MsgKeyValue::MsgKeyValue(td::ConstBitPtr key_pfx, int key_pfx_len, int _src, Ref<vm::Cell> node)
|
||||
: source(_src) {
|
||||
unpack_node(key_pfx, key_pfx_len, std::move(node));
|
||||
}
|
||||
|
||||
OutputQueueMerger::MsgKeyValue::MsgKeyValue(int _src, Ref<vm::Cell> node) : source(_src) {
|
||||
unpack_node(td::ConstBitPtr{nullptr}, 0, std::move(node));
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::invalidate() {
|
||||
msg.clear();
|
||||
lt = 0;
|
||||
source = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
ton::LogicalTime OutputQueueMerger::MsgKeyValue::get_node_lt(Ref<vm::Cell> node, int key_pfx_len) {
|
||||
if (node.is_null() || (unsigned)key_pfx_len > (unsigned)max_key_len) {
|
||||
return std::numeric_limits<td::uint64>::max();
|
||||
}
|
||||
vm::dict::LabelParser label{std::move(node), max_key_len - key_pfx_len, vm::dict::LabelParser::chk_size};
|
||||
if (!label.is_valid()) {
|
||||
return std::numeric_limits<td::uint64>::max();
|
||||
}
|
||||
label.skip_label();
|
||||
return label.remainder->prefetch_ulong(64);
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::unpack_node(td::ConstBitPtr key_pfx, int key_pfx_len, Ref<vm::Cell> node) {
|
||||
if (node.is_null() || (unsigned)key_pfx_len >= (unsigned)max_key_len) {
|
||||
return invalidate();
|
||||
}
|
||||
if (!key_pfx.is_null()) {
|
||||
td::bitstring::bits_memcpy(key.bits(), key_pfx, key_pfx_len);
|
||||
}
|
||||
vm::dict::LabelParser label{std::move(node), max_key_len - key_pfx_len, vm::dict::LabelParser::chk_size};
|
||||
if (!label.is_valid()) {
|
||||
return invalidate();
|
||||
}
|
||||
label.extract_label_to(key.bits() + key_pfx_len);
|
||||
key_len = key_pfx_len + label.l_bits;
|
||||
msg = std::move(label.remainder);
|
||||
if (!msg.write().fetch_uint_to(64, lt)) {
|
||||
return invalidate();
|
||||
}
|
||||
if (is_fork() && msg->size_ext() != 0x20000) {
|
||||
return invalidate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::replace_with_child(bool child_idx) {
|
||||
if (!is_fork() || msg.is_null() || msg->size_ext() != 0x20000) {
|
||||
return false;
|
||||
}
|
||||
key[key_len] = child_idx;
|
||||
return unpack_node(td::ConstBitPtr{nullptr}, key_len + 1, msg->prefetch_ref(child_idx));
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::replace_by_prefix(td::ConstBitPtr req_pfx, int req_pfx_len) {
|
||||
do {
|
||||
if (td::bitstring::bits_memcmp(req_pfx, key.cbits(), std::min(req_pfx_len, key_len))) {
|
||||
return false;
|
||||
}
|
||||
if (key_len >= req_pfx_len) {
|
||||
return true;
|
||||
}
|
||||
} while (replace_with_child(req_pfx[key_len]));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::MsgKeyValue::split(MsgKeyValue& second) {
|
||||
if (!is_fork() || msg.is_null()) {
|
||||
return false;
|
||||
}
|
||||
unsigned long long keep_lt = lt;
|
||||
unsigned long long left_lt = get_node_lt(msg->prefetch_ref(0), key_len + 1);
|
||||
bool sw = (left_lt == lt);
|
||||
second.source = source;
|
||||
key[key_len] = sw;
|
||||
if (!second.unpack_node(key.cbits(), key_len + 1, msg->prefetch_ref(sw))) {
|
||||
return false;
|
||||
}
|
||||
key[key_len] = 1 - sw;
|
||||
if (!unpack_node(td::ConstBitPtr{nullptr}, key_len + 1, msg->prefetch_ref(1 - sw))) {
|
||||
return false;
|
||||
}
|
||||
if (lt != keep_lt || second.lt < keep_lt) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::add_root(int src, Ref<vm::Cell> outmsg_root) {
|
||||
if (outmsg_root.is_null()) {
|
||||
return true;
|
||||
}
|
||||
//block::gen::HashmapAug{352, block::gen::t_EnqueuedMsg, block::gen::t_uint64}.print_ref(std::cerr, outmsg_root);
|
||||
auto kv = std::make_unique<MsgKeyValue>(src, std::move(outmsg_root));
|
||||
if (kv->replace_by_prefix(common_pfx.cbits(), common_pfx_len)) {
|
||||
heap.push_back(std::move(kv));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OutputQueueMerger::OutputQueueMerger(ton::ShardIdFull _queue_for, std::vector<block::McShardDescr> _neighbors)
|
||||
: queue_for(_queue_for), neighbors(std::move(_neighbors)), eof(false), failed(false) {
|
||||
init();
|
||||
}
|
||||
|
||||
void OutputQueueMerger::init() {
|
||||
common_pfx.bits().store_int(queue_for.workchain, 32);
|
||||
int l = queue_for.pfx_len();
|
||||
td::bitstring::bits_store_long_top(common_pfx.bits() + 32, queue_for.shard, l);
|
||||
common_pfx_len = 32 + l;
|
||||
int i = 0;
|
||||
for (block::McShardDescr& neighbor : neighbors) {
|
||||
if (!neighbor.is_disabled()) {
|
||||
LOG(DEBUG) << "adding " << (neighbor.outmsg_root.is_null() ? "" : "non-") << "empty output queue for neighbor #"
|
||||
<< i << " (" << neighbor.blk_.to_str() << ")";
|
||||
add_root(i++, neighbor.outmsg_root);
|
||||
} else {
|
||||
LOG(DEBUG) << "skipping output queue for disabled neighbor #" << i;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
std::make_heap(heap.begin(), heap.end(), MsgKeyValue::greater);
|
||||
eof = heap.empty();
|
||||
if (!eof) {
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
OutputQueueMerger::MsgKeyValue* OutputQueueMerger::cur() {
|
||||
return eof ? nullptr : msg_list.at(pos).get();
|
||||
}
|
||||
|
||||
std::unique_ptr<OutputQueueMerger::MsgKeyValue> OutputQueueMerger::extract_cur() {
|
||||
return eof ? std::unique_ptr<MsgKeyValue>{} : std::move(msg_list.at(pos));
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::next() {
|
||||
if (eof) {
|
||||
return false;
|
||||
} else if (++pos < msg_list.size() || load()) {
|
||||
return true;
|
||||
} else {
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OutputQueueMerger::load() {
|
||||
if (heap.empty() || failed) {
|
||||
return false;
|
||||
}
|
||||
unsigned long long lt = heap[0]->lt;
|
||||
std::size_t orig_size = msg_list.size();
|
||||
do {
|
||||
while (heap[0]->is_fork()) {
|
||||
auto other = std::make_unique<MsgKeyValue>();
|
||||
if (!heap[0]->split(*other)) {
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
heap.push_back(std::move(other));
|
||||
std::push_heap(heap.begin(), heap.end(), MsgKeyValue::greater);
|
||||
}
|
||||
assert(heap[0]->lt == lt);
|
||||
std::pop_heap(heap.begin(), heap.end(), MsgKeyValue::greater);
|
||||
msg_list.push_back(std::move(heap.back()));
|
||||
heap.pop_back();
|
||||
} while (!heap.empty() && heap[0]->lt <= lt);
|
||||
std::sort(msg_list.begin() + orig_size, msg_list.end(), MsgKeyValue::less);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace block
|
80
crypto/block/output-queue-merger.h
Normal file
80
crypto/block/output-queue-merger.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "ton/ton-types.h"
|
||||
#include "vm/cells/CellSlice.h"
|
||||
#include "block/mc-config.h"
|
||||
|
||||
namespace block {
|
||||
using td::Ref;
|
||||
|
||||
struct OutputQueueMerger {
|
||||
struct MsgKeyValue {
|
||||
static constexpr int max_key_len = 32 + 64 + 256;
|
||||
Ref<vm::CellSlice> msg;
|
||||
unsigned long long lt;
|
||||
int source;
|
||||
int key_len{0};
|
||||
td::BitArray<max_key_len> key;
|
||||
MsgKeyValue() = default;
|
||||
MsgKeyValue(int src, Ref<vm::Cell> node);
|
||||
MsgKeyValue(td::ConstBitPtr key_pfx, int key_pfx_len, int src, Ref<vm::Cell> node);
|
||||
bool operator<(const MsgKeyValue& other) const;
|
||||
bool is_fork() const {
|
||||
return key_len < max_key_len;
|
||||
}
|
||||
bool invalidate();
|
||||
static bool less(const std::unique_ptr<MsgKeyValue>& he1, const std::unique_ptr<MsgKeyValue>& he2);
|
||||
static bool greater(const std::unique_ptr<MsgKeyValue>& he1, const std::unique_ptr<MsgKeyValue>& he2);
|
||||
|
||||
protected:
|
||||
friend struct OutputQueueMerger;
|
||||
static ton::LogicalTime get_node_lt(Ref<vm::Cell> node, int key_pfx_len);
|
||||
bool replace_with_child(bool child_idx);
|
||||
bool replace_by_prefix(td::ConstBitPtr req_pfx, int req_pfx_len);
|
||||
bool unpack_node(td::ConstBitPtr key_pfx, int key_pfx_len, Ref<vm::Cell> node);
|
||||
bool split(MsgKeyValue& second);
|
||||
};
|
||||
//
|
||||
ton::ShardIdFull queue_for;
|
||||
std::vector<std::unique_ptr<MsgKeyValue>> msg_list;
|
||||
std::vector<block::McShardDescr> neighbors;
|
||||
|
||||
public:
|
||||
OutputQueueMerger(ton::ShardIdFull _queue_for, std::vector<block::McShardDescr> _neighbors);
|
||||
bool is_eof() const {
|
||||
return eof;
|
||||
}
|
||||
MsgKeyValue* cur();
|
||||
std::unique_ptr<MsgKeyValue> extract_cur();
|
||||
bool next();
|
||||
|
||||
private:
|
||||
td::BitArray<32 + 64> common_pfx;
|
||||
int common_pfx_len;
|
||||
std::vector<std::unique_ptr<MsgKeyValue>> heap;
|
||||
std::size_t pos{0};
|
||||
bool eof;
|
||||
bool failed;
|
||||
void init();
|
||||
bool add_root(int src, Ref<vm::Cell> outmsg_root);
|
||||
bool load();
|
||||
};
|
||||
|
||||
} // namespace block
|
248
crypto/block/test-block.cpp
Normal file
248
crypto/block/test-block.cpp
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
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
|
||||
*/
|
||||
#include "block/block.h"
|
||||
#include "vm/boc.h"
|
||||
#include <iostream>
|
||||
#include "block-db.h"
|
||||
#include "block-auto.h"
|
||||
#include "block-parse.h"
|
||||
#include "vm/cp0.h"
|
||||
#include <getopt.h>
|
||||
|
||||
using td::Ref;
|
||||
|
||||
int verbosity;
|
||||
|
||||
struct IntError {
|
||||
std::string err_msg;
|
||||
IntError(std::string _msg) : err_msg(_msg) {
|
||||
}
|
||||
IntError(const char* _msg) : err_msg(_msg) {
|
||||
}
|
||||
};
|
||||
|
||||
td::Ref<vm::Cell> load_boc(std::string filename) {
|
||||
std::cerr << "loading bag-of-cell file " << filename << std::endl;
|
||||
auto bytes_res = block::load_binary_file(filename);
|
||||
if (bytes_res.is_error()) {
|
||||
throw IntError{PSTRING() << "cannot load file `" << filename << "` : " << bytes_res.move_as_error()};
|
||||
}
|
||||
vm::BagOfCells boc;
|
||||
auto res = boc.deserialize(bytes_res.move_as_ok());
|
||||
if (res.is_error()) {
|
||||
throw IntError{PSTRING() << "cannot deserialize bag-of-cells " << res.move_as_error()};
|
||||
}
|
||||
if (res.move_as_ok() <= 0 || boc.get_root_cell().is_null()) {
|
||||
throw IntError{"cannot deserialize bag-of-cells "};
|
||||
}
|
||||
return boc.get_root_cell();
|
||||
}
|
||||
|
||||
void test1() {
|
||||
block::ShardId id{ton::masterchainId}, id2{ton::basechainId, 0x11efULL << 48};
|
||||
std::cout << '[' << id << "][" << id2 << ']' << std::endl;
|
||||
vm::CellBuilder cb;
|
||||
cb << id << id2;
|
||||
std::cout << "ShardIdent.pack() = " << block::tlb::t_ShardIdent.pack(cb, {12, 3, 0x3aeULL << 52}) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
auto cref = cb.finalize();
|
||||
td::Ref<vm::CellSlice> cs{true, cref}, cs2;
|
||||
block::ShardId id3{cs.write()}, id4, id5;
|
||||
cs >> id4 >> id5;
|
||||
std::cout << '[' << id3 << "][" << id4 << "][" << id5 << ']' << std::endl;
|
||||
vm::CellSlice csl{std::move(cref)};
|
||||
std::cout << "ShardIdent.get_size() = " << block::tlb::t_ShardIdent.get_size(csl) << std::endl;
|
||||
std::cout << "MsgAddress.get_size() = " << block::tlb::t_MsgAddress.get_size(csl) << std::endl;
|
||||
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl) << std::endl;
|
||||
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl) << std::endl;
|
||||
(csl + 8).print_rec(std::cout);
|
||||
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl + 8) << std::endl;
|
||||
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl + 8) << std::endl;
|
||||
|
||||
vm::CellSlice csl2{csl};
|
||||
block::gen::ShardIdent::Record sh_id;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
std::cout << csl2 << std::endl;
|
||||
bool ok = tlb::unpack(csl2, sh_id);
|
||||
std::cout << "block::gen::ShardIdent.unpack() = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " (shard_ident shard_pfx_bits:" << sh_id.shard_pfx_bits << " workchain_id:" << sh_id.workchain_id
|
||||
<< " shard_prefix:" << std::hex << sh_id.shard_prefix << std::dec << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
block::tlb::ShardIdent::Record shard_id;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
std::cout << "ShardIdent.validate() = " << block::tlb::t_ShardIdent.validate(csl) << std::endl;
|
||||
csl.print_rec(std::cerr);
|
||||
csl.dump(std::cerr, 7);
|
||||
std::cout << "ShardIdent.unpack() = " << block::tlb::t_ShardIdent.unpack(csl, shard_id) << std::endl;
|
||||
if (shard_id.is_valid()) {
|
||||
std::cout << "shard_pfx_bits:" << shard_id.shard_pfx_bits << " workchain_id:" << shard_id.workchain_id
|
||||
<< " shard_prefix:" << shard_id.shard_prefix << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
|
||||
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
|
||||
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
|
||||
using namespace td::literals;
|
||||
std::cout << "Grams.store_intval(239) = " << block::tlb::t_Grams.store_integer_value(cb, "239"_i256) << std::endl;
|
||||
std::cout << "Grams.store_intval(17239) = " << block::tlb::t_Grams.store_integer_value(cb, "17239"_i256) << std::endl;
|
||||
std::cout << "Grams.store_intval(-17) = " << block::tlb::t_Grams.store_integer_value(cb, "-17"_i256) << std::endl;
|
||||
std::cout << "Grams.store_intval(0) = " << block::tlb::t_Grams.store_integer_value(cb, "0"_i256) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
cs = td::Ref<vm::CellSlice>{true, cb.finalize()};
|
||||
std::cout << "Grams.store_intval(666) = " << block::tlb::t_Grams.store_integer_value(cb, "666"_i256) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
cs2 = td::Ref<vm::CellSlice>{true, cb.finalize()};
|
||||
std::cout << "Grams.validate(cs) = " << block::tlb::t_Grams.validate(*cs) << std::endl;
|
||||
std::cout << "Grams.validate(cs2) = " << block::tlb::t_Grams.validate(*cs2) << std::endl;
|
||||
//
|
||||
block::gen::SplitMergeInfo::Record data;
|
||||
block::gen::Grams::Record data2;
|
||||
std::cout << "block::gen::Grams.validate(cs) = " << block::gen::t_Grams.validate(*cs) << std::endl;
|
||||
std::cout << "block::gen::Grams.validate(cs2) = " << block::gen::t_Grams.validate(*cs2) << std::endl;
|
||||
std::cout << "[cs = " << cs << "]" << std::endl;
|
||||
bool ok = tlb::csr_unpack_inexact(cs, data);
|
||||
std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " cur_shard_pfx_len = " << data.cur_shard_pfx_len << "; acc_split_depth = " << data.acc_split_depth
|
||||
<< "; this_addr = " << data.this_addr << "; sibling_addr = " << data.sibling_addr << std::endl;
|
||||
}
|
||||
ok = tlb::csr_unpack_inexact(cs, data2);
|
||||
std::cout << "block::gen::Grams.unpack(cs, data2) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " amount = " << data2.amount << std::endl;
|
||||
block::gen::VarUInteger::Record data3;
|
||||
ok = tlb::csr_type_unpack(data2.amount, block::gen::t_VarUInteger_16, data3);
|
||||
std::cout << " block::gen::VarUInteger16.unpack(amount, data3) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " len = " << data3.len << "; value = " << data3.value << std::endl;
|
||||
vm::CellBuilder cb;
|
||||
std::cout << " block::gen::VarUInteger16.pack(cb, data3) = "
|
||||
<< tlb::type_pack(cb, block::gen::t_VarUInteger_16, data3) << std::endl;
|
||||
std::cout << " cb = " << cb.finalize() << std::endl;
|
||||
}
|
||||
}
|
||||
/*
|
||||
{
|
||||
vm::CellBuilder cb;
|
||||
td::BitArray<256> hash;
|
||||
std::memset(hash.data(), 0x69, 32);
|
||||
bool ok = tlb::pack(
|
||||
cb, block::gen::Test::Record{1000000000000, {170239, -888, {239017, "1000000000000000000"_ri256}, hash}, 17});
|
||||
std::cout << " block::gen::Test::pack(cb, {1000000000000, ...}) = " << ok << std::endl;
|
||||
std::cout << " cb = " << cb << std::endl;
|
||||
auto cell = cb.finalize();
|
||||
vm::CellSlice cs{cell};
|
||||
cs.print_rec(std::cout);
|
||||
block::gen::Test::Record data;
|
||||
std::cout << " block::gen::Test::validate_ref(cell) = " << block::gen::t_Test.validate_ref(cell) << std::endl;
|
||||
ok = tlb::unpack(cs, data);
|
||||
std::cout << " block::gen::Test::unpack(cs, data) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << "a:" << data.a << " b:" << data.r1.b << " c:" << data.r1.c << " d:" << data.r1.r1.d
|
||||
<< " e:" << data.r1.r1.e << " f:" << data.r1.f << " g:" << data.g << std::endl;
|
||||
}
|
||||
std::cout << " block::gen::Test::print_ref(cell) = ";
|
||||
block::gen::t_Test.print_ref(std::cout, cell, 2);
|
||||
block::gen::t_CurrencyCollection.print_ref(std::cout, cell, 2);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
*/
|
||||
std::cout << "Grams.add_values() = " << block::tlb::t_Grams.add_values(cb, cs.write(), cs2.write()) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
std::cout << "block::gen::t_HashmapAug_64_...print_type() = "
|
||||
<< block::gen::t_HashmapAug_64_Ref_Transaction_CurrencyCollection << std::endl;
|
||||
}
|
||||
|
||||
void test2(vm::CellSlice& cs) {
|
||||
std::cout << "Bool.validate() = " << block::tlb::t_Bool.validate(cs) << std::endl;
|
||||
std::cout << "UInt16.validate() = " << block::tlb::t_uint16.validate(cs) << std::endl;
|
||||
std::cout << "HashmapE(32,UInt16).validate() = " << block::tlb::HashmapE(32, block::tlb::t_uint16).validate(cs)
|
||||
<< std::endl;
|
||||
std::cout << "block::gen::HashmapE(32,UInt16).validate() = "
|
||||
<< block::gen::HashmapE{32, block::gen::t_uint16}.validate(cs) << std::endl;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
std::cout << "usage: test-block [<boc-file>]\n\tor test-block -h\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
int new_verbosity_level = VERBOSITY_NAME(INFO);
|
||||
auto zerostate = std::make_unique<block::ZerostateInfo>();
|
||||
while ((i = getopt(argc, argv, "hv:")) != -1) {
|
||||
switch (i) {
|
||||
case 'v':
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
std::exit(2);
|
||||
default:
|
||||
usage();
|
||||
std::exit(2);
|
||||
}
|
||||
}
|
||||
SET_VERBOSITY_LEVEL(new_verbosity_level);
|
||||
try {
|
||||
bool done = false;
|
||||
while (optind < argc) {
|
||||
auto boc = load_boc(argv[optind++]);
|
||||
if (boc.is_null()) {
|
||||
std::cerr << "(invalid boc)" << std::endl;
|
||||
std::exit(2);
|
||||
} else {
|
||||
done = true;
|
||||
vm::CellSlice cs{vm::NoVm(), boc};
|
||||
cs.print_rec(std::cout);
|
||||
std::cout << std::endl;
|
||||
block::gen::t_Block.print_ref(std::cout, boc);
|
||||
std::cout << std::endl;
|
||||
if (!block::gen::t_Block.validate_ref(boc)) {
|
||||
std::cout << "(invalid Block)" << std::endl;
|
||||
} else {
|
||||
std::cout << "(valid Block)" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
test1();
|
||||
}
|
||||
} catch (IntError& err) {
|
||||
std::cerr << "caught internal error " << err.err_msg << std::endl;
|
||||
return 1;
|
||||
} catch (vm::VmError& err) {
|
||||
std::cerr << "caught vm error " << err.get_msg() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
2089
crypto/block/transaction.cpp
Normal file
2089
crypto/block/transaction.cpp
Normal file
File diff suppressed because it is too large
Load diff
401
crypto/block/transaction.h
Normal file
401
crypto/block/transaction.h
Normal file
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include "common/refcnt.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "vm/cells.h"
|
||||
#include "vm/cellslice.h"
|
||||
#include "vm/dict.h"
|
||||
#include "vm/boc.h"
|
||||
#include <ostream>
|
||||
#include "tl/tlblib.hpp"
|
||||
#include "td/utils/bits.h"
|
||||
#include "ton/ton-types.h"
|
||||
#include "block/block.h"
|
||||
#include "block/mc-config.h"
|
||||
|
||||
namespace block {
|
||||
using td::Ref;
|
||||
using LtCellRef = std::pair<ton::LogicalTime, Ref<vm::Cell>>;
|
||||
|
||||
struct Account;
|
||||
struct Transaction;
|
||||
|
||||
struct CollatorError {
|
||||
std::string msg;
|
||||
CollatorError(std::string _msg) : msg(_msg) {
|
||||
}
|
||||
CollatorError(const char* _msg) : msg(_msg) {
|
||||
}
|
||||
std::string get_msg() const {
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
static inline bool operator<(const LtCellRef& a, const LtCellRef& b) {
|
||||
return a.first < b.first;
|
||||
}
|
||||
|
||||
struct LtCellCompare {
|
||||
bool operator()(const LtCellRef& a, const LtCellRef& b) {
|
||||
return a.first < b.first;
|
||||
}
|
||||
};
|
||||
|
||||
struct NewOutMsg {
|
||||
ton::LogicalTime lt;
|
||||
Ref<vm::Cell> msg;
|
||||
Ref<vm::Cell> trans;
|
||||
NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans)
|
||||
: lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) {
|
||||
}
|
||||
bool operator<(const NewOutMsg& other) const & {
|
||||
return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash());
|
||||
}
|
||||
bool operator>(const NewOutMsg& other) const & {
|
||||
return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash());
|
||||
}
|
||||
};
|
||||
|
||||
struct StoragePhaseConfig {
|
||||
const std::vector<block::StoragePrices>* pricing{nullptr};
|
||||
td::RefInt256 freeze_due_limit;
|
||||
td::RefInt256 delete_due_limit;
|
||||
StoragePhaseConfig() = default;
|
||||
StoragePhaseConfig(const std::vector<block::StoragePrices>* _pricing, td::RefInt256 freeze_limit = {},
|
||||
td::RefInt256 delete_limit = {})
|
||||
: pricing(_pricing), freeze_due_limit(freeze_limit), delete_due_limit(delete_limit) {
|
||||
}
|
||||
};
|
||||
|
||||
struct StoragePhase {
|
||||
td::RefInt256 fees_collected;
|
||||
td::RefInt256 fees_due;
|
||||
ton::UnixTime last_paid_updated;
|
||||
bool frozen{false};
|
||||
bool deleted{false};
|
||||
bool is_special{false};
|
||||
};
|
||||
|
||||
struct ComputePhaseConfig {
|
||||
td::uint64 gas_price;
|
||||
td::uint64 gas_limit;
|
||||
td::uint64 gas_credit;
|
||||
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
|
||||
td::RefInt256 gas_price256;
|
||||
td::RefInt256 max_gas_threshold;
|
||||
std::unique_ptr<vm::Dictionary> libraries;
|
||||
Ref<vm::Cell> global_config;
|
||||
td::BitArray<256> block_rand_seed;
|
||||
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
|
||||
: gas_price(_gas_price), gas_limit(_gas_limit), gas_credit(_gas_credit) {
|
||||
compute_threshold();
|
||||
}
|
||||
void compute_threshold();
|
||||
td::uint64 gas_bought_for(td::RefInt256 nanograms) const;
|
||||
td::RefInt256 compute_gas_price(td::uint64 gas_used) const;
|
||||
void set_gas_price(td::uint64 _gas_price) {
|
||||
gas_price = _gas_price;
|
||||
compute_threshold();
|
||||
}
|
||||
Ref<vm::Cell> lookup_library(td::ConstBitPtr key) const;
|
||||
Ref<vm::Cell> lookup_library(const td::Bits256& key) const {
|
||||
return lookup_library(key.bits());
|
||||
}
|
||||
Ref<vm::Cell> get_lib_root() const {
|
||||
return libraries ? libraries->get_root_cell() : Ref<vm::Cell>{};
|
||||
}
|
||||
};
|
||||
|
||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
||||
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
|
||||
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
|
||||
|
||||
struct MsgPrices {
|
||||
td::uint64 lump_price;
|
||||
td::uint64 bit_price;
|
||||
td::uint64 cell_price;
|
||||
td::uint32 ihr_factor;
|
||||
td::uint32 first_frac;
|
||||
td::uint32 next_frac;
|
||||
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const;
|
||||
std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
|
||||
bool ihr_disabled = false) const;
|
||||
MsgPrices() = default;
|
||||
MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf)
|
||||
: lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) {
|
||||
}
|
||||
td::RefInt256 get_first_part(td::RefInt256 total) const;
|
||||
td::uint64 get_first_part(td::uint64 total) const;
|
||||
td::RefInt256 get_next_part(td::RefInt256 total) const;
|
||||
};
|
||||
|
||||
struct ActionPhaseConfig {
|
||||
int max_actions{255};
|
||||
MsgPrices fwd_std;
|
||||
MsgPrices fwd_mc; // from/to masterchain
|
||||
const WorkchainSet* workchains{nullptr};
|
||||
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
|
||||
return is_masterchain ? fwd_mc : fwd_std;
|
||||
}
|
||||
};
|
||||
|
||||
struct CreditPhase {
|
||||
td::RefInt256 due_fees_collected;
|
||||
block::CurrencyCollection credit;
|
||||
// td::RefInt256 credit;
|
||||
// Ref<vm::Cell> credit_extra;
|
||||
};
|
||||
|
||||
struct ComputePhase {
|
||||
enum { sk_none, sk_no_state, sk_bad_state, sk_no_gas };
|
||||
int skip_reason{sk_none};
|
||||
bool success{false};
|
||||
bool msg_state_used{false};
|
||||
bool account_activated{false};
|
||||
bool out_of_gas{false};
|
||||
bool accepted{false};
|
||||
td::RefInt256 gas_fees;
|
||||
td::uint64 gas_used, gas_max, gas_limit, gas_credit;
|
||||
int mode;
|
||||
int exit_code;
|
||||
int exit_arg;
|
||||
int vm_steps;
|
||||
ton::Bits256 vm_init_state_hash, vm_final_state_hash;
|
||||
Ref<vm::Cell> in_msg;
|
||||
Ref<vm::Cell> new_data;
|
||||
Ref<vm::Cell> actions;
|
||||
};
|
||||
|
||||
struct ActionPhase {
|
||||
bool success{false};
|
||||
bool valid{false};
|
||||
bool no_funds{false};
|
||||
bool code_changed{false};
|
||||
bool action_list_invalid{false};
|
||||
bool acc_delete_req{false};
|
||||
enum { acst_unchanged = 0, acst_frozen = 2, acst_deleted = 3 };
|
||||
int acc_status_change{acst_unchanged};
|
||||
td::RefInt256 total_fwd_fees; // all fees debited from the account
|
||||
td::RefInt256 total_action_fees; // fees credited to validators in this action phase
|
||||
int result_code;
|
||||
int result_arg;
|
||||
int tot_actions;
|
||||
int spec_actions;
|
||||
int skipped_actions;
|
||||
int msgs_created;
|
||||
Ref<vm::Cell> new_code;
|
||||
td::BitArray<256> action_list_hash;
|
||||
block::CurrencyCollection remaining_balance, reserved_balance;
|
||||
// td::RefInt256 remaining_balance;
|
||||
// Ref<vm::Cell> remaining_extra;
|
||||
// td::RefInt256 reserved_balance;
|
||||
// Ref<vm::Cell> reserved_extra;
|
||||
std::vector<Ref<vm::Cell>> action_list; // processed in reverse order
|
||||
std::vector<Ref<vm::Cell>> out_msgs;
|
||||
ton::LogicalTime end_lt;
|
||||
unsigned long long tot_msg_bits{0}, tot_msg_cells{0};
|
||||
};
|
||||
|
||||
struct BouncePhase {
|
||||
bool ok{false};
|
||||
bool nofunds{false};
|
||||
unsigned long long msg_bits, msg_cells;
|
||||
unsigned long long fwd_fees, fwd_fees_collected;
|
||||
Ref<vm::Cell> out_msg;
|
||||
};
|
||||
|
||||
struct Account {
|
||||
enum { acc_nonexist = 0, acc_uninit = 1, acc_frozen = 2, acc_active = 3, acc_deleted = 4 };
|
||||
int status{acc_nonexist}, orig_status{acc_nonexist};
|
||||
bool is_special{false};
|
||||
bool tick{false};
|
||||
bool tock{false};
|
||||
bool created{false};
|
||||
bool split_depth_set_{false};
|
||||
unsigned char split_depth_{0};
|
||||
int verbosity{3 * 0};
|
||||
ton::UnixTime now_{0};
|
||||
ton::WorkchainId workchain{ton::workchainInvalid};
|
||||
td::BitArray<32> addr_rewrite; // rewrite (anycast) data, split_depth bits
|
||||
ton::StdSmcAddress addr; // rewritten address (by replacing a prefix of `addr_orig` with `addr_rewrite`)
|
||||
ton::StdSmcAddress addr_orig; // address indicated in smart-contract data
|
||||
Ref<vm::CellSlice> my_addr; // address as stored in the smart contract (MsgAddressInt)
|
||||
Ref<vm::CellSlice> my_addr_exact; // exact address without anycast info
|
||||
ton::LogicalTime last_trans_end_lt_;
|
||||
ton::LogicalTime last_trans_lt_;
|
||||
ton::Bits256 last_trans_hash_;
|
||||
ton::LogicalTime block_lt;
|
||||
ton::UnixTime last_paid;
|
||||
vm::CellStorageStat storage_stat;
|
||||
block::CurrencyCollection balance;
|
||||
// td::RefInt256 balance;
|
||||
// Ref<vm::Cell> extra_balance;
|
||||
td::RefInt256 due_payment;
|
||||
Ref<vm::Cell> orig_total_state; // ^Account
|
||||
Ref<vm::Cell> total_state; // ^Account
|
||||
Ref<vm::CellSlice> inner_state; // StateInit
|
||||
ton::Bits256 state_hash; // hash of StateInit for frozen accounts
|
||||
Ref<vm::Cell> code, data, library, orig_library;
|
||||
std::vector<LtCellRef> transactions;
|
||||
Account() = default;
|
||||
Account(ton::WorkchainId wc, td::ConstBitPtr _addr) : workchain(wc), addr(_addr) {
|
||||
}
|
||||
Account(ton::WorkchainId wc, td::ConstBitPtr _addr, int depth)
|
||||
: split_depth_set_(true), split_depth_((unsigned char)depth), workchain(wc), addr(_addr) {
|
||||
}
|
||||
block::CurrencyCollection get_balance() const {
|
||||
return balance;
|
||||
}
|
||||
bool set_address(ton::WorkchainId wc, td::ConstBitPtr new_addr);
|
||||
bool unpack(Ref<vm::CellSlice> account, Ref<vm::CellSlice> extra, ton::UnixTime now, bool special = false);
|
||||
bool init_new(ton::UnixTime now);
|
||||
bool recompute_tmp_addr(Ref<vm::CellSlice>& tmp_addr, int split_depth, td::ConstBitPtr orig_addr_rewrite) const;
|
||||
td::RefInt256 compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing) const;
|
||||
bool is_masterchain() const {
|
||||
return workchain == ton::masterchainId;
|
||||
}
|
||||
bool belongs_to_shard(ton::ShardIdFull shard) const;
|
||||
bool store_acc_status(vm::CellBuilder& cb, int status) const;
|
||||
bool store_acc_status(vm::CellBuilder& cb) const {
|
||||
return store_acc_status(cb, status);
|
||||
}
|
||||
void push_transaction(Ref<vm::Cell> trans_root, ton::LogicalTime trans_lt);
|
||||
bool libraries_changed() const;
|
||||
bool create_account_block(vm::CellBuilder& cb); // stores an AccountBlock with all transactions
|
||||
|
||||
protected:
|
||||
friend struct Transaction;
|
||||
bool set_split_depth(int split_depth);
|
||||
bool check_split_depth(int split_depth) const;
|
||||
bool init_rewrite_addr(int split_depth, td::ConstBitPtr orig_addr_rewrite);
|
||||
|
||||
private:
|
||||
bool unpack_address(vm::CellSlice& addr_cs);
|
||||
bool unpack_storage_info(vm::CellSlice& cs);
|
||||
bool unpack_state(vm::CellSlice& cs);
|
||||
bool parse_maybe_anycast(vm::CellSlice& cs);
|
||||
bool store_maybe_anycast(vm::CellBuilder& cb) const;
|
||||
bool compute_my_addr(bool force = false);
|
||||
};
|
||||
|
||||
struct Transaction {
|
||||
static constexpr unsigned max_msg_bits = (1 << 21), max_msg_cells = (1 << 13);
|
||||
enum {
|
||||
tr_none,
|
||||
tr_ord,
|
||||
tr_storage,
|
||||
tr_tick,
|
||||
tr_tock,
|
||||
tr_split_prepare,
|
||||
tr_split_install,
|
||||
tr_merge_prepare,
|
||||
tr_merge_install
|
||||
};
|
||||
int trans_type{tr_none};
|
||||
bool was_deleted{false};
|
||||
bool was_frozen{false};
|
||||
bool was_activated{false};
|
||||
bool was_created{false};
|
||||
bool bounce_enabled{false};
|
||||
bool in_msg_extern{false};
|
||||
bool use_msg_state{false};
|
||||
bool is_first{false};
|
||||
bool orig_addr_rewrite_set{false};
|
||||
bool new_tick;
|
||||
bool new_tock;
|
||||
signed char new_split_depth{-1};
|
||||
ton::UnixTime now;
|
||||
int acc_status;
|
||||
int verbosity{3 * 0};
|
||||
int in_msg_type{0};
|
||||
const Account& account; // only `commit` method modifies the account
|
||||
Ref<vm::CellSlice> my_addr, my_addr_exact; // almost the same as in account.*
|
||||
ton::LogicalTime start_lt, end_lt;
|
||||
block::CurrencyCollection balance;
|
||||
block::CurrencyCollection msg_balance_remaining;
|
||||
td::RefInt256 due_payment;
|
||||
td::RefInt256 in_fwd_fee, msg_fwd_fees;
|
||||
block::CurrencyCollection total_fees{0};
|
||||
ton::UnixTime last_paid;
|
||||
Ref<vm::Cell> root;
|
||||
Ref<vm::Cell> new_total_state;
|
||||
Ref<vm::CellSlice> new_inner_state;
|
||||
// Ref<vm::Cell> extra_balance;
|
||||
// Ref<vm::Cell> msg_extra;
|
||||
Ref<vm::Cell> new_code, new_data, new_library;
|
||||
Ref<vm::Cell> in_msg, in_msg_state;
|
||||
Ref<vm::CellSlice> in_msg_body;
|
||||
Ref<vm::Cell> in_msg_library;
|
||||
td::BitArray<256> frozen_hash;
|
||||
td::BitArray<32> orig_addr_rewrite;
|
||||
std::vector<Ref<vm::Cell>> out_msgs;
|
||||
std::unique_ptr<StoragePhase> storage_phase;
|
||||
std::unique_ptr<CreditPhase> credit_phase;
|
||||
std::unique_ptr<ComputePhase> compute_phase;
|
||||
std::unique_ptr<ActionPhase> action_phase;
|
||||
std::unique_ptr<BouncePhase> bounce_phase;
|
||||
vm::CellStorageStat new_storage_stat;
|
||||
Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
|
||||
Ref<vm::Cell> _inmsg = {});
|
||||
bool unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg);
|
||||
bool check_in_msg_state_hash();
|
||||
bool prepare_storage_phase(const StoragePhaseConfig& cfg, bool force_collect = true);
|
||||
bool prepare_credit_phase();
|
||||
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
|
||||
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
|
||||
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
|
||||
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
|
||||
bool prepare_action_phase(const ActionPhaseConfig& cfg);
|
||||
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
|
||||
bool compute_state();
|
||||
bool serialize();
|
||||
td::uint64 gas_used() const {
|
||||
return compute_phase ? compute_phase->gas_used : 0;
|
||||
}
|
||||
|
||||
td::Result<vm::NewCellStorageStat::Stat> estimate_block_storage_profile_incr(
|
||||
const vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
|
||||
bool update_block_storage_profile(vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
|
||||
bool would_fit(unsigned cls, const block::BlockLimitStatus& blk_lim_st) const;
|
||||
bool update_limits(block::BlockLimitStatus& blk_lim_st) const;
|
||||
|
||||
Ref<vm::Cell> commit(Account& _account); // _account should point to the same account
|
||||
LtCellRef extract_out_msg(unsigned i);
|
||||
NewOutMsg extract_out_msg_ext(unsigned i);
|
||||
void extract_out_msgs(std::vector<LtCellRef>& list);
|
||||
|
||||
private:
|
||||
Ref<vm::Tuple> prepare_vm_c7(const ComputePhaseConfig& cfg) const;
|
||||
bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const;
|
||||
int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
||||
int try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
||||
int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
||||
bool check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const;
|
||||
bool check_rewrite_dest_addr(Ref<vm::CellSlice>& dest_addr, const ActionPhaseConfig& cfg,
|
||||
bool* is_mc = nullptr) const;
|
||||
bool serialize_storage_phase(vm::CellBuilder& cb);
|
||||
bool serialize_credit_phase(vm::CellBuilder& cb);
|
||||
bool serialize_compute_phase(vm::CellBuilder& cb);
|
||||
bool serialize_action_phase(vm::CellBuilder& cb);
|
||||
bool serialize_bounce_phase(vm::CellBuilder& cb);
|
||||
bool unpack_msg_state(bool lib_only = false);
|
||||
};
|
||||
|
||||
} // namespace block
|
Loading…
Add table
Add a link
Reference in a new issue